C语言向函数传递结构信息的7种途径


C语言向函数传递结构信息的7种途径

函数的参数把值传递给函数。每个值都是一个数字:可能是int类型、float类型,可能是ASCII字符码,或者是一个地址。然而,一个结构比一个单独的值复杂,所以难怪以前的C实现不允许把结构作为参数传递给函数。当前的实现已经移除了这个限制,ANSI-C允许把结构作为参数使用。所以程序员可以选择是传递结构本身,还是传递指向结构的指针。如果你只关心结构中的某一部分,也可以把结构的成员作为参数。我们接下来将分析这3种传递方式,首先介绍以结构成员作为参数的情况。

1 传递结构成员

只要结构成员是一个具有单个值的数据类型(即,int及其相关类型、char、float、double或指针),便可把它作为参数传递给接受该特定类型的函数。程序funds1.c中的财务分析程序(初级版本)演示了这一点,该程序把客户的银行账户添加到他/她的储蓄和贷款账户中。

/* funds1.c -- passing structure members as arguments */
#include 
#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",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
​
    printf("Stan has a total of $%.2f.n",
           sum(stan.bankfund, stan.savefund) );
    return 0;
}
​
/* adds two double numbers */
double sum(double x, double y)
{
    return(x + y);
}

运行该程序后输出如下:

Stan has a total of $12576.21.

看来,这样传递参数没问题。注意,sum()函数既不知道也不关心实际的参数是否是结构的成员,它只要求传入的数据是double类型。当然,如果需要在被调函数中修改主调函数中成员的值,就要传递成员的地址:

modify(&stan.bankfund);

这是一个更改银行账户的函数。把结构的信息告诉函数的第2种方法是,让被调函数知道自己正在处理一个结构。

2 传递结构的地址

我们继续解决前面的问题,但是这次把结构的地址作为参数。由于函数要处理funds结构,所以必须声明funds结构。如程序funds2.c 所示。

/* funds2.c -- passing a pointer to a structure */
#include 
#define FUNDLEN 50
​
struct funds {
    char   bank[FUNDLEN];
    double bankfund;
    char   save[FUNDLEN];
    double savefund;
};
​
double sum(const struct funds *);  /* argument is a pointer */
​
int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
​
    printf("Stan has a total of $%.2f.n", sum(&stan));
​
    return 0;
}
​
double sum(const struct funds * money)
{
    return(money->bankfund + money->savefund);
}

运行该程序后输出如下:

Stan has a total of $12576.21.

sum()函数使用指向funds结构的指针(money)作为它的参数。把地址&stan传递给该函数,使得指针money指向结构变量stan。然后通过->运算符获取stan.bankfund和stan.savefund的值。由于该函数不能改变指针所指向值的内容,所以把money声明为一个指向const的指针。 虽然该函数并未使用其他成员,但是也可以访问它们。注意,必须使用&运算符来获取结构的地址。和数组名不同,结构变量名不是其地址的别名。

3 传递结构

对于允许把结构作为参数的编译器,可以把程序funds2.c 重写为程序funds3.c 。

/* funds3.c -- passing a structure */
#include 
#define FUNDLEN 50
​
struct funds {
    char   bank[FUNDLEN];
    double bankfund;
    char   save[FUNDLEN];
    double savefund;
};
​
double sum(struct funds moolah);  /* argument is a structure */
​
int main(void)
{
    struct funds stan = {
        "Garlic-Melon Bank",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
​
    printf("Stan has a total of $%.2f.n", sum(stan));
​
    return 0;
}
​
double sum(struct funds moolah)
{
    return(moolah.bankfund + moolah.savefund);
}

下面是运行该程序后的输出:

Stan has a total of $12576.21.

该程序把程序funds3.c 中指向struct funds类型的结构指针money替换成struct funds类型的结构变量moolah。调用sum()时,编译器根据funds模板创建了一个名为moolah的自动结构变量。然后,该结构的各成员被初始化为stan结构变量相应成员的值的副本。因此,程序使用原来结构的副本进行计算,然而,传递指针的程序funds3.c 使用的是原始的结构进行计算。由于moolah是一个结构,所以该程序使用moolah.bankfund,而不是moolah->bankfund。另一方面,由于money是指针,不是结构,所以程序funds3.c 使用的是money->bankfund。

4 其他结构特性

现在的C允许把一个结构赋值给另一个结构,但是数组不能这样做。也就是说,如果ndata和odata都是相同类型的结构,可以这样做:

o_data = n_data;    // assigning one structure to another

这条语句把ndata的每个成员的值都赋给odata的相应成员。即使成员是数组,也能完成赋值。另外,还可以把一个结构初始化为相同类型的另一个结构:

struct names right_field = {"Ruthie", "George"};
struct names captain = right_field;  // initialize a structure to another

现在的C(包括ANSI-C),函数不仅能把结构本身作为参数传递,还能把结构作为返回值返回。把结构作为函数参数可以把结构的信息传送给函数;把结构作为返回值的函数能把结构的信息从被调函数传回主调函数。结构指针也允许这种双向通信,因此可以选择任一种方法来解决编程问题。我们通过另一组程序示例来演示这两种方法。 为了对比这两种方法,我们先编写一个程序以传递指针的方式处理结构(见程序names1.c),然后以传递结构和返回结构的方式重写该程序。

/* names1.c -- uses pointers to a structure */
#include 
#include 

#define NLEN 30
struct namect {
    char fname[NLEN];
    char lname[NLEN];
    int letters;
};

void getinfo(struct namect *);
void makeinfo(struct namect *);
void showinfo(const struct namect *);
char * s_gets(char * st, int n);

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");
    s_gets(pst->fname, NLEN);
    printf("Please enter your last name.n");
    s_gets(pst->lname, NLEN);
}

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);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;

    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, 'n');   // look for newline
        if (find)                  // if the address is not NULL,
            *find = '0';          // place a null character there
        else
            while (getchar() != 'n')
                continue;          // dispose of rest of line
    }
    return ret_val;
}

下面是编译并运行该程序后的一个输出示例:

Please enter your first name. Viola Please enter your last name. Plunderfest Viola Plunderfest, your name contains 16 letters.

该程序把任务分配给3个函数来完成,都在main()中调用。每调用一个函数就把person结构的地址传递给它。 getinfo()函数把结构的信息从自身传递给main()。该函数通过与用户交互获得姓名,并通过pst指针定位,将其放入person结构中。由于pst->lname意味着pst指向结构的lname成员,这使得pst->lname等价于char数组的名称,因此做sgets()的参数很合适。注意,虽然getinfo()给main()提供了信息,但是它并未使用返回机制,所以其返回类型是void。 makeinfo()函数使用双向传输方式传送信息。通过使用指向person的指针,该指针定位了存储在该结构中的名和姓。该函数使用C库函数strlen()分别计算名和姓中的字母总数,然后使用person的地址存储两数之和。同样,makeinfo()函数的返回类型也是void。 showinfo()函数使用一个指针定位待打印的信息。因为该函数不改变数组的内容,所以将其声明为const。 所有这些操作中,只有一个结构变量person,每个函数都使用该结构变量的地址来访问它。一个函数把信息从自身传回主调函数,一个函数把信息从主调函数传给自身,一个函数通过双向传输来传递信息。 现在,我们来看如何使用结构参数和返回值来完成相同的任务。第一,为了传递结构本身,函数的参数必须是person,而不是&person。那么,相应的形式参数应声明为structnamect,而不是指向该类型的指针。第二,可以通过返回一个结构,把结构的信息返回给main()。程序names2.c演示了不使用指针的版本。

/* names2.c -- passes and returns structures */
#include 
#include 

#define NLEN 30
struct namect {
    char fname[NLEN];
    char lname[NLEN];
    int letters;
};

struct namect getinfo(void);
struct namect makeinfo(struct namect);
void showinfo(struct namect);
char * s_gets(char * st, int n);

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");
    s_gets(temp.fname, NLEN);
    printf("Please enter your last name.n");
    s_gets(temp.lname, NLEN);

    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);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;

    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, 'n');   // look for newline
        if (find)                  // if the address is not NULL,
            *find = '0';          // place a null character there
        else
            while (getchar() != 'n')
                continue;          // dispose of rest of line
    }
    return ret_val;
}

该版本最终的输出和前面版本相同,但是它使用了不同的方式。程序中的每个函数都创建了自己的person备份,所以该程序使用了4个不同的结构,不像前面的版本只使用一个结构。 例如,考虑makeinfo()函数。在第1个程序中,传递的是person的地址,该函数实际上处理的是person的值。在第2个版本的程序中,创建了一个新的结构info。存储在person中的值被拷贝到info中,函数处理的是这个副本。因此,统计完字母个数后,计算结果存储在info中,而不是person中。然而,返回机制弥补了这一点。makeinfo()中的这行代码:

return info;

与main()中的这行结合:

person = makeinfo(person);

把存储在info中的值拷贝到person中。注意,必须把makeinfo()函数声明为struct namect类型,因为该函数要返回一个结构。

5 结构和结构指针的选择

假设要编写一个与结构相关的函数,是用结构指针作为参数,还是用结构作为参数和返回值?两者各有优缺点。 把指针作为参数有两个优点:无论是以前还是现在的C实现都能使用这种方法,而且执行起来很快,只需要传递一个地址。缺点是无法保护数据。被调函数中的某些操作可能会意外影响原来结构中的数据。不过,ANSI-C新增的const限定符解决了这个问题。例如,如果在程序names1.c中,showinfo()函数中的代码改变了结构的任意成员,编译器会捕获这个错误。 把结构作为参数传递的优点是,函数处理的是原始数据的副本,这保护了原始数据。另外,代码风格也更清楚。假设定义了下面的结构类型:

struct vector {double x; double y;};

如果用vector类型的结构ans存储相同类型结构a和b的和,就要把结构作为参数和返回值:

struct vector ans, a, b;
struct vector sum_vect(struct vector, struct vector);
...
ans = sum_vect(a,b);

对程序员而言,上面的版本比用指针传递的版本更自然。指针版本如下:

struct vector ans, a, b;
void sum_vect(const struct vector *, const struct vector *, struct vector *);
...
sum_vect(&a, &b, &ans);

另外,如果使用指针版本,程序员必须记住总和的地址应该是第1个参数还是第2个参数的地址。 传递结构的两个缺点是:较老版本的实现可能无法处理这样的代码,而且传递结构浪费时间和存储空间。尤其是把大型结构传递给函数,而它只使用结构中的一两个成员时特别浪费。这种情况下传递指针或只传递函数所需的成员更合理。 通常,程序员为了追求效率会使用结构指针作为函数参数,如需防止原始数据被意外修改,使用const限定符。按值传递结构是处理小型结构最常用的方法。

6 结构中的字符数组和字符指针

到目前为止,我们在结构中都使用字符数组来存储字符串。是否可以使用指向char的指针来代替字符数组?例如,程序清单14.3中有如下声明:

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

Can you do this instead?

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

其中的结构声明是否可以这样写:

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

以上代码都没问题,也能正常运行,但是思考一下字符串被存储在何处。对于struct names类型的结构变量veep,以上字符串都存储在结构内部,结构总共要分配40字节存储姓名。然而,对于struct pnames类型的结构变量treas,以上字符串存储在编译器存储常量的地方。结构本身只存储了两个地址,在我们的系统中共占16字节。尤其是,struct pnames结构不用为字符串分配任何存储空间。它使用的是存储在别处的字符串(如,字符串常量或数组中的字符串)。简而言之,在pnames结构变量中的指针应该只用来在程序中管理那些已分配和在别处分配的字符串。 我们看看这种限制在什么情况下出问题。考虑下面的代码:

struct names accountant;
struct pnames attorney;
puts("Enter the last name of your accountant:");
scanf("%s", accountant.last);
puts("Enter the last name of your attorney:");
scanf("%s", attorney.last);   /* here lies the danger */

就语法而言,这段代码没问题。但是,用户的输入存储到哪里去了?对于会计师(accountant),他的名存储在accountant结构变量的last成员中,该结构中有一个存储字符串的数组。对于律师(attorney),scanf()把字符串放到attorney.last表示的地址上。由于这是未经初始化的变量,地址可以是任何值,因此程序可以把名放在任何地方。如果走运的话,程序不会出问题,至少暂时不会出问题,否则这一操作会导致程序崩溃。实际上,如果程序能正常运行并不是好事,因为这意味着一个未被觉察的危险潜伏在程序中。 因此,如果要用结构存储字符串,用字符数组作为成员比较简单。用指向char的指针也行,但是误用会导致严重的问题。

7 结构、指针和malloc()

如果使用malloc()分配内存并使用指针存储该地址,那么在结构中使用指针处理字符串就比较合理。这种方法的优点是,可以请求malloc()为字符串分配合适的存储空间。可以要求用4字节存储"Joe"和用18字节存储"Rasolofomasoandro"。用这种方法改写程序清单14.9并不费劲。主要是更改结构声明(用指针代替数组)和提供一个新版本的getinfo()函数。新的结构声明如下:

struct namect {
    char * fname;  // using pointers instead of arrays
    char * lname;
    int letters;
};

新版本的getinfo()把用户的输入读入临时数组中,调用malloc()函数分配存储空间,并把字符串拷贝到新分配的存储空间中。对名和姓都要这样做:

void getinfo (struct namect * pst)
{
    char temp[SLEN];
    printf("Please enter your first name.n");
    s_gets(temp, SLEN);
    // allocate memory to hold name
    pst->fname = (char *) malloc(strlen(temp) + 1);
    // copy name to allocated memory
    strcpy(pst->fname, temp);
    printf("Please enter your last name.n");
    s_gets(temp, SLEN);
    pst->lname = (char *) malloc(strlen(temp) + 1);
    strcpy(pst->lname, temp);
}

要理解这两个字符串都未存储在结构中,它们存储在malloc()分配的内存块中。然而,结构中存储着这两个字符串的地址,处理字符串的函数通常都要使用字符串的地址。因此,不用修改程序中的其他函数。还要在程序中添加一个新的函数cleanup(),用于释放程序动态分配的内存。如程序names3.c所示。

// names3.c -- use pointers and malloc()
#include 
#include    // for strcpy(), strlen()
#include    // for malloc(), free()
#define SLEN 81
struct namect {
    char * fname;  // using pointers
    char * lname;
    int letters;
};

void getinfo(struct namect *);        // allocates memory
void makeinfo(struct namect *);
void showinfo(const struct namect *);
void cleanup(struct namect *);        // free memory when done
char * s_gets(char * st, int n);

int main(void)
{
    struct namect person;

    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    cleanup(&person);

    return 0;
}

void getinfo (struct namect * pst)
{
    char temp[SLEN];
    printf("Please enter your first name.n");
    s_gets(temp, SLEN);
    // allocate memory to hold name
    pst->fname = (char *) malloc(strlen(temp) + 1);
    // copy name to allocated memory
    strcpy(pst->fname, temp);
    printf("Please enter your last name.n");
    s_gets(temp, SLEN);
    pst->lname = (char *) malloc(strlen(temp) + 1);
    strcpy(pst->lname, temp);
}

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);
}

void cleanup(struct namect * pst)
{
    free(pst->fname);
    free(pst->lname);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;

    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, 'n');   // look for newline
        if (find)                  // if the address is not NULL,
            *find = '0';          // place a null character there
        else
            while (getchar() != 'n')
                continue;          // dispose of rest of line
    }
    return ret_val;
}

下面是该程序的输出:

Please enter your first name. Floresiensis Please enter your last name. Mann Floresiensis Mann, your name contains 16 letters.

8 复合字面量和结构(C99)

C99的复合字面量特性可用于结构和数组。如果只需要一个临时结构值,复合字面量很好用。例如,可以使用复合字面量创建一个结构作为函数的参数或赋给另一个结构。语法是把类型名放在圆括号中,后面紧跟一个用花括号括起来的初始化列表。例如,下面是struct book类型的复合字面量:

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

程序complit.c中的程序示例,使用复合字面量为一个结构变量提供两个可替换的值(在撰写本书时,并不是所有的编译器都支持这个特性,不过这是时间的问题)。

/* complit.c -- compound literals */
#include 
#define MAXTITL  41
#define MAXAUTL  31

struct book {          // structure template: tag is 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",
                                   11.25};
    else
          readfirst = (struct book) {"Mr. Bouncy's Nice Hat",
                                   "Fred Winsome",
                                    5.99};
    printf("Your assigned reading:n");
    printf("%s by %s: $%.2fn",readfirst.title,
          readfirst.author, readfirst.value);

    return 0;
}

还可以把复合字面量作为函数的参数。如果函数接受一个结构,可以把复合字面量作为实际参数传递:

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});

值210被赋给area。如果函数接受一个地址,可以传递复合字面量的地址:

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});

值210被赋给area。复合字面量在所有函数的外部,具有静态存储期;如果复合字面量在块中,则具有自动存储期。复合字面量和普通初始化列表的语法规则相同。这意味着,可以在复合字面量中使用指定初始化器。

9 伸缩型数组成员(C99)

C99新增了一个特性:伸缩型数组成员(flexible-arraymember),利用这项特性声明的结构,其最后一个数组成员具有一些特性。第1个特性是,该数组不会立即存在。第2个特性是,使用这个伸缩型数组成员可以编写合适的代码,就好像它确实存在并具有所需数目的元素一样。这可能听起来很奇怪,所以我们来一步步地创建和使用一个带伸缩型数组成员的结构。 首先,声明一个伸缩型数组成员有如下规则:

下面用一个示例来解释以上几点:

struct flex
{
    int count;
    double average;
    double scores[];   // flexible array member
};

声明一个struct flex类型的结构变量时,不能用scores做任何事,因为没有给这个数组预留存储空间。实际上,C99的意图并不是让你声明struct flex类型的变量,而是希望你声明一个指向struct flex类型的指针,然后用malloc()来分配足够的空间,以存储struct flex类型结构的常规内容和伸缩型数组成员所需的额外空间。例如,假设用scores表示一个内含5个double类型值的数组,可以这样做:

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

现在有足够的存储空间存储count、average和一个内含5个double类型值的数组。可以用指针pf访问这些成员:

pf->count = 5;          // set count member
pf->scores[2] = 18.5;   // access an element of the array member

程序flexmemb.c进一步扩展了这个例子,让伸缩型数组成员在第1种情况下表示5个值,在第2种情况下代表9个值。该程序也演示了如何编写一个函数处理带伸缩型数组元素的结构。

Listing 14.12 The flexmemb.c Program

// flexmemb.c -- flexible array member (C99 feature)
#include 
#include 

struct flex
{
    size_t count;
    double average;
    double scores[];   // flexible array member
};

void showFlex(const struct flex * p);

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

    // allocate space for structure plus array
    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: %gn", p->average);
}

下面是该程序的输出:

Scores : 20 19 18 17 16 Average: 18 Scores : 20 19.5 19 18.5 18 17.5 17 16.5 16 Average: 17

带伸缩型数组成员的结构确实有一些特殊的处理要求。 第一,不能用结构进行赋值或拷贝:

    struct flex * pf1, *pf2;  // *pf1 and *pf2 are structures
...
    *pf2 = *pf1;   // don't do this

这样做只能拷贝除伸缩型数组成员以外的其他成员。确实要进行拷贝,应使用memcpy()函数。 第二,不要以按值方式把这种结构传递给结构。原因相同,按值传递一个参数与赋值类似。要把结构的地址传递给函数。 第三,不要使用带伸缩型数组成员的结构作为数组成员或另一个结构的成员。

这种类似于在结构中最后一个成员是伸缩型数组的情况,称为struct hack。除了伸缩型数组成员在声明时用空的方括号外,struct hack特指大小为0的数组。然而,struct hack是针对特殊编译器(GCC)的,不属于C标准。这种伸缩型数组成员方法是标准认可的编程技巧。

10 匿名结构(C11)

匿名结构是一个没有名称的结构成员。为了理解它的工作原理,我们先考虑如何创建嵌套结构:

struct names
{
    char first[20];
    char last[20];
};
struct person
{
    int id;
    struct names name;  // nested structure member
};
struct person ted = {8483, {"Ted", "Grass"}};

这里,name成员是一个嵌套结构,可以通过类似ted.name.first的表达式访问"ted":

puts(ted.name.first);

在C11中,可以用嵌套的匿名成员结构定义person:

struct person
{
    int id;
    struct {char first[20]; char last[20];};  // anonymous structure
};

初始化ted的方式相同:

struct person ted = {8483, {"Ted", "Grass"}};

但是,在访问ted时简化了步骤,只需把first看作是person的成员那样使用它:

puts(ted.first);

当然,也可以把first和last直接作为person的成员,删除嵌套循环。匿名特性在嵌套联合中更加有用,我们在本章后面介绍。

11 使用结构数组的函数

假设一个函数要处理一个结构数组。由于数组名就是该数组的地址,所以可以把它传递给函数。另外,该函数还需访问结构模板。为了理解该函数的工作原理,程序funds4.c把前面的金融程序扩展为两人,所以需要一个内含两个funds结构的数组。

/* funds4.c -- passing an array of structures to a function */
#include 
#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",
            4032.27,
            "Lucky's Savings and Loan",
            8543.94
​
        },
        {
            "Honest Jack's Bank",
            3620.88,
            "Party Time Savings",
            3802.91
        }
    };
​
    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 Joneses have a total of $20000.00.

(读者也许认为这个总和有些巧合!)数组名jones是该数组的地址,即该数组首元素(jones[0])的地址。因此,指针money的初始值相当于通过下面的表达式获得:

money = &jones[0];

因为money指向jones数组的首元素,所以money[0]是该数组首元素的另一个名称。与此类似,money[1]是第2个元素。每个元素都是一个funds类型的结构,所以都可以使用点运算符(.)来访问funds类型结构的成员。 下面是几个要点。

sum(&jones[0], N)

因为jones和&jones[0]的地址相同,使用数组名是传递结构地址的一种间接的方法。 由于sum()函数不能改变原始数据,所以该函数使用了ANSI C的限定符const。

展开阅读全文

页面更新:2024-04-14

标签:函数   结构   字面   数组   伸缩   字符串   指针   变量   途径   声明   成员   参数   语言   类型   版本

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top