赋值运算符“=”只能针对同类型的结构变量。对于结构数组,只能像普通数组那样,针对元素之间进行逐个域的赋值。
【例21.1】同类型结构变量之间的相互赋值示例。
#include <stdio.h> void disp (STUDNT ); typedef struct student { char name[10] ; int studnem ; }STUDNT ; struct NUM { int a ; int b ; }m={2 ,5} ,n={3 ,4} ,d ; int main () { STUDNT b[3] ,c ; STUDNT a[3]={{\"Wangping0\" ,1123} , {\"WangPing1\" ,1124} ,{\"Wangping2\" ,1125}} ; disp (a ); b[1]=a[1] ; b[2]=a[2] ; b[0]=a[0] ; disp (b ); c=a[1] ; printf (\" 姓名:%s 学号:%dn\" ,c.name ,c.studnem ); d=n ; d.a+=m.a ; d.b+=m.b ; printf (\"%d %dn\" ,d.a ,d.b ); return 0 ; } void disp (STUDNT a ) { int i ; char st[8]={\" 姓名:\" ,\" 学号:\"} ; for (i=0 ;i<3 ;i++ ) printf (\"%s%s %s%dn\" ,st[0] ,a[i].name ,st[1] ,a[i].studnem ); }
【解释】对于同类型的结构变量,可以直接使用赋值运算符“=”,如
d=n ;
但对结构数组必须逐个赋值,赋值的顺序没有关系,如
b[2]=a[2] ; b[0]=a[0] ; b[1]=a[1] ;
下面是使用for语句按顺序赋值
for (i=0 ;i<3 ;i++ ) b[i]=a[i] ;
结构数组的各个元素可以单独作为变量参与运算,如
b[0].studnum=a[2].studnum ;
同类型的结构变量可以如普通变量一样进行操作,例如:
d.a+=m.a ; d.b+=m.b ;
注意:一定要避免犯“b=a;”的错误。
程序运行结果如下。
姓名:Wangping0 学号:1123 姓名:WangPing1 学号:1124 姓名:Wangping2 学号:1125 姓名:Wangping0 学号:1123 姓名:WangPing1 学号:1124 姓名:Wangping2 学号:1125 姓名:WangPing1 学号:1124 5 9
【例21.2】结构指针赋值示例。
本例设计一个结构,这个结构还具有一个指向自身的指针。
struct NUM { int a ; int b ; struct NUM *next ; }m={2 ,5} ,n={3 ,4} ,d ;
初始化变量m和n时,没有给它们的指针赋初值,没有初始化变量d,所以也没有给变量d的指针next、域a和b赋初始值。程序先演示结构指针变量,再演示next指针概念。为了方便讲解,将输出编号,然后结合输出讲解。
源程序如下。
#include <stdio.h> #include <stdlib.h> struct NUM { int a ; int b ; struct NUM *next ; }m={2 ,5} ,n={3 ,4} ,d ; int main () { struct NUM *p1 ,*p2 ,*p ; p= (struct NUM * )malloc (sizeof (struct NUM )); printf (\"p=%un\" ,p ); // 演示指针变量和结构变量的赋值 p1=&m ; // 初始化指针p1 p2=&n ; // 初始化指针p2 d=m ; // 结构变量之间使用赋值运算符进行整体赋值 printf (\"%d ,%d ,%d ,%d ,%d ,%d ,%un\" ,m.a ,m.b ,n.a ,n.b ,d.a ,d.b ,&d ); *p1=*p2 ; // 演示p1 ≠p2 printf (\"%u ,%u ,%d ,%d ,%d ,%dn\" ,p1 ,p2 ,p1->a ,p1->b ,p2->a ,p2->b ); p1=&d ; p1->a=35 ;p1->b=58 ; m.a=12 ;m.b=15 ; printf (\" 改变p1 的域值为:a=%d ,b=%dn\" ,p1->a ,p1->b ); p2=p1 ; // 演示p1=p2 且*p1=*p2 ,p2 指向的是d printf (\"%u ,%u ,%d ,%d ,%d ,%dn\" ,p1 ,p2 ,p1->a ,p1->b ,p2->a ,p2->b ); p=p2 ; // 演示链表概念,p 具有节点d printf (\"%u ,%u ,%d ,%d ,%d ,%dn\" ,p ,p2 ,p->a ,p->b ,p2->a ,p2->b ); // 让d 的next 指向m 节点,m 作为第2 个节点 d.next=&m ; printf (\"%d ,%d ,%un\" ,d.next->a ,d.next->b ,d.next ); //m 的next 指向n ,n 作为第3 个节点 m.next=&n ; //n 的next 设置为NULL ,作为结束标志 n.next=NULL ; { int i=0 ; for (i=0 ;i<3 ;i++ ,p ){ printf (\"%d ,%d ,%u ,%un\" ,p->a ,p->b ,p ,p->next ); p=p->next ; } } // 使p 指向起点,用NULL 判别,输出链表内容 p=&d ; while (p !=NULL ) { printf (\"%d ,%dn\" ,p->a ,p->b ); p=p->next ; } return 0 ; }
程序运行结果如下。
p=4398640 //1 2 ,5 ,3 ,4 ,2 ,5 ,4339632 //2 4336176 ,4336192 ,3 ,4 ,3 ,4 //3 p1 的a=35 ,b=58 。m 的a=12 ,b=15 //4 4339632 ,4339632 ,35 ,58 ,35 ,58 //5 4339632 ,4339632 ,35 ,58 ,35 ,58 //6 12 ,15 ,4336176 //7 35 ,58 ,4339632 ,4336176 //8 12 ,15 ,4336176 ,4336192 //9 3 ,4 ,4336192 ,0 //10 35 ,58 //11 12 ,15 //12 3 ,4 //13
【解释】前两行的输出,只是作为以后对比的依据,其中也演示整体赋值(d=m)。注意第2行输出d的地址是4339632。
第3行的输出演示了指针变量“*p1=*p2”的赋值效果。这里表示p1的a和b变为p2的a和b,但指针p1并没有放弃自己指向的地址(p1≠p2),所以输出的地址不同。
执行“p1=&d;”,然后演示使用指针改变d的a和b的值,直接改变m的值。输出第4行只是验证修改信息正确,d将作为第1节点,注意两个变量a和b的值。
第5行的输出验证“p2=p1;”的效果。p2=p1表示指针p2放弃了原来指向的地址,与p1指向同一个对象,这就理所当然地使*p2=*p1自然成立。因为不是字符串,所以存储内容也相同。注意和第2行的输出比较,这里指向的地址是结构变量d的存储首地址。
执行“p=p2;”,使d作为第1个节点。第6行的输出与第5行的完全一样。
执行“d.next=&m;”,得到2个节点。第7行的输出是第2个节点信息。
执行“m.next=&n;”,得到3个节点。执行“n.next=NULL;”,作为结束标志。
第8-10行的输出演示了链表关系。每一行均显示节点数据、节点存储首地址和指向下一个节点的地址(next)。
第11-13行演示使用while循环语句输出链表信息的方法。
【例21.3】结构数组和指针赋值示例。
#include <stdio.h> #include <string.h> void disp (STUDNT ); typedef struct student { char name[10] ; int studnem ; }STUDNT ; int main () { STUDNT b[3] ,c[3]={\"we\"} ,*pc1 ,*pc3 ,*pc3=c ; STUDNT a[3]={{\"Wangping0\" ,1123} , {\"WangPing1\" ,1124} ,{\"Wangping2\" ,1125}} ; pc1=a ; pc2=b ; pc2=pc1 ; // 演示 pc2=pc1 ; disp (pc2 ); // 第1 组输出 *pc3=*pc1 ; // 演示 *pc3=*pc1 ; disp (pc3 ); // 第2 组输出 * (pc3+1 )=a[2] ; // 演示数组元素整体赋值方法 * (pc3+2 )=* (pc3 ); disp (pc3 ); // 第3 组输出 pc3[0]=a[2] ; pc3[1]=pc2[0] ; pc3[2]=a[1] ; disp (pc3 ); // 第4 组输出 strcpy (pc3->name ,a[0].name ); pc3->studnem=a[0].studnem ; strcpy ((pc3+1 )->name ,a[1].name ); (pc3+1 )->studnem=a[1].studnem ; strcpy ((pc3+2 )->name ,a[2].name ); (pc3+2 )->studnem=a[2].studnem ; disp (pc3 ); // 第5 组输出 return 0 ; } void disp (STUDNT a ) { int i ; char st[8]={\" 姓名:\" ,\" 学号:\"} ; for (i=0 ;i<3 ;i++ ) printf (\"%s%s %s%dn\" ,st[0] ,a[i].name ,st[1] ,a[i].studnem ); }
运行结果如下。
姓名:Wangping0 学号:1123 // 第1 组输出 姓名:WangPing1 学号:1124 姓名:Wangping2 学号:1125 姓名:Wangping0 学号:1123 // 第2 组输出 姓名: 学号:0 姓名: 学号:0 姓名:Wangping0 学号:1123 // 第3 组输出 姓名:Wangping2 学号:1125 姓名:Wangping0 学号:1123 姓名:Wangping2 学号:1125 // 第4 组输出 姓名:Wangping0 学号:1123 姓名:WangPing1 学号:1124 姓名:Wangping0 学号:1123 // 第5 组输出 姓名:WangPing1 学号:1124 姓名:Wangping2 学号:1125
第1组演示“pc2=pc1;”,它们是等效的。
第2组演示“*pc3=*pc1;”,只是将数组的第1个元素整体赋值。其他元素均为0值。
第3组演示使用相同方式给其他元素整体赋值,而第4组则使用指针下标整体赋值。
第5组则使用指针地址偏移值寻找对数组元素具体的域名进行定位。因为name是字符串,所以需要调用库函数strcpy赋值。
由这三个典型的例子可见,需要根据实际情况选择最佳方案。在不需要改变地址的情况下,应尽量使用偏移量定位。在需要移动地址时,如链表,要注意它移动的位置以及是否需要恢复原来的指向(如例21.2的while语句)。对于动态内存,不用时也需及时释放。
其实,这种赋值方式就是链表的循环赋值基础。