结构与数组的关系有两重:其一是在结构中使用数组类型作为结构的一个成员;其二是用结构类型作为数组元素的基类型构成数组。前者在前面的例题中已多次见到;后者是本节要讨论的内容。 结构数组是一个数组,其数组中的每一个基本元素都是结构类型。说明结构数组的方法是:先定义一个结构,然后用结构类型说明一个数组变量。 例如:为记录100个人的基本情况。可以说明一个有100个元素的数组。每个元素的基类型为一个结构,在说明数组时可以写成: struct person man[100]; man就是有100个元素的结构数组,数组的每个元素为person型结构。 要访问结构数组中的具体结构,必须遵守数组使用的规定,按数组名及其下标进行访问,要访问结构数组中某个具体结构下的成员,又要遵守有关访问结构成员的规定,使用"."访问运算符和成员名。访问结构数组成员的一般格式是: 结构数组名[下标].成员名 同一般的数组一样,结构数组中每个元素的起始下标从0开始,数组名称表示该结构数组的存储首地址。结构数组存放在一连续的内存区域中,它所占内存数目为结构类型的大小乘以数组元素的个数。结构数组man在内存中的存储如图11-3所示:
|
例如,我们要将数组man中的3号元素赋值为:"Fangjin",'M',1963,9,13,就可以使用下列语句: strcpy(man[3].name, "Fangjin"); man[3].sex='M'; man[3].birthday.year=1963; man[3].birthday.month=9; man[3].birthday.day=13; /* 为结构数组中一个元素的各个成员赋值 */ 为了将"Fangjin"改为"Fangjun",修改其中的字母'i',可以使用下列语句,为结构数组中一个元素的数组成员中的一个字符赋值。 man[3].name[5]='u'; 利用有3个元素的结构数组,可以很方便地改写例11-3.C,请读者自己编写。
例11-4:分析运行结果。 struct s { int x; int *y; /* y: 结构中的成员是指向整型的指针 */ } ; int data[5]={ 10, 20, 30, 40, 50 }; /* data: 整型数组 */ struct s array[5] = { 100, &data[0], 200, &data[1], 300, &data[2], 400, &data[3], 500, &data[4] }; /* array: 结构数组,初始化 */ #include "stdio.h" main ( ) { int i=0; /* 说明变量i并赋初值 */ struct s s_var; /* s_ver: 一般的结构变量 */ s_var=array[0]; /* 将结构数组的array[0]整体赋给s_var */ printf ("%d/n", s_var.x); /* 按照结构变量的方式引用结构的成员 */ printf ("%d/n", *s_var.y); printf ("For array:/n"); /* 以下是按结构数组元素方式引用结构成员 */ printf ("%d/n", array[i].x); printf ("%d/n", *array[i].y); printf ("%d/n", ++array[i].x); printf ("%d/n", ++ *array[i].y); printf ("%d/n", array[++i].x); printf ("%d/n", *++array[i].y); printf ("%d/n", (*array[i].y)++); printf ("%d/n", *(array[i].y++)); printf ("%d/n", *array[i].y++); printf ("%d/n", * array[i].y); } 程序中说明了一个结构数组array,结构数组array的每个元素有两个成员,其一为整型x,其二为指向整型的指针y。结构数组array的初始化后的状态如图11-4所示。
|
程序各个printf语句中对数组操作的含义如下: s_var.x /* 取s_var的成员x的值,输出 100 */ *s_var.y /* 取s_var的成员指针y所指的内容,输出 10 */ array[i].x /* 取array[i]的x的值,输出 100 */ *array[i].y /* 取array[i]的指针y所指的内容,输出10 */ ++array[i].x /* 取array[i]的x的值,x加1后输出 101 */ ++*array[i].y /* 取array[i]的指针y所指的内容,y的内容加1后输出11 */ array[++i].x /* i先加1后取array[i]的x的值,输出 200 */ *++array[i].y /* 将array[i]的指针y先加1后再取y所指的内容,输出30 */ (*array[i].y)++ /* 取array[i]的指针y的内容,输出30后,y的内容再加1 */ *(array[i].y++) /* 取array[i]的指针y的内容,输出31后,指针y再加1 */ *array[i].y++ /* 同上,由于运算的结合性隐含了括号,输出 40 */ *array[i].y /* 输出 50 */ 程序运行结束时,结构数组array的状态如图11-5所示。
例11-5:简单的密码加密程序。其加密过程是先定义一张字母加密对照表。 将需要加密的一行文字输入加密程序,程序根据加密表中的对应关系,可以很简单地将输入的文字加密输出,对于表中未出现的字符则不加密。 可以定义一个结构来表示加密表。结构table中的成员input存入输入的字符,成员output保存加密后对应的字符。
|
#include "stdio.h" struct table /* 定义结构table */ { char input; /* 成员input存输入的字符 */ char output; /* 成员output存输出的字符 }; struct table translate[ ]= /* 说明外部的结构数组translate并初始化 */ { 'a', 'd', 'b', 'w', 'c', 'k', 'd', '; ', 'e', 'i', 'i', 'a', 'k', 'b', ';', 'c', 'w', 'e' }; /* 建立加密对照表 */ main( ) { char ch; int str_long, i; str_long = sizeof(translate)/sizeof(struct table); /* 计算数组元素个数 */ while ( (ch=getchar( )) != '/n') { for ( i=0; translate[i].input!=ch && i<str_long; i++) ; if (i<str_long) putchar(translate[i].output); /* 对表中的字符加密输出 */ else putchar (ch); /* 对其它字符原样输出 */ } }
语句"struct table translate[]={...}"有三个作用,一是说明了一个外部的结构数组translate;二是表示数组的大小由后面给出的初始化数据决定,三是对结构数组进行初始化。在程序中给出了数组初始化数据,所以结构数组translate有9个元素。 程序中语句"str_long = sizeof(translate)/sizeof(struct table);"是用sizeof运算计算结构数组translate中元素的数目。sizeof(translate)求出数组translate所占用的字节总数,sizeof(struct table)求出数组中每个元素所占用的字节总数。 运行程序时,从键盘逐个读取输入的字符存入变量ch中,将ch的值与结构translate中的input比较,如果是要加密的字符,则输入加密后的字符output,否则ch原样输出。
例11-6:有N个小孩围成一圈并依次编号,教师指定从第M个小孩开始报数,当报到第S个小孩时,即令其出列,然后再从下一个孩子起从1开始继续报数,数到第s个小孩又令其出列,这样直到所有的孩子都依次出列。求小孩出列的顺序。这就是约瑟夫问题。 由于问题中的小孩围成一个圈,因而启发我们用一个环形链来表示。我们用结构数组构成一个环形链。结构名为child。数组名为link。nextp的含义是排在当前这个孩子后面的下一个孩子的序号。由nextp可构成一个环型链,no是孩子的序号。这样就可以从第M个小孩开始沿着nextp连成的闭合链不断计数S次,输出对应的no表示让他出列。
#include "stdio.h" struct child /* 定义结构child */ { int nextp; /* 排在后面下一个位置上的孩子的序号 */ int no; /* 孩子的序号 */ } link [100]; /* 说明结构数组link */ main( ) { int i, n, s, y, k, m, count=0; /* count为输出计数器 */ printf ("/nTell me how many children are there ?"); scanf ("%3d", &n); printf ("/nFrom which to count ?"); scanf ("%3d", &m); printf ("/nHow many shall I count ?"); scanf ("%3d", &s); for ( i=1; i<=n; i++ ) /* 根据孩子总数n建立一个环 */ { if ( i==n ) link[i].nextp=1; /* 若是最后一个,则他的下一个是第一个人 */ else link[i].nextp=i+1; /* 否则,第i个人的下一个为第i+1个人 */ link[i].no=i; /* 为第i个孩子建立序号 */ } printf ("/nStand out :/n"); if ( m>=i ) k=n; else k=m-1; /* k定位在应开始计数的孩子的前一个人上 */ while (count != n) { for (i=0; i!=s; ) /* s个孩子报数 */ { k = link[k].nextp; /* 取下一个孩子 */ if ( link[k].no != 0 ) ++i; /* 若序号不为0表示没出列,则计数 */ } printf ("%7d", link[k].no); /* 输出出列的孩子序号 */ link[k].no = 0; /* 将出列孩子的序号清为0 */ if ( ++count % 10 == 0) printf ("/n"); } }
程序中首先输入小孩总数n,报数起始位置m和需要出列的计数步长s,然后由总数n初始化结构数组link。为了程序中处理的方便,link数组从下标1开始使用,下标为0的元素不用,由于数组说明长度为100,所以输入的n值不能大于99。 变量k记录下一次需要计数的数组下标,初始化时,变量k的值在要开始报数的小孩的前一个位置。在while循环中,使用count作为出列小孩计数器,对于已出列的小孩,将no清为0,变量i是报数计数器,当link[k].no不为0时,才操作++i。语句"k=link[k].nextp;"的含义是将下一个孩子所在数组中的下标位置送入变量k中。 运行程序,输入n=35、m=5、s=3,可得到如下结果: 7 10 13 16 19 22 25 28 31 34 2 5 9 14 18 23 27 32 1 6 12 20 26 33 4 15 24 35 11 29 8 30 21 3 17 本例完全可以用一维数组实现,请读者自己编写程序。
|