1
五、结构的初始化六、结构与函数七、结构的组合
2
五、结构的初始化集合数据类型包括数组、结构、类和联合。对于严格意义上集合类型加上了 4点限制:
a,只允许存在公共成员 ;
b,没有虚函数 ;
c,没有基类 ;
d,没有明显提交构造函数 ;
3
1,结构变量的初始化结构变量的初始化是对结构成员的初始赋值过程,结构作为严格意义上的集合类型,允许在定义变量的时候同时进行初始赋值,结构变量的初始化规则的形式为:
struct 结构名 结构变量名 ={初始数据列表 };
如果结构包含集合类型的成员,可以递归采用初始化规则。
4
如声明空间点的结构 Point_3D并且同时定义一个结构变量 u为:
struct Point_3D
{double x,y,z; } u= {1,2,3};
这效果上在局部范围等价于:
struct Point_3D { double x,y,z; } ;
struct Point_3D u;
u.x=1; u.y=2; u.z=3;
5
2,结构数组的初始化用 a表示结构数组名,member表示结构中的成员名,i
表示下标。结构数组的每一个元素 a[i]是结构变量。结构数组在定义的时候可进行初始化或部分地初始化,其格式为:
struct 结构名 结构数组名 [N]=
{结构数据 1,{结构数据 2},..,{结构数据 n}};
在对结构数组进行初始化时,方括号中的元素个数 N可以事先指定也可缺省。缺省时编译器根据其后的初始数据列表中的项数静态确定元素个数。
结构数组的初始化通常是用花括号包括的结构型的常数,
这些花括号在语法上不是必须的,访问结构数组元素的成员的格式为:
结构数组名 [下标 ].成员名
a[ I ].member 或 (a+i)- >member
6
例如,空间点的结构声明和结构数组 a[3]的定义如下:
typedef struct
{ double v[3]; } point_t;
point_t a[ ]= {1,2,3,{4,5,6}};
相当于结构数组 a[2]具有状态:
a[0].v[0]=1; a[0].v[1]=2; a[0].v[2]=3;
a[1].v[0]=4; a[1].v[1]=5; a[1].v[2]=6;
类似地对于结构名为 S的结构声明和结构数组 a[3]的定义:
struct S { char c; int k; } ;
S a[3]= {'a',1,{'b',2},'c',3};
相当于,S a[3]; a[0].c='a'; a[0].k=1;
a[1].c='b'; a[1].k=2; a[2].c='c'; a[2].k=3;
7
[例 ] 字符指针成员和字符数组成员
# include<stdio.h>
typedef struct data_s { int n; char *s; } SData;
typedef struct data_a { int n; char a[12]; } AData;
void main (void)
{ SData d[ ]={1,"For",2,"Before",3,"And",4,"Anyone"};
AData c[ ]= {5,"for",6,"before",7,"and",8,"anyone"};
const int n=sizeof (d)/sizeof (d[0]);
d[1].s =c[3].a;
d[1].s[1] ='N';
SData*p =d;
int k ; for (k=0; k<n; k++,p++)
printf ("{%p,%s} ",d [k].s,p->s);
printf ("\n");
8
for ( k=0; k<n; k++)
printf ( "{%p,%s } ",(*(c+k)).a,(c+k)->a);
printf ("\n");
AData *q=c;
for (p=d,k=0; k<n; k++,p++,q++)
printf ("[%d,%d] ",p->n,q->n) ;
}
//输出结果,
{0042405C,For}{0065FDCC,aNyone}
{00424044,And}{00424068,Anyone}
{0065FD9C,for} {0065FDAC,before}
{0065FDBC,and} {0065FDCC,aNyone}
[1,5] [2,6] [3,7] [4,8]
9
AData结构含有字符数组成员 a,这个成员 a的首地址相对于结构变量是相对不变的,SData结构含有字符指针成员
s,字符指针 s可以指向不同的字符内存区。
SData的局部结构数组 d和 AData的局部结构数组 c采取类似的初始化格式,但其含义是不同的:
SData结构的字符指针 s初始化指向全局字符串占有的只读内存区,AData结构中的字符数组 a是字符串初始化的截断赋值,字符数组 a的内存区可以被更新。
10
[例 ]求数组和的函数 double sum(double*,int )求结构成员数组的和
# include<stdio.h>
typedef struct SType { int v; double a[6]; } S,*PS;
double sum (double* q,int n) { double s=0;
for (double*p=q; p<q+n;p++) s+=*p; return s; }
void main()
{ S a [2]={4,{1,2,3,4},5,{1,2,3,4,5 }
};
for (PS s=a; s<a+2; s++)
printf ("%2.0f ",sum (s->a,s->v));
//输出结果,10 15
}
11
六、结构与函数
1,结构的数值形参和引用形参首先声明全局结构类型,然后在结构声明之后的全局范围定义操作结构的函数。
C++函数的调用约定有两种:
一是数值传递,二是引用传递数值传递是将结构变量的数据状态拷贝到形参所需要的堆栈空间。若结构变量具有 n=sizeof(结构名 )个字节,则对应的结构形参所消耗的堆栈空间的大小就是 n个字节。
数值传递的好处是在函数体中对结构数值形参的操作不影响对应的实参结构变量。
12
引用传递是将结构实参变量名的地址压入堆栈,在函数体中对结构引用形参的操作就是对实参变量的直接操作,引用形参同步地改变相应实参结构变量的值,在函数体中采用的是变量名即 r.v形式的语法,这是 C++新引进的函数调用方式。
在 C语言中与结构引用形参完成相同工作的是结构指针数值形参,结构指针作为数值形参时其指针的内容就是另一个结构变量的地址。
13
系统将该地址值送入相应的堆栈空间,这个地址值在函数体中既可作为指针形参的初始值用于地址计算的起步条件,也可以直接作为凝固的地址用于访问既定的内存空间,
在函数体中采用结构指针即 p->v的语法格式,达到改变指针形参所指向的实参结构变量的作用。
r.v和 p->v在结构变量引用形参和指针数值形参固定寻址时是等价的间接寻址方式,这两种外在格式不同的语法形式可以完成被调函数对主控函数相应结构变量的改变。
14
[例 ]数值形参 a,引用形参 r和指针形参 p对实参结构变量的影响。
# include<stdio.h>
typedef struct Sa { int x; } A;
void f (A a,A& r,A* p) { a.x += 1; r.x += 2; p->x += 3; }
void main ()
{ A a[3]= {1,2,3};
//结构数组的初始赋值
f (a[0],a[1],a+2);
printf ("%d,%d,%d",a[0].x,a[1].x,a[2].x);
}
//输出结果,1,4,6
15
2,各种数据传递方式实现点积和叉积算例
[例 ] 数值形参 v和引用形参 u求点积的函数 double
DotProduct( PT& u,PT v),
# include <stdio.h>
# include <stdio.h>
typedef struct point_t { double x,y,z;} PT;
double DotProduct ( PT& u,PT v)
{ return u.x*v.x+u.y*v.y+u.z*v.z;}
double Dot (double p[3],double *q)
{return p[0]*q[0]+p[1]*q[1]+p[2]*q[2]; }
16
void main (void)
{ PT x= {1,1,0},y={0,2,1},z= {1,0,3};
PT a;
a.y=Dot (&y.x,&z.x);
a.x=DotProduct (x,y);
a.z=DotProduct (z,x);
printf ("a={%1.0f,%1.0f,%1.0f } \t",a.x,a.y,a.z) ;
}
//输出,a={2,3,1}
17
[例 ] 只读指针形参 double DotProduct (const
point_t *,const point_t *)求点积
# include <stdio.h>
typedef struct { double v[3]; } point_t;
double DotProduct (const point_t *p,const point_t *q)
{ return p->v[0]*q->v[0]+p->v[1]*q->v[1]+p->
v[2]*q->v[2];
}
double Dot (double *p,double q[3])
{
return p[0]*q[0]+p[1]*q[1]+p[2]*q[2];
}
18
void main (void)
{ point_t x={1,1,0},y={0,2,1},z={1,0,3};
point_t a= { DotProduct (&x,&y),
DotProduct (&y,&z),Dot (z.v,x.v)};
printf ("a= {%1.0f,%1.0f,%1.0f}\n",
a.v[0],a.v[1],a.v[2]) ;
}
//输出,a={2,3,1}
19
3,结构数组的排序算例前面的章节中对于字符指针数组和变量数组进行了排序处理,结构的好处是可以将指针成员和变量成员组合在一起,构成字符指针和变量并存的格局。
程序设计中常需要根据不同的准则进行不同的排序。排序的核心思路并未发生变换,此处的排序算法是将前面章节的算法进行相应的文本替换得来的,请比较它们的异同。
SelectSort函数是按字符串大小排序 ;两个 BubbleSort
函数是按整型变量排序,其中之一是对结构数组元素直接交换,另一个交换结构指针,通过结构指针数组间接操作结构数组的相关元素。交换结构变量系统要隐含地调用 memcpy
函数,交换结构指针的代价相当于交换 int型变量。
交换结构变量比交换指针的代价高。因此对结构指针数组排序的效率高于对结构数组排序算法的效率
20
[例 ] 对结构不同成员的排序
# include<stdio.h>
#include<string.h
typedef struct data_tag { int n; char *s;} SData;
void SelectSort(SData* a,int n)
{ for(int i=0;i<n-1;i++)
{ int min=i;
for(int j=i+1;j<n;j++)
if (strcmp(a[j].s,a[min].s)<0) min=j;
if (min!=i) { SData t=a[i];a[i]=a[min];a[min]=t; }
}
}
21
void BubbleSort (SData a[],int n)
{ for (int i=1; i<n; i++)
for (int j=0;j<n-i;j++)
if (a[j].n>a[j+1].n)
{ SData t=a[j]; a[j]=a[j+1]; a[j+1]=t; }
}
void BubbleSort (SData *a[ ],int n)
{ for (int i=1; i<n; i++)
for (int j=0; j<n-i; j++)
if (a[j]->n > a[j+1]->n)
{SData *t=a[j]; a[j]=a[j+1]; a[j+1]=t; }
}
22
void main (void)
{ char * const
ca[ ]={"For","Before","FOR","And","anyone"};
SData d[ ]={6,ca[0],4,ca[1],7,ca[2],5,ca[3],8,ca[4]};
const int n=sizeof (d)/sizeof (d[0]); int k;
for (k=0;k<n;k++)
printf ("{%d,%s} ",d[k].n,d[k].s);
printf ("\n1");
BubbleSort (d,n);
SData* p=d;
for (k=0; k<n;k++,p++)
printf ("{%d,%s} ",p->n,p->s);
23
printf ("\n2");SelectSort(d,n);
for( k=0; k<n; k++)
printf ("{%d,%s} ",d[k].n,(d+k)->s);
SData* s[n];
for( k=0; k<n; k++)
s[k]=d+k;
printf ("\n3");
BubbleSort(s,n);
SData** pp=s;p=*pp;
for( k=0; k<n; k++,pp++,p=*pp)
printf ("{%d,%s} ",p->n,s[k]->s);
}
24
//程序运行输出结果如下,
{6,For} {4,Before} {7,FOR} {5,And} {8,anyone}
1{4,Before} {5,And} {6,For} {7,FOR} {8,anyone}
2{5,And} {4,Before} {7,FOR} {6,For} {8,anyone}
3{4,Before} {5,And} {6,For} {7,FOR} {8,anyone}
25
七、结构的组合结构中可以嵌入另一个结构变量或联合变量作为成员,
这样的现象称为组合。嵌入的集合成员称为嵌入对象或嵌入结构变量。下面 B结构嵌入一个 A类型的结构变量,这样 B类型的结构变量访问 A类型的成员需要一层一层的进行。
typedef struct a_t { int x ; } A;
typedef struct b_t { int n; A a; } B;
下面的表达式是合法的寻址计算次序,
A a; A *pa=&a; a.x; pa->x;
B b; B *pb=&b; b.a.x; pb->a.x;
pa=&b.a; pa->x;
26
C/C++语言对与结构类型的嵌入层次没有限制,组合结构分层访问的一般形式为:
结构变量名,嵌入结构变量名,内层成员名 b.a.x;
圆点运算符具有左结合性,运算总是自左向右逐层访问各层的成员。对于结构数组的嵌入,数组下标表达式具有更强的结合性。
[例 ] 指针形参输出成员数据
#include<stdio.h>
typedef struct a_t { int x ; } A;
typedef struct b_t { int m; A a; } B;
typedef struct c_t { int n; B b; } C;
27
void show (const C* pc)
{ printf ("c={%d,%d,%d}\t",pc->n,pc->b.m,pc->b.a.x);
}
void main (void)
{ A a={1}; B b={3,4};
C c={6,7,8};
C *pc=&c;
pc->b = b; show (pc);
pc->b.a=a; show (&c);
}
//输出,c={6,3,4} c={6,3,1}
28
pc- >b=b表示将 b结构变量拷贝给 c结构变量中嵌入的结构变量。 pc->b.a= a表示将集合 a赋值给相应的 c结构变量中子集合 b的子集合 a。
数组名和结构名都代表集合数据的首地址,但数组名是右值而结构变量则是左值。
对于数组定义 { double v[3],u[3]; },{ u=v;}是非法的。
结构变量之间可以相互赋值,这种规则延拓到嵌入的结构变量:
a= b.a;
29
下面的 B结构嵌入一个结构 A的数组 a[8],int型二维
d[3][4]。
typedef struct { double x ; } A;
typedef struct { int n; A a[8];
int d[3][4]; const int* p;} B;
B x,y; x=y;
x=y引起集合型数据的整体赋值,可以存在嵌入对象的相互赋值:
x.a[0]=y.a[1];
但不能写成,x.a=y.a;
这导致右值数组名的赋值。一般地成员声明语句中引入的名称在原先定义语句所在的环境为右值,则放置到结构声明中保持右值特性不变。例如对于数组定义语句 [int d[3][4];]
d,d[k]是右值,则 x.d,x.d[k] 也是右值。同样地 *x.p是右值。
30
五、结构的初始化六、结构与函数七、结构的组合
2
五、结构的初始化集合数据类型包括数组、结构、类和联合。对于严格意义上集合类型加上了 4点限制:
a,只允许存在公共成员 ;
b,没有虚函数 ;
c,没有基类 ;
d,没有明显提交构造函数 ;
3
1,结构变量的初始化结构变量的初始化是对结构成员的初始赋值过程,结构作为严格意义上的集合类型,允许在定义变量的时候同时进行初始赋值,结构变量的初始化规则的形式为:
struct 结构名 结构变量名 ={初始数据列表 };
如果结构包含集合类型的成员,可以递归采用初始化规则。
4
如声明空间点的结构 Point_3D并且同时定义一个结构变量 u为:
struct Point_3D
{double x,y,z; } u= {1,2,3};
这效果上在局部范围等价于:
struct Point_3D { double x,y,z; } ;
struct Point_3D u;
u.x=1; u.y=2; u.z=3;
5
2,结构数组的初始化用 a表示结构数组名,member表示结构中的成员名,i
表示下标。结构数组的每一个元素 a[i]是结构变量。结构数组在定义的时候可进行初始化或部分地初始化,其格式为:
struct 结构名 结构数组名 [N]=
{结构数据 1,{结构数据 2},..,{结构数据 n}};
在对结构数组进行初始化时,方括号中的元素个数 N可以事先指定也可缺省。缺省时编译器根据其后的初始数据列表中的项数静态确定元素个数。
结构数组的初始化通常是用花括号包括的结构型的常数,
这些花括号在语法上不是必须的,访问结构数组元素的成员的格式为:
结构数组名 [下标 ].成员名
a[ I ].member 或 (a+i)- >member
6
例如,空间点的结构声明和结构数组 a[3]的定义如下:
typedef struct
{ double v[3]; } point_t;
point_t a[ ]= {1,2,3,{4,5,6}};
相当于结构数组 a[2]具有状态:
a[0].v[0]=1; a[0].v[1]=2; a[0].v[2]=3;
a[1].v[0]=4; a[1].v[1]=5; a[1].v[2]=6;
类似地对于结构名为 S的结构声明和结构数组 a[3]的定义:
struct S { char c; int k; } ;
S a[3]= {'a',1,{'b',2},'c',3};
相当于,S a[3]; a[0].c='a'; a[0].k=1;
a[1].c='b'; a[1].k=2; a[2].c='c'; a[2].k=3;
7
[例 ] 字符指针成员和字符数组成员
# include<stdio.h>
typedef struct data_s { int n; char *s; } SData;
typedef struct data_a { int n; char a[12]; } AData;
void main (void)
{ SData d[ ]={1,"For",2,"Before",3,"And",4,"Anyone"};
AData c[ ]= {5,"for",6,"before",7,"and",8,"anyone"};
const int n=sizeof (d)/sizeof (d[0]);
d[1].s =c[3].a;
d[1].s[1] ='N';
SData*p =d;
int k ; for (k=0; k<n; k++,p++)
printf ("{%p,%s} ",d [k].s,p->s);
printf ("\n");
8
for ( k=0; k<n; k++)
printf ( "{%p,%s } ",(*(c+k)).a,(c+k)->a);
printf ("\n");
AData *q=c;
for (p=d,k=0; k<n; k++,p++,q++)
printf ("[%d,%d] ",p->n,q->n) ;
}
//输出结果,
{0042405C,For}{0065FDCC,aNyone}
{00424044,And}{00424068,Anyone}
{0065FD9C,for} {0065FDAC,before}
{0065FDBC,and} {0065FDCC,aNyone}
[1,5] [2,6] [3,7] [4,8]
9
AData结构含有字符数组成员 a,这个成员 a的首地址相对于结构变量是相对不变的,SData结构含有字符指针成员
s,字符指针 s可以指向不同的字符内存区。
SData的局部结构数组 d和 AData的局部结构数组 c采取类似的初始化格式,但其含义是不同的:
SData结构的字符指针 s初始化指向全局字符串占有的只读内存区,AData结构中的字符数组 a是字符串初始化的截断赋值,字符数组 a的内存区可以被更新。
10
[例 ]求数组和的函数 double sum(double*,int )求结构成员数组的和
# include<stdio.h>
typedef struct SType { int v; double a[6]; } S,*PS;
double sum (double* q,int n) { double s=0;
for (double*p=q; p<q+n;p++) s+=*p; return s; }
void main()
{ S a [2]={4,{1,2,3,4},5,{1,2,3,4,5 }
};
for (PS s=a; s<a+2; s++)
printf ("%2.0f ",sum (s->a,s->v));
//输出结果,10 15
}
11
六、结构与函数
1,结构的数值形参和引用形参首先声明全局结构类型,然后在结构声明之后的全局范围定义操作结构的函数。
C++函数的调用约定有两种:
一是数值传递,二是引用传递数值传递是将结构变量的数据状态拷贝到形参所需要的堆栈空间。若结构变量具有 n=sizeof(结构名 )个字节,则对应的结构形参所消耗的堆栈空间的大小就是 n个字节。
数值传递的好处是在函数体中对结构数值形参的操作不影响对应的实参结构变量。
12
引用传递是将结构实参变量名的地址压入堆栈,在函数体中对结构引用形参的操作就是对实参变量的直接操作,引用形参同步地改变相应实参结构变量的值,在函数体中采用的是变量名即 r.v形式的语法,这是 C++新引进的函数调用方式。
在 C语言中与结构引用形参完成相同工作的是结构指针数值形参,结构指针作为数值形参时其指针的内容就是另一个结构变量的地址。
13
系统将该地址值送入相应的堆栈空间,这个地址值在函数体中既可作为指针形参的初始值用于地址计算的起步条件,也可以直接作为凝固的地址用于访问既定的内存空间,
在函数体中采用结构指针即 p->v的语法格式,达到改变指针形参所指向的实参结构变量的作用。
r.v和 p->v在结构变量引用形参和指针数值形参固定寻址时是等价的间接寻址方式,这两种外在格式不同的语法形式可以完成被调函数对主控函数相应结构变量的改变。
14
[例 ]数值形参 a,引用形参 r和指针形参 p对实参结构变量的影响。
# include<stdio.h>
typedef struct Sa { int x; } A;
void f (A a,A& r,A* p) { a.x += 1; r.x += 2; p->x += 3; }
void main ()
{ A a[3]= {1,2,3};
//结构数组的初始赋值
f (a[0],a[1],a+2);
printf ("%d,%d,%d",a[0].x,a[1].x,a[2].x);
}
//输出结果,1,4,6
15
2,各种数据传递方式实现点积和叉积算例
[例 ] 数值形参 v和引用形参 u求点积的函数 double
DotProduct( PT& u,PT v),
# include <stdio.h>
# include <stdio.h>
typedef struct point_t { double x,y,z;} PT;
double DotProduct ( PT& u,PT v)
{ return u.x*v.x+u.y*v.y+u.z*v.z;}
double Dot (double p[3],double *q)
{return p[0]*q[0]+p[1]*q[1]+p[2]*q[2]; }
16
void main (void)
{ PT x= {1,1,0},y={0,2,1},z= {1,0,3};
PT a;
a.y=Dot (&y.x,&z.x);
a.x=DotProduct (x,y);
a.z=DotProduct (z,x);
printf ("a={%1.0f,%1.0f,%1.0f } \t",a.x,a.y,a.z) ;
}
//输出,a={2,3,1}
17
[例 ] 只读指针形参 double DotProduct (const
point_t *,const point_t *)求点积
# include <stdio.h>
typedef struct { double v[3]; } point_t;
double DotProduct (const point_t *p,const point_t *q)
{ return p->v[0]*q->v[0]+p->v[1]*q->v[1]+p->
v[2]*q->v[2];
}
double Dot (double *p,double q[3])
{
return p[0]*q[0]+p[1]*q[1]+p[2]*q[2];
}
18
void main (void)
{ point_t x={1,1,0},y={0,2,1},z={1,0,3};
point_t a= { DotProduct (&x,&y),
DotProduct (&y,&z),Dot (z.v,x.v)};
printf ("a= {%1.0f,%1.0f,%1.0f}\n",
a.v[0],a.v[1],a.v[2]) ;
}
//输出,a={2,3,1}
19
3,结构数组的排序算例前面的章节中对于字符指针数组和变量数组进行了排序处理,结构的好处是可以将指针成员和变量成员组合在一起,构成字符指针和变量并存的格局。
程序设计中常需要根据不同的准则进行不同的排序。排序的核心思路并未发生变换,此处的排序算法是将前面章节的算法进行相应的文本替换得来的,请比较它们的异同。
SelectSort函数是按字符串大小排序 ;两个 BubbleSort
函数是按整型变量排序,其中之一是对结构数组元素直接交换,另一个交换结构指针,通过结构指针数组间接操作结构数组的相关元素。交换结构变量系统要隐含地调用 memcpy
函数,交换结构指针的代价相当于交换 int型变量。
交换结构变量比交换指针的代价高。因此对结构指针数组排序的效率高于对结构数组排序算法的效率
20
[例 ] 对结构不同成员的排序
# include<stdio.h>
#include<string.h
typedef struct data_tag { int n; char *s;} SData;
void SelectSort(SData* a,int n)
{ for(int i=0;i<n-1;i++)
{ int min=i;
for(int j=i+1;j<n;j++)
if (strcmp(a[j].s,a[min].s)<0) min=j;
if (min!=i) { SData t=a[i];a[i]=a[min];a[min]=t; }
}
}
21
void BubbleSort (SData a[],int n)
{ for (int i=1; i<n; i++)
for (int j=0;j<n-i;j++)
if (a[j].n>a[j+1].n)
{ SData t=a[j]; a[j]=a[j+1]; a[j+1]=t; }
}
void BubbleSort (SData *a[ ],int n)
{ for (int i=1; i<n; i++)
for (int j=0; j<n-i; j++)
if (a[j]->n > a[j+1]->n)
{SData *t=a[j]; a[j]=a[j+1]; a[j+1]=t; }
}
22
void main (void)
{ char * const
ca[ ]={"For","Before","FOR","And","anyone"};
SData d[ ]={6,ca[0],4,ca[1],7,ca[2],5,ca[3],8,ca[4]};
const int n=sizeof (d)/sizeof (d[0]); int k;
for (k=0;k<n;k++)
printf ("{%d,%s} ",d[k].n,d[k].s);
printf ("\n1");
BubbleSort (d,n);
SData* p=d;
for (k=0; k<n;k++,p++)
printf ("{%d,%s} ",p->n,p->s);
23
printf ("\n2");SelectSort(d,n);
for( k=0; k<n; k++)
printf ("{%d,%s} ",d[k].n,(d+k)->s);
SData* s[n];
for( k=0; k<n; k++)
s[k]=d+k;
printf ("\n3");
BubbleSort(s,n);
SData** pp=s;p=*pp;
for( k=0; k<n; k++,pp++,p=*pp)
printf ("{%d,%s} ",p->n,s[k]->s);
}
24
//程序运行输出结果如下,
{6,For} {4,Before} {7,FOR} {5,And} {8,anyone}
1{4,Before} {5,And} {6,For} {7,FOR} {8,anyone}
2{5,And} {4,Before} {7,FOR} {6,For} {8,anyone}
3{4,Before} {5,And} {6,For} {7,FOR} {8,anyone}
25
七、结构的组合结构中可以嵌入另一个结构变量或联合变量作为成员,
这样的现象称为组合。嵌入的集合成员称为嵌入对象或嵌入结构变量。下面 B结构嵌入一个 A类型的结构变量,这样 B类型的结构变量访问 A类型的成员需要一层一层的进行。
typedef struct a_t { int x ; } A;
typedef struct b_t { int n; A a; } B;
下面的表达式是合法的寻址计算次序,
A a; A *pa=&a; a.x; pa->x;
B b; B *pb=&b; b.a.x; pb->a.x;
pa=&b.a; pa->x;
26
C/C++语言对与结构类型的嵌入层次没有限制,组合结构分层访问的一般形式为:
结构变量名,嵌入结构变量名,内层成员名 b.a.x;
圆点运算符具有左结合性,运算总是自左向右逐层访问各层的成员。对于结构数组的嵌入,数组下标表达式具有更强的结合性。
[例 ] 指针形参输出成员数据
#include<stdio.h>
typedef struct a_t { int x ; } A;
typedef struct b_t { int m; A a; } B;
typedef struct c_t { int n; B b; } C;
27
void show (const C* pc)
{ printf ("c={%d,%d,%d}\t",pc->n,pc->b.m,pc->b.a.x);
}
void main (void)
{ A a={1}; B b={3,4};
C c={6,7,8};
C *pc=&c;
pc->b = b; show (pc);
pc->b.a=a; show (&c);
}
//输出,c={6,3,4} c={6,3,1}
28
pc- >b=b表示将 b结构变量拷贝给 c结构变量中嵌入的结构变量。 pc->b.a= a表示将集合 a赋值给相应的 c结构变量中子集合 b的子集合 a。
数组名和结构名都代表集合数据的首地址,但数组名是右值而结构变量则是左值。
对于数组定义 { double v[3],u[3]; },{ u=v;}是非法的。
结构变量之间可以相互赋值,这种规则延拓到嵌入的结构变量:
a= b.a;
29
下面的 B结构嵌入一个结构 A的数组 a[8],int型二维
d[3][4]。
typedef struct { double x ; } A;
typedef struct { int n; A a[8];
int d[3][4]; const int* p;} B;
B x,y; x=y;
x=y引起集合型数据的整体赋值,可以存在嵌入对象的相互赋值:
x.a[0]=y.a[1];
但不能写成,x.a=y.a;
这导致右值数组名的赋值。一般地成员声明语句中引入的名称在原先定义语句所在的环境为右值,则放置到结构声明中保持右值特性不变。例如对于数组定义语句 [int d[3][4];]
d,d[k]是右值,则 x.d,x.d[k] 也是右值。同样地 *x.p是右值。
30