结构作为函数参数:
声明了一个结构就有了一种自定义的数据类型,这个数据类型和int、float、double一样,int等基本类型可以作为函数的参数,那么这种个自定义的结构类型也应该可以作为函数参数,比如:
int numberofDays(struct date d);函数numberofDays的参数就是一种结构变量。
整个结构是可以作为参数的值传入函数的,这时是在函数内新建一个结构变量,并复制调用者的结构的值,这和数组是完全不一样的。结构除了可以作为参数,也可以作为返回值,例如下述程序中,直接将结构变量d传给另外两个函数:
1 #include <stdio.h> 2 #include <stdbool.h> 3 4 struct date{ 5 int month; 6 int day; 7 int year; 8 }; 9 10 bool isLeap(struct date d); //判断是否闰年 ,若是返回true,否则返回false 11 int numberofDays(struct date d); 12 13 int main(int argc,char const *argv[]){ 14 15 struct date today,tomorrow; //定义两个结构变量 16 printf("Enter today's date (mm dd yyyy):"); //输入今天的日期 17 scanf("%i %i %i",&today.month,&today.day,&today.year);//.运算符比&运算符优先级高 18 19 if(today.day != numberofDays(today)){ //如果today不是这个月的最后一天,那么明天就是今天加1,年和月不变 20 tomorrow.day=today.day +1; 21 tomorrow.month=today.month; 22 tomorrow.year=today.year; 23 }else if(today.month == 12){ //如果today是这个月的最后一天,且月份是12月份,那么第二天就是新的一年,月日都是1,年加1 24 tomorrow.day=1; 25 tomorrow.month=1; 26 tomorrow.year=today.year+1; 27 }else{ //如果today不是这个月的最后一天,且月也不是12月,那么明天就是下个月,月加1,day是1,year不变。 28 tomorrow.day=1; 29 tomorrow.month=tomorrow.month+1; 30 tomorrow.year=today.year; 31 } 32 33 printf("Tomorrow's date is %i-%i-%i. '",tomorrow.year,tomorrow.month,tomorrow.day); 34 35 return 0; 36 } 37 38 int numberofDays(struct date d){ 39 int days; 40 const int daysPerMonth[12]={31,28,31,30,31,30,31,31,30,31,30,31};//每个月有多少天 41 if(d.month==2&&isLeap(d)) //如果是2月且是闰年,days是29天 42 days=29; 43 else 44 days=daysPerMonth[d.month-1];//否则直接输出该月的天数,之所以减1是因为数组是从0开始下标的 45 return days; //单一出口 46 } 47 48 bool isLeap(struct date d){ 49 bool leap=false; 50 if((d.year%4==0 && d.year%100!=0)||d.year%400==0) 51 leap =true; 52 return leap; 53 }
输入结构:
int等基础类型可以%d直接使用scanf和printf进行输入输出,但没有一个直接的方式可以一次scanf一个结构,如果打算写一个函数来读入结构:
1 #include <stdio.h> 2 struct point{ 3 int x; 4 int y; 5 }; 6 void getStruct(struct point); //做了一个函数getStruct,参数是一个结构 7 void output(struct point); 8 int main(void){ 9 struct point y={0,0}; //定义一个结构变量y 10 getStruct(y); //将这个结构变量y给函数getStruct, 11 output(y); 12 return 0; 13 } 14 15 void getStruct(struct point p){ //作为参数接收到结构变量y的值 ,p是和y相同值的结构变量 16 scanf("%d",&p.x); 17 scanf("%d",&p.y); //在函数内给p的x和y做了赋值 18 printf("%d,%d ",p.x,p.y); //赋值后输出出来 19 } 20 21 void output(struct point p){ 22 printf("%d,%d ",p.x,p.y); 23 }
编译和运行上述代码,并输入数据4 和5.结果如下:
4 5 4,5 0,0 -------------------------------- Process exited after 2.377 seconds with return value 0 请按任意键继续. . .
在getStruct函数中输入4和5之后,该函数内部输出4和5,但是回到main中,y的值依然是0和0.在getStruct中的变量p只是得到y的值,p和y并没有联系。这一点和数组不一样,C语言里在函数调用时是传值的,所以在getStruct函数中的p与main中的y是不同的,在函数getStruct读入了p的数值之后,没有任何东西回到main,所以y还是{0,0}.
解决方案一 将结构变量作为函数返回值:
所以,把一个结构传入了函数,然后在函数中操作,但是没有返回回去,问题在于传入函数的是外面那个结构的克隆体,而不是指针,传入结构和传入数组是不同的,那么怎么解决这个问题呢?一种方案是在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者,也就似乎可以把上述程序改成这个样子:
1 #include <stdio.h> 2 struct point{ 3 int x; 4 int y; 5 }; 6 7 struct point getStruct(void); //做了一个函数getStruct,没有参数,返回一个结构变量 8 void output(struct point); 9 int main(void){ 10 struct point y={0,0}; 11 y=getStruct(); //调用函数getStruct时,使用y接收该函数的返回值 12 output(y); 13 return 0; 14 } 15 16 struct point getStruct(void){ 17 struct point p; //做一个本地结构变量p,这个变量在离开这个函数时会消失掉 18 scanf("%d",&p.x); 19 scanf("%d",&p.y); 20 printf("%d,%d ",p.x,p.y); 21 return p; 22 } 23 24 void output(struct point p){ 25 printf("%d,%d ",p.x,p.y); 26 }
在输入函数getStruct返回一个结构变量,而没有传入参数,在函数内新建一个本地结构变量p,给p赋值,然后将p返回给main,结果如下,这时y的值就发生了变化:
4 5 4,5 0,0 -------------------------------- Process exited after 2.377 seconds with return value 0 请按任意键继续. . .
解决方案二 结构指针作为函数参数:
在将一个结构传递给函数时,一般不传结构,而是传递结构的指针,这也是比较推荐的方式。在C语言的经典教材K & R说过(p.131):
If a large structure is to be passed to a function, it is generally more efficient to pass a pointer than to copy the whole structure.
因为在C语言中结构的传递方式是值的传递,也就是说需要在被调用的函数里建立一个和调用函数里一模一样的一个结构变量,然后把每一个变量成员的值都拷贝过去,这是一个即费空间(需要建立一个空间存放新的结构变量)又费时间(需要花费时间拷贝)的事情。所以早在K&R的书中就说过,更好的方式是传指针。
因为结构变量名字不是地址,所以需要使用&符号取得地址赋值给一个结构指针,那么使用结构指针怎么获取变量的成员?有两种方式,一般使用->箭头指向的方式:
struct date{ int month; int day; int year; } myday; struct date *p = &myday; (*p).month =12; //*p就表示myday,但是这种书写起来麻烦 p->month =12; //一般使用这种,一般说成p所指的month成员
所有有了这种方式,getStruct就可以改造成下面的样子:
1 #include <stdio.h> 2 struct point{ 3 int x; 4 int y; 5 }; 6 struct point* getStruct(struct point*); //做了一个函数getStruct,参数是一个结构 7 void output(struct point); 8 void print(const struct point *p); 9 int main(int argc, char const *argv[]){ 10 struct point y={0,0}; //定义一个结构变量y 11 getStruct(&y); //将这个结构变量y给函数getStruct, 12 output(y); 13 output(*getStruct(&y)); //对getStruct函数的返回值进行*运算符,取出函数返回值的结构地址里的值。 14 print(getStruct(&y)); //将getStruct函数的返回值返回给print,打印出来 15 16 getStruct(&y)->x =0; //甚至可以对返回值赋值 17 *getStruct(&y)=(struct point){1,2}; 18 return 0; 19 } 20 21 struct point * getStruct(struct point *p){ //作为参数接收到结构变量y的值 ,p是和y相同值的结构变量 22 scanf("%d",&p->x); 23 scanf("%d",&p->y); //在函数内给p的x和y做了赋值 24 printf("%d,%d ",p->x,p->y); //赋值后输出出来 25 return (struct point *)p; //常用套路, 传进函数一个指针,对该指针做了一定处理后,再传出去 。好处是可将该返回值潜在其他函数里。 26 } 27 28 void output(struct point p){ 29 printf("%d,%d ",p.x,p.x); 30 } 31 void print(const struct point *p){ //传递一个point指针,只是输出所以const,不修改point 32 printf("%d,%d",p->x,p->y); 33 }