1
一、结构的声明和结构变量二、结构变量的定义形式三、对结构数据的操作四、结构变量的内存分布
2
一、结构的声明和结构变量由关键字 struct声明的数据描述称为结构类型而 union
或 class声明的数据描述称为联合类型或类类型,分别简称为结构和联合或类。
结构、联合、类是集合数据类型。
int,float等是系统预先取好的可以直接使用的类名,
关键字 struct和 union 或 class后紧跟的标识符则是用户引入的类名,需要事先加以声明。
3
定义直接导致内存的存储单元分配,声明则是一种关于数据类型的描述,本身一般不涉及内存的分配。
结构将密切相关的数据集合在一起。结构的声明具有如下的语法形式:
struct structName 结构 结构名
{ type member; { 数据类型 成员名 ;
type2 member2; 数据类型 2 成员名 2;
...
typen membern; 数据类型 n 成员名 n;
}; };
其中 type,typen等代表已经声明的类型,具体地 (下面左边和右边是等价的 ):
4
struct SType struct SType
{ float member; { float member; int v;
int v; enum {N=6};
double a[6]; double a[N];
}; }
花括号包括的部分称为结构的成员数据列表,成员数据列表具体描述了结构的组成情况,其间每一个数据项采用
[数据类型 成员名 ;]的格式,这种格式类似于变量定义语句但不进行初始化处理,称为成员声明语句。
5
关键字 struct之后引出结构名为 SType的结构声明,
SType是用户建立的类名,类名和成员名遵循标识符的命名规定,结构名可以和系统内置的类名 int,float和 double一样定义变量,通过结构名定义的变量称为结构变量。
结构中声明的变量 member,v,a[6]等是结构的成员。
同一个结构中的成员不能彼此同名,但是不同结构名的结构可以拥有一样的成员名称,不同数量的成员。
其中枚举常数 N可以界定成员数组的维数,它不占结构变量的内存。
6
可以在结构中出现当前结构名,以声明一个指向该结构变量的指针,如 struct A{int n;A* p;};由此形成复杂的链表结构。
但结构的声明中不能用当前结构名声明成员变量即结构中不能包含自身的实例,这称为递归声明。建立结构的声明时不允许直接递归或间接递归。
例如,struct A{int n;A a;};是不可以的。
结构类型的声明可以出现在函数体内也可以位于全局范围。在函数体内声明的结构其有效范围仅限于该函数之内,
而在全局范围声明的结构对于其后的程序段具有可操作性。
在相同作用范围结构名应是唯一的。在函数体内不定义函数,很少在函数体内声明结构。
7
二、结构变量的定义形式结构变量分为全局的、局部的结构变量和静态结构变量。指向结构变量的指针简称结构指针。结构变量或结构指针的定义有下面 4种方式,
1,先声明结构类型然后再定义结构变量 ;定义形式为:
结构名 结构变量名 ;
struct 结构名 结构变量名 1,结构变量名 2,...,结构变名 ;
如,struct SType v,*p,a[2];
SType v,*p,a[2];
可将结构声明,
struct SType { float member;int v; double a[6];};
与结构变量定义,struct SType v,*p,a[2];
合为一体形成结构声明和结构变量定义同步进行的格式。
8
2,有名结构类型直接定义结构变量,形式为:
struct 结构名 { 成员数据列表 ; } 结构变量列表 ;
struct SType { float member;int v; double a[6]; }
v,*p,a[2];
3,无名结构类型直接定义结构变量,形式为:
struct { 成员数据列表; } 结构变量列表 ;
struct { float member;int v; double a[6]; } v,*p,a[2];
4,typedef 简化结构声明和变量定义实用程序设计中采用如下方式来声明结构和定义变量:
struct SType { float member;int v; double a[6];};
typedef struct SType SNAME,*SP;
9
先建立一个结构类型的声明,结构名为 SType,然后由
typedef语句指定 SNAME为 SType的别名。
SP是 SType*的别名。或者干脆将上面两种形式合而为一有:
typedef struct SType
{ float member;int v; double a[6];} SNAME,*SP;
这种合一的声明放置在相应的头文件中,然后在相应的实现文件中用结构型别类名 SNAME定义结构变量或结构指针类型别名 SP定义结构指针:
SNAME x,y,z; SP p,q,r;
10
三、对结构数据的操作
1,结构变量的赋值操作,
考虑结构声明和结构变量 a,b结构指针 p的定义,如下:
struct SType { float member; int v; double a[6]; } a,b,*p;
对结构变量可以进行赋值操作,同类型的结构变量可以相互赋值。 其格式为:
目标结构变量 =源结构变量 ; a=b;
赋值操作就是将源结构变量的数据成员复制给同类型的目标结构变量的相应成员。两者拥有相同的数据状态。
系统一般通过高效的内存拷贝函数 memcpy完成结构变量之间的赋值。同类型的算术变量或指针相互赋值则简单地通过寄存器输送数据。
11
2,对结构指针的操作访问,
取地址运算符 &可以作用于结构变量 a,&a运算的结果得到由该结构变量代表的数据集合的首地址。这个地址也就是结构的第一个数据成员的地址 ;
访问指针运算符 *可以作用于结构指针,访问结构指针的结果是该指针正在指向的结构变量。
如果 p=&a则称 *p是结构变量 a的间接变量。结构指针可以先定义然后再赋予同类型属性的初值,也可以将定义和赋初值的过程同步,其格式为:
struct 结构名 *结构指针名 = &结构变量;
其中的结构名已经声明,结构变量是同一结构名定义的变量。 例如,p=&a;
b=*p;
12
3,对结构成员的操作对结构变量成员的操作通过双目圆点操作符,.”和箭头运算符,->”进行。圆点操作符称为对象访问成员运算符。
左操作数是结构变量,右操作数是相应的数据成员。其格式为:
结构变量,成员名 a.v
箭头运算符,->”是双目操作符,称为指针访问成员运算符。指针访问成员运算符的左操作数是指向结构变量的 指针,用 p表示。右操作数是结构中的成员。其格式为:
结构指针 ->成员名 p->v
圆点运算符 "." 箭头运算符 "->"都是访问成员运算符。
13
例如,a.v*p->v (a.v)*(p->v) ; a.v++ (a.v)++
- - p->v - - (p->v)
int* q=&a.v; //指针 q指向变量 a.v
double* r=&a.a[1];
//指针 r指向变量 a.a[1],*(a.a+1)=a.a[1]
*a.a=a.v; //*a.a表示访问地址 a.a指向的内存单元
scanf ("%f,%d",&a.member,&a.v);
//从屏幕上读取数据到变量 a.member和变量 a.v
scanf ("%lf,%lf",&a.a[0],a.a+1);
//从屏幕上读取数据到变量 a.a[0]和变量 a.a[1]
printf ("%lf,%f",p->a[0],(*p).a[1]);
//输出 double型变量 a.a[0]和 a.a[1]
14
编程时很少采用迂回的形式 (*p).v,(*p).v系统转换为,
(&(*p))->.v,
简化为,p->v
(&a)->v 和 (*p).v的圆括号是不可少的,省去圆括号导致不同的语法解释。
&a->v等价于 &(a->v),这表示取成员变量的地址,此时
a应为结构指针。
*p.v理解为 *(p.v),这表示访问指针 p.v指向的内存。如果 v不是指针成员,p不是结构变量,则导致语法错误。
15
printf 和 scanf 函数的格式转换一对一地匹配算术型和指针型数据 ;不能整个地读写结构变量,因
scanf(" %f,%d",&a)和 printf (" %lf,%f",a)导致运行时错误。
下面四个表达式在 p=&a时具有相同的值和类型属性,
a,a.v b,(&a)- >v c,p- >v d,(*p).v
如果结构指针原先仅与离散的结构变量向关联,不要对这时的指针进行加减运算。结构指针的增 1表示指针前移了
Sizeof (结构名 )个内存字节。 例如:
struct A { double d; long c[9]; } s [2] ;
// sizeof (A)=48,sizeof (s)=96
struct A* q=&s[0]; double * pd=&s[0].d;
long* pc=&s[0].c;
16
定义了一个结构数组 s[2]。
&s[0]和 &s[0].d具有相同的内存定位值,但它们的地址类型属性是不同的。
&s[0]具有地址属性为 struct A*,&s[0].d则具有 long*
类型的地址属性。
q+1指向 s[1],q[1] s[1],
pc[1] s[0].c[1]
*pd=pd[0]= s[0].d
17
四、结构变量的内存分布考虑一个结构名为 s的结构,同时定义一个结构变量名亦为 s的结构变量如下:
struct s
{ char c; //一个字节的元素
int k; //四字节的成员
} s ;
编译器通常将结构中的第一个成员放置在内存的双字起始边界上。其余的结构成员未必连续沿内存布置,结构变量中可能存在内存空洞。空白处的内存不具有确定值。
结构变量所占有的内存是其中数据成员所占内存之和同时考虑到边界对齐时所占有的空洞对内存的贡献。
18
[例 ]结构变量的内存与边界对齐效应
#include<stdio.h>
void main (void)
{ struct S { char c; int k; } s ;
struct A { char c[9]; double d; } a ;
struct B { char *c[9]; double* d; } b;
struct C { char c[8]; double d; } c ;
printf ("%d,%d,%d,%d \t",
sizeof (S),sizeof (a),sizeof (B),sizeof (c))
}
//输出,8,24,40,16
19
一、结构的声明和结构变量二、结构变量的定义形式三、对结构数据的操作四、结构变量的内存分布
2
一、结构的声明和结构变量由关键字 struct声明的数据描述称为结构类型而 union
或 class声明的数据描述称为联合类型或类类型,分别简称为结构和联合或类。
结构、联合、类是集合数据类型。
int,float等是系统预先取好的可以直接使用的类名,
关键字 struct和 union 或 class后紧跟的标识符则是用户引入的类名,需要事先加以声明。
3
定义直接导致内存的存储单元分配,声明则是一种关于数据类型的描述,本身一般不涉及内存的分配。
结构将密切相关的数据集合在一起。结构的声明具有如下的语法形式:
struct structName 结构 结构名
{ type member; { 数据类型 成员名 ;
type2 member2; 数据类型 2 成员名 2;
...
typen membern; 数据类型 n 成员名 n;
}; };
其中 type,typen等代表已经声明的类型,具体地 (下面左边和右边是等价的 ):
4
struct SType struct SType
{ float member; { float member; int v;
int v; enum {N=6};
double a[6]; double a[N];
}; }
花括号包括的部分称为结构的成员数据列表,成员数据列表具体描述了结构的组成情况,其间每一个数据项采用
[数据类型 成员名 ;]的格式,这种格式类似于变量定义语句但不进行初始化处理,称为成员声明语句。
5
关键字 struct之后引出结构名为 SType的结构声明,
SType是用户建立的类名,类名和成员名遵循标识符的命名规定,结构名可以和系统内置的类名 int,float和 double一样定义变量,通过结构名定义的变量称为结构变量。
结构中声明的变量 member,v,a[6]等是结构的成员。
同一个结构中的成员不能彼此同名,但是不同结构名的结构可以拥有一样的成员名称,不同数量的成员。
其中枚举常数 N可以界定成员数组的维数,它不占结构变量的内存。
6
可以在结构中出现当前结构名,以声明一个指向该结构变量的指针,如 struct A{int n;A* p;};由此形成复杂的链表结构。
但结构的声明中不能用当前结构名声明成员变量即结构中不能包含自身的实例,这称为递归声明。建立结构的声明时不允许直接递归或间接递归。
例如,struct A{int n;A a;};是不可以的。
结构类型的声明可以出现在函数体内也可以位于全局范围。在函数体内声明的结构其有效范围仅限于该函数之内,
而在全局范围声明的结构对于其后的程序段具有可操作性。
在相同作用范围结构名应是唯一的。在函数体内不定义函数,很少在函数体内声明结构。
7
二、结构变量的定义形式结构变量分为全局的、局部的结构变量和静态结构变量。指向结构变量的指针简称结构指针。结构变量或结构指针的定义有下面 4种方式,
1,先声明结构类型然后再定义结构变量 ;定义形式为:
结构名 结构变量名 ;
struct 结构名 结构变量名 1,结构变量名 2,...,结构变名 ;
如,struct SType v,*p,a[2];
SType v,*p,a[2];
可将结构声明,
struct SType { float member;int v; double a[6];};
与结构变量定义,struct SType v,*p,a[2];
合为一体形成结构声明和结构变量定义同步进行的格式。
8
2,有名结构类型直接定义结构变量,形式为:
struct 结构名 { 成员数据列表 ; } 结构变量列表 ;
struct SType { float member;int v; double a[6]; }
v,*p,a[2];
3,无名结构类型直接定义结构变量,形式为:
struct { 成员数据列表; } 结构变量列表 ;
struct { float member;int v; double a[6]; } v,*p,a[2];
4,typedef 简化结构声明和变量定义实用程序设计中采用如下方式来声明结构和定义变量:
struct SType { float member;int v; double a[6];};
typedef struct SType SNAME,*SP;
9
先建立一个结构类型的声明,结构名为 SType,然后由
typedef语句指定 SNAME为 SType的别名。
SP是 SType*的别名。或者干脆将上面两种形式合而为一有:
typedef struct SType
{ float member;int v; double a[6];} SNAME,*SP;
这种合一的声明放置在相应的头文件中,然后在相应的实现文件中用结构型别类名 SNAME定义结构变量或结构指针类型别名 SP定义结构指针:
SNAME x,y,z; SP p,q,r;
10
三、对结构数据的操作
1,结构变量的赋值操作,
考虑结构声明和结构变量 a,b结构指针 p的定义,如下:
struct SType { float member; int v; double a[6]; } a,b,*p;
对结构变量可以进行赋值操作,同类型的结构变量可以相互赋值。 其格式为:
目标结构变量 =源结构变量 ; a=b;
赋值操作就是将源结构变量的数据成员复制给同类型的目标结构变量的相应成员。两者拥有相同的数据状态。
系统一般通过高效的内存拷贝函数 memcpy完成结构变量之间的赋值。同类型的算术变量或指针相互赋值则简单地通过寄存器输送数据。
11
2,对结构指针的操作访问,
取地址运算符 &可以作用于结构变量 a,&a运算的结果得到由该结构变量代表的数据集合的首地址。这个地址也就是结构的第一个数据成员的地址 ;
访问指针运算符 *可以作用于结构指针,访问结构指针的结果是该指针正在指向的结构变量。
如果 p=&a则称 *p是结构变量 a的间接变量。结构指针可以先定义然后再赋予同类型属性的初值,也可以将定义和赋初值的过程同步,其格式为:
struct 结构名 *结构指针名 = &结构变量;
其中的结构名已经声明,结构变量是同一结构名定义的变量。 例如,p=&a;
b=*p;
12
3,对结构成员的操作对结构变量成员的操作通过双目圆点操作符,.”和箭头运算符,->”进行。圆点操作符称为对象访问成员运算符。
左操作数是结构变量,右操作数是相应的数据成员。其格式为:
结构变量,成员名 a.v
箭头运算符,->”是双目操作符,称为指针访问成员运算符。指针访问成员运算符的左操作数是指向结构变量的 指针,用 p表示。右操作数是结构中的成员。其格式为:
结构指针 ->成员名 p->v
圆点运算符 "." 箭头运算符 "->"都是访问成员运算符。
13
例如,a.v*p->v (a.v)*(p->v) ; a.v++ (a.v)++
- - p->v - - (p->v)
int* q=&a.v; //指针 q指向变量 a.v
double* r=&a.a[1];
//指针 r指向变量 a.a[1],*(a.a+1)=a.a[1]
*a.a=a.v; //*a.a表示访问地址 a.a指向的内存单元
scanf ("%f,%d",&a.member,&a.v);
//从屏幕上读取数据到变量 a.member和变量 a.v
scanf ("%lf,%lf",&a.a[0],a.a+1);
//从屏幕上读取数据到变量 a.a[0]和变量 a.a[1]
printf ("%lf,%f",p->a[0],(*p).a[1]);
//输出 double型变量 a.a[0]和 a.a[1]
14
编程时很少采用迂回的形式 (*p).v,(*p).v系统转换为,
(&(*p))->.v,
简化为,p->v
(&a)->v 和 (*p).v的圆括号是不可少的,省去圆括号导致不同的语法解释。
&a->v等价于 &(a->v),这表示取成员变量的地址,此时
a应为结构指针。
*p.v理解为 *(p.v),这表示访问指针 p.v指向的内存。如果 v不是指针成员,p不是结构变量,则导致语法错误。
15
printf 和 scanf 函数的格式转换一对一地匹配算术型和指针型数据 ;不能整个地读写结构变量,因
scanf(" %f,%d",&a)和 printf (" %lf,%f",a)导致运行时错误。
下面四个表达式在 p=&a时具有相同的值和类型属性,
a,a.v b,(&a)- >v c,p- >v d,(*p).v
如果结构指针原先仅与离散的结构变量向关联,不要对这时的指针进行加减运算。结构指针的增 1表示指针前移了
Sizeof (结构名 )个内存字节。 例如:
struct A { double d; long c[9]; } s [2] ;
// sizeof (A)=48,sizeof (s)=96
struct A* q=&s[0]; double * pd=&s[0].d;
long* pc=&s[0].c;
16
定义了一个结构数组 s[2]。
&s[0]和 &s[0].d具有相同的内存定位值,但它们的地址类型属性是不同的。
&s[0]具有地址属性为 struct A*,&s[0].d则具有 long*
类型的地址属性。
q+1指向 s[1],q[1] s[1],
pc[1] s[0].c[1]
*pd=pd[0]= s[0].d
17
四、结构变量的内存分布考虑一个结构名为 s的结构,同时定义一个结构变量名亦为 s的结构变量如下:
struct s
{ char c; //一个字节的元素
int k; //四字节的成员
} s ;
编译器通常将结构中的第一个成员放置在内存的双字起始边界上。其余的结构成员未必连续沿内存布置,结构变量中可能存在内存空洞。空白处的内存不具有确定值。
结构变量所占有的内存是其中数据成员所占内存之和同时考虑到边界对齐时所占有的空洞对内存的贡献。
18
[例 ]结构变量的内存与边界对齐效应
#include<stdio.h>
void main (void)
{ struct S { char c; int k; } s ;
struct A { char c[9]; double d; } a ;
struct B { char *c[9]; double* d; } b;
struct C { char c[8]; double d; } c ;
printf ("%d,%d,%d,%d \t",
sizeof (S),sizeof (a),sizeof (B),sizeof (c))
}
//输出,8,24,40,16
19