Chapter 14 Structure and Other Data Forms 14.7 Transfer Structural Information to Functions

Posted by Slip on Tue, 16 Jul 2019 19:23:21 +0200

Current C implementations allow structure to be passed as parameters, or pointers to the structure to be passed as parameters. If only part of the structure is concerned, structure members can also be passed as parameters. First, let's look at passing structural members as parameters.

14.7.1 Members of the Transfer Structure

As long as the structure member is a data type with a single value (i.e. int and its associated type, char, float, double or pointer), it can be passed as a parameter to a function that accepts this particular type. This is illustrated by the financial analysis prototype shown in List 14.5. This procedure adds the customer's bank account to his savings and loan account.

Program List 14.5 Funs1.c Program

/*funds1.c  --Transfer structural members as parameters*/
#include <stdio.h>
#define FUNDLEN 50
struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};
double sum(double,double);

int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        3024.72,
        "Lucky's savings and Loan",
        9237.11
    };
    printf("Stan has a total of $%.2f.\n",
            sum(stan.bankfund,stan.savefund));
    return 0;
}
double sum(double x,double y)
{
    return(x+y);
}

Note that the function sum () does not know or care whether the actual parameter is a member of the structure, it only requires that the parameter be of double type. Of course, if you want the called function to affect the value of the member in the calling function, you can pass the member address:

modify(&stan.bankfund);

This is a function of changing stan's bank account.

The next way to pass structural information to a function is to make the function known that it is working on a structure.

14.7.2 Use Structural Address

We still solve the previous problem, but this time we take the address of the structure as a parameter. Because the function deals with the funds structure, it also uses the funds declaration. See List 14.6:

Program List 14.6 Funs2.c Program

/*funds2.c  --Pass the pointer to the structure*/
#include <stdio.h>
#define FUNDLEN 50
struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};
double sum(const struct funds *);/*A parameter is a pointer*/

int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        3024.72,
        "Lucky's savings and Loan",
        9237.11
    };
    printf("Stan has a total of $%.2f.\n",
            sum(&stan));
    return 0;
}
double sum(const struct funds *money)
{
    return(money->bankfund+money->savefund);
}

The sum() function uses a money pointer to the funds structure as its only parameter. Pass the address & stan to the function so that the pointer money points to the structure stan. Then, use the - > operator to get the values of stan.bankfund and stan.savefund. Because the function does not change the content of the pointed value, it declares money as a pointer to const.

Note that you must use the & operator to get the address of the structure. Unlike array names, a single structural name is not synonymous with the address of the structure.

14.7.3 Transfer of Structures as Parameters

For compilers that allow structure to be passed as a parameter, the previous example can be rewritten to the program shown in Listing 14.7.

/*funds2.c  --Transfer structure as parameter*/
#include <stdio.h>
#define FUNDLEN 50
struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};
double sum(struct funds moolah);/*A parameter is a structure*/

int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        3024.72,
        "Lucky's savings and Loan",
        9237.11
    };
    printf("Stan has a total of $%.2f.\n",
            sum(stan));
    return 0;
}
double sum(struct funds moolah)
{
    return(moolah.bankfund+moolah.savefund);
}

When sum() is called, an automatic variable moolah is created based on the funds template. The members of this structure are then initialized as copies of the values of the corresponding members of the stan structure. Therefore, a copy of the original structure will be used to complete the calculation, while the previous program (the one using the pointer) uses the original structure itself. Because moolah is a structure, the program uses moolah. bank fund instead of moolah - > Bank fund.

14.7.4 Other structural characteristics

Now C allows one structure to be assigned to another, but it cannot do this to arrays.

That is to say, n_data and o_data are the same kind of structures, which can be done as follows:

o_data=n_data; // assign one structure to another

This allows each member of o_data to be assigned the value of the corresponding member of n_data, even if one member is an array.

It is also possible to initialize one structure to another of the same type:

struct names right_field={"Ruthie","George"};

struct names captain=reght_field; // initialize a structure to a structure

In current C, the structure can not only be passed to the function as a parameter, but also be returned as a function return value.

Structural information can be transferred to a function by using structure as function parameter. Structural information can be transferred from the function to the calling function by using function return structure. At the same time, the structure pointer also allows two-way communication, so any method can be used to solve programming problems.

To compare these two methods, we write a simple program that handles the structure with a pointer, and then rewrite the program with structure transfer and structure return. The program itself asks you to enter your name and name, and then tells you the total number of letters in your name.

Listing 14.8 gives the pointer version of the program.

List of procedures 14.8

/*funds2.c  --Use pointers to structures*/
#include <stdio.h>
#include <string.h>

struct namect {
    char fname[20];
    char lname[20];
    int letters;
};

void getinfo(struct namect *);
void makeinfo(struct namect *);
void showinfo(const struct namect *);

int main(void)
{
    struct namect person;

    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    return 0;
}

void getinfo(struct namect *pst)
{
    printf("Please enter your first name.\n");
    gets(pst->fname);
    printf("Please enter your last name.\n");
    gets(pst->lname);
}

void makeinfo(struct namect *pst)
{
    pst->letters=strlen(pst->fname)+
                 strlen(pst->lname);
}

void showinfo(const struct namect *pst)
{
    printf("%s %s,your name contains %d letters.\n",
           pst->fname,pst->lname,pst->letters);
}

The work of the program is assigned to three functions called by main(). The address of the person structure is passed to each function.

The getinfo() function passes information from itself to main(). Specifically, it gets the name from the user and puts the name into the person structure by using the pointer pst location. Recall that pst - > lname is an lname member of the structure to which pst refers. This makes pst - > lname equivalent to the name of a char array, so it is suitable for getting () parameters. Although getinfo() provides information to the main program, it does not use a return mechanism, so it is of void type.

The function makeinfo() performs a confident two-way pass. It uses a pointer to person to determine the location of the names and names stored in the structure. It uses the function strlen() in the C function library to calculate the total number of letters in the name and name, and then uses the person's address to store the total number. Its type is also void. Finally, showinfo() uses a pointer to locate the information to be printed. Because this function does not change the content of the structure, it uses const.

In all operations, there is only one structural variable, person, which is accessed by each function using the address of the structure. One function (getinfo ()) passes information from the function itself to the calling function, and one function (showinfo()) passes information from the calling function to the function itself. A function does both tasks.

Now let's look at how to use structural parameters and return values to accomplish this task. First, in order to pass the structure itself, you need to use the parameter person instead of & person. The corresponding formal parameter should then be declared as a struct namect type, rather than as a pointer to that type. Secondly, to supply the structure value to the main() function, a structure can be returned. Listing 14.9 shows a version that does not use pointers.

Program List 14.9 names2.c Program

/*funds2.c  --Use pointers to structures*/
#include <stdio.h>
#include <string.h>

struct namect {
    char fname[20];
    char lname[20];
    int letters;
};

struct namect getinfo(void);
struct namect makeinfo(struct namect);
void showinfo(struct namect);

int main(void)
{
    struct namect person;

    person=getinfo();
    person=makeinfo(person);
    showinfo(person);
    return 0;
}

struct namect getinfo(void)
{
    struct namect temp;
    printf("Please enter your first name.\n");
    gets(temp.fname);
    printf("Please enter your last name.\n");
    gets(temp.lname);

    return temp;
}

struct namect makeinfo(struct namect info)
{
    info.letters=strlen(info.fname)+
                 strlen(info.lname);

    return info;
}

void showinfo(struct namect info)
{
    printf("%s %s,your name contains %d letters.\n",
           info.fname,info.lname,info.letters);
}

Each of the three functions creates its own copy of person, so instead of using one structure, the program uses four different structures.

For example, consider the function makeinfo(). In the second version, a new structural variable named info was created. The values stored in person are copied to info, and the function processes the copy. So when calculating the total number of letters, the value is stored in info, not in person. However, the return mechanism compensates for this. The following line in makeinfo():

return info;

With the lines in main():

person=makeinfo(person);

Combined, the values stored in info are copied into person. Note that the makeinfo() function must be declared as struct namect because it returns a structure.

14.7.5 Structure or Pointer to Structure

The pointer as a parameter has two advantages: it works on earlier C and newer C, and it executes quickly; it only needs to pass a single address. The disadvantage is the lack of data protection. Some operations in the tuned function may inadvertently affect the data in the original structure. However, ANSI C adds const qualifier to solve this problem. For example, if you write code in the showinfo() function that changes any member of the structure, the compiler catches it as an error.

One advantage of using structure as a parameter is that the function handles copies of the original data, which is safer than dealing with the original data directly. Programming styles are often clearer.

The two major drawbacks of the delivery structure are that early C implementations may not handle this code, and this wastes time and space. Transferring a large structure to a function requires only one or two structural members, which wastes time and space in particular. In this case, it would be more reasonable to pass pointers or only the required members as parameters.

Typically, programmers use structural pointers as function parameters in pursuit of efficiency; const qualifiers are used for pointers when data need to be protected against unexpected data. Transferring structural values is the most commonly used method for handling small structures.

14.7.6 Use character arrays or character pointers in structures

The previous examples used character arrays to store strings in structures. You may wonder if you can replace character arrays with pointers to characters. For example, in Listing 14.3 of Programs, there is a declaration that:

#define LEN 20
struct names {
    char first[LEN];
    char last[LEN];
};

//Can you rewrite it as follows?

struct pnames{
  char *fist;
  char *last;
};

The answer is yes, but there may be trouble unless you understand what it means. Consider the following code:

struct names veep = {"Talia","summers"};
struct pnames treas = {"Brad","Fallingjaw"};
printf("%s and %s\n",veep.first,treas.first);

This is the right piece of code and works, but think about where the strings are stored? For the variable veep, strings are stored inside the structure; in total, I don't allocate 40 bytes to store two strings. However, for the pnames variable treas, strings are stored anywhere in the compiler that stores string constants. This structure only stores two addresses, which takes up a total of eight bytes in our system. The struct pnames structure does not allocate any storage space for strings. It applies only to strings that have been allocated space elsewhere (such as string constants or strings in arrays). Simply put, pointers in the pnames structure should only be used to manage strings that have been created and that have been allocated space elsewhere in the program.

Let's see under what circumstances this qualification will escalate into a problem. Consider the following code:

struct names accountant;
struct pnames accorney;
puts("Enter the last name of your accountant: ");
scanf("%s",accountant.last);
puts("Enter the last name of your attorney: ");
scanf("%s",attorney.last); /*There is a potential risk here.*/

From a grammatical point of view, this code is correct. But where is the input stored? For an accountant, his name is stored in the last member of the accountant variable; this structure has an array for storing strings. For lawyers, scanf() places strings in addresses given by attorney.last. Because this is an uninitialized variable, the address can be any value, and the program can put the name anywhere. If you're lucky, at least sometimes the program will work properly. Otherwise, this operation may terminate the program completely. In fact, if the program runs, it will not increase, because the program will contain programming errors that you have not wronged.

Therefore, if you need a structure to store strings, use character array members. Storage character pointers have their uses, but they can also be seriously misused.

14.7.7 Structure, pointer and malloc()

A meaningful example of using pointers to process strings in structures is to allocate memory using malloc() and store addresses using pointers. The advantage of this method is that malloc() can be requested to allocate exactly the amount of space required by Hunan Road string.

It does not take much effort to turn program list 14.9 into this method. The two main changes are to change the structure definition, using pointers instead of arrays, and then give a new form of getinfo() function.

The new structure definition is as follows:

struct namect {
    char *fname;
    char *lname;
    int letters;
};

The new form of getinfo() reads input into a temporary array; allocates storage space with malloc(), and then copies strings into the newly allocated space. Do this for each name:
 

void getinfo(struct namect *pst)
{
    char temp[81];
    printf("Please enter your first name.\n");
    gets(temp);
    //Allocate memory for names
    pst->fname = (char*)malloc(strlen(temp)+1);
    //Copy names into allocated memory
    strcpy(pst->fname,temp);
    printf("Please enter your last name.\n");
    gets(temp);
    pst->lname = (char *)malloc(strlen(temp)+1);
    strcpy(pst->lanme,temp);
}

Make sure you understand that neither string is stored in the structure, and that they are stored in memory blocks managed by malloc(). However, the addresses of the two strings are preserved in the structure, and these addresses are the objects normally handled by string processing functions. In this way, the rest of the functions in the program need not be changed.

But as recommended in Chapter 12, free() should also be called after malloc(), so the program adds a new function named cleanup(), which frees the memory after the program has used it. This function and the rest of the program can be found in Listing 14.10.

//name3.c -- Using pointers and malloc() functions
#include <stdio.h>
#Include < string.h >// To use strcpy(),strlen()
#Include < stdlib.h >// To use malloc(),free()

struct namect {
    char *fname;    //Use pointers
    char *lname;
    int letters;
};

void getinfo(struct namect *);    //Allocate memory
void makeinfo(struct namect *);
void showinfo(const struct namect *);
void cleanup(struct namect *);  //Release memory after use

int main(void)
{
    struct namect person;

    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    cleanup(&person);
    return 0;
}

getinfo(struct namect *pst)
{
    char temp[81];  //Temporary array
    printf("Please enter your first name.\n");
    gets(temp);
    //Memory used to allocate names
    pst->fname = (char *)malloc(strlen(temp)+1);
    //Copy names to allocated memory
    strcpy(pst->fname,temp);
    printf("Please enter your last name.\n");
    gets(temp);
    pst->lname = (char *)malloc(strlen(temp)+1);
    strcpy(pst->lname,temp);
}

makeinfo(struct namect *pst)
{
    pst->letters=strlen(pst->fname)+
                 strlen(pst->lname);
}

void showinfo(const struct namect *pst)
{
    printf("%s %s,your name contains %d letters.\n",
           pst->fname,pst->lname,pst->letters);
}
void cleanup(struct namect *pst)
{
    free(pst->fname);
    free(pst->lname);
}

14.7.8 Compound Text and Structure (C99)

The new features of compound text in c99 are applicable not only to arrays, but also to structures. Composite text can be used to create a structure that can be used as a function parameter or assigned to another structure. The grammar is to write the type name in parentheses followed by a list of initialization items enclosed in curly brackets. For example, here is a struct book type compound text:

(struct book){"The Idiot","Fyodor Dostoyevsky",6.99}

Program Listing 14.11 Compt. C gives an example of using compound text to selectively assign structural variables.

/*complit.c  Compound Text*/
#include <stdio.h>
#define MAXTITL 41
#define MAXAUTL 31

struct book {
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};

int main(void)
{
    struct book readfirst;
    int score;

    printf("Enter test score: ");
    scanf("%d",&score);

    if(score>=84)
    readfirst=(struct book){"Crime and Punishment",
                            "Fyodor Dostoyevsky",
                            9.99};

    else
    readfirst=(struct book){"Mr.Bouncy's Nice Hat",
                            "Fred Winsome",
                            5.99};
    printf("Your assigned reading:\n");
    printf("%s by %s:$%0.2f\n",readfirst.title,
           readfirst.author,readfirst.value);
    return 0;
}

Compound text can also be used as a function parameter. If a function needs a structure, it can be passed as an actual parameter by using compound text:

struct rect {double x;double y};
double rect_area(struct rect r)
{
    return r.x * r.y;
}
...
double area;
area=rect_area((struct rect){10.5, 20.0});

This assigns area to 210.0.

If you need an address, you can pass the address of a compound text to it:

struct rect{double x;double y};
double rect_areap(struct rect *rp)
{
    return rp->x * rp->y;
}
...
double area;
area = rect_areap(&(struct rect){10.5, 20.0});

This assigns area to 210.0.

Composite text appearing outside all functions has a static storage period, while compound text appearing inside a block of code has an automatic storage period. The syntax for routine initialization of item lists is also applicable to compound text. This means, for example, that initialization items can be specified in compound text.

14.7.9 Scalable Group Membership (C99)

C99 has a new feature called flexible array member. Using this new feature, you can declare that the last member is the structure of an array with special attributes. One of the special attributes of this group member is that it does not exist, at least not immediately. The second special property is that you can write the appropriate code to use this scalable array member, just as it does exist and has any number of elements you need.

First, look at the rules for declaring members of a scalable array:

** The scalable group member must be the last member of the group.

** There must be at least one other member in the structure;

** Scalable arrays are declared just like ordinary arrays except that they are empty in square brackets.

Here is an example to illustrate these rules:

struct flex 
{
    int count;
    double average;
    double scores[];  //Scalable Group Members
};

If you declare a variable of struct flex type, you can't do anything with scores because no memory space is allocated for it. In fact, the intention of c99 is not to let you declare variables of struct flex type, but rather to declare pointers of struct flex type, and then use malloc() to allocate enough space to store the normal content of struct flex structure and any additional space required by members of scalable array. If you want scores to represent an array with five double-type values now, do this:

struct flex *pf;  //Declare a pointer
//Request space for a structure and an array
pf=malloc(sizeof(struct flex)+5*sizeof(double)); 

Now you have a large enough memory to store count, average, and an array with five double values. You can use the pointer pf to access these members:

Pf - > count = 5; // Sets the value of count members;

Pf - > scores [2] = 18.5; / / an element of the members of the access array;

Listing 14.12 further expands this example by letting members of a scalable array represent five values in the first case and nine values in the second case. It also shows how to write a function that handles the structure with scalable group members.

/*flexmemeb.c --Scalable Group Members*/
#include <stdio.h>
#include <stdlib.h>

struct flex
{
    int count;
    double average;
    double scores[];
};

void showflex(const struct flex *p);
int main(void)
{
    struct flex *pf1, *pf2;
    int n=5;
    int i;
    int tot=0;

    //Allocate memory for structures and arrays
    pf1=malloc(sizeof(struct flex)+n*sizeof(double));
    pf1->count=n;
    for(i=0;i<n;i++)
    {
        pf1->scores[i]=20.0-i;
        tot+=pf1->scores[i];
    }
    pf1->average=tot/n;
    showflex(pf1);

    n=9;
    tot=0;
    pf2=malloc(sizeof(struct flex)+n*sizeof(double));
    pf2->count=n;
    for(i=0;i<n;i++)
    {
        pf2->scores[i]=20.0-i/2.0;
        tot+=pf2->scores[i];
    }
    pf2->average = tot/n;
    showflex(pf2);

    free(pf1);
    free(pf2);

    return 0;
}

void showflex(const struct flex *p)
{
    int i;
    printf("Scores: ");
    for(i=0;i<p->count;i++)
        printf("%g ",p->scores[i]);
    printf("\nAverage: %g\n",p->average);
}

14.7.10 Use Structural Group Membership

Suppose you need a function to handle structured arrays. Because the name of the array is the same as its address, you can pass the array name to the function. Again, the function needs to access the structure template. Listing 14.13 expands the program for money to two people, an array with two funds structures.

Program List 14.13 Funs4.c

/*funds4.c  --Transfer a structured array to a function*/
#include <stdio.h>
#define FUNDLEN 50
#define N 2

struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};

double sum(const struct funds money[],int n);

int main(void)
{
    struct funds jones[N] ={
        {
            "Garlic - Melon Bank",
            3024.72,
            "Lucky's Savings and Loan",
            9237.11
        },
        {
            "Honest Jack's Bank",
            3534.28,
            "Party Time Savings",
            3203.89
        }
    };
    printf("The Joneses have a total of $%.2f.\n",sum(jones,N));
    return 0;
}

double sum(const struct funds money[],int n)
{
    double total;
    int i;

    for(i=0,total=0;i<n;i++)
        total += money[i].bankfund+money[i].savefund;
    return total;
}

The array name jones is the address of the array. Specifically, it is the address of the first element of the array, the structure jones[0]. Therefore, the pointer money was originally given by this expression:

money = &jones[0];

Because money points to the first element of the array jones, money[0] is another name for the first element of the array. Also money[1] is the second element. Each element is a funds structure, so each element can use the (.) operator to access its constituent members.

The following are the main points:

** The address of the first structure in the array can be passed to the function with the array name.

** You can then use the square brackets of the array to access the subsequent structure of the array. Note that the following function calls and array names have the same effect:

sum(&jones[0],N)

Because they both point to the same address. Using array names is only an indirect way of passing structural addresses.

** Because the function sum() does not need to change the original data, we use the qualifier const of ANSI C.

Topics: Programming REST