1.结构体类型的说明
前面我们学的数据类型都只包含一种数据类型,即使是数组,也只能是同一类型的多个元素。在实际应用中,通常存在由多种不同类型的数据组成的实体,如一个职工的数据包括编号,姓名,性别,出生日期,文化程度等等。如果用独立的简单数据项分别表示它们,就不能体现数据的整体性,不便于操作,针对这种问题,C语言规定可以用“结构体”数据类型来描述。
2.结构体类型定义的一般形式
结构体类型是一种“构造”而成的数据类型,那么在说明和使用它之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。结构体类型和系统预定义的标准类型一样,可以用来定义变量,结构体类型的一般形式:
struct 结构体类型名
{
成员变量说明列表
};
其中花括号内的内容是该结构体类型的成员说明,每个成员说明的形式为:
数据类型符 成员变量名;
例如,由年,月,日组成的结构体类型为:
struct date
{
int year;
int month;
int day;
};
又如一个职工实体的结构体类型为:
struct employee
{
long no;//编号
char name[20];//名字
char sex;//性别
struct date birth;//出生日期
char education[30];//教育程度
double salary;//工资
long IDcard;//省份证号码
char addr[40];//家庭住址
};
对以上说明:
1.
struct是定义结构体类型的关键字,struct后面的“结构体类型名”,是任意选取的,但应符合标识符的命名规则。建议使用具有一定意义的单词作为结构体类型名。
2.
花括号括起来的是结构体成员变量的说明,按照之前变量的定义方式就可以,虽然成员变量的定义形式同变量定义,但不能直接使用。
3.
结构体成员变量的类型可以不相同。
4.
结构体类型struct employee内含有一个结构体类型成员birth,说明结构体可以嵌套定义,但是这种嵌套不能包含自身,即不能自己定义自己(因为这样的结构体变量的为无穷大)。但是可以通过指针访问自己(即定义一个结构体指针变量)如:
struct node
{
int date;
struct node* next;
};
5.
定义一个结构体类型,并不意味着系统将分配一段内存单元来储存各个数据项成员,这只是定义类型而不是定义变量。他告诉系统该结构体由哪些类型的成员构成,并把他们当作一个整体来处理,struct employee是程序设计者自定义的类型,它与系统预定义的标准类型一样,可以用来定义变量,使变量具有struct employee类型。如:struct employee worker,workers[20];分别定义了struct employee 结构体类型的变量worker和struct employee 结构体类型的数组workers。
1.匿名结构体
匿名结构体只能使用一次
struct
{
char c;
int i;
}s={'x',100};
ps:匿名结构体中含有变量s.
2.结构体的重命名
typedef struct Node
{
int date;
struct Node* next;
}Node;
这种写法等同于下面的:
struct Node
{
int date;
struct Node* next;
};
typedef struct Node Node;
上述代码都是将结构体struct Node重命名为Node。
3.结构体的内存对齐
结构体的对齐规则:
1.
结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
2.
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的对齐数与该成员变量大小的较小值。vs默认对齐数为8,Linux中gcc没有默认的对齐数,对齐数就是其成员自身大小。(不同的编译器默认的对齐数可能不同)
3.
结构体总大小为最大对齐数(结构体中所有对齐数中最大的)的整数倍。
4.
如果存在嵌套结构体的情况,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍,结构体的总大小就是所有对齐数(含嵌套结构体成员的对齐数)的整数倍。
例题1;
代码自取:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S
{
char c1;
int i;
char c2;
};
int main()
{
struct S s = { 0 };
printf("%zd", sizeof(s));
return 0;
}
例题2:
代码自取:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S
{
char c1;
char c2;
int i;
};
int main()
{
struct S s = { 0 };
printf("%zd", sizeof(s));
return 0;
}
下面我们画图来解析上面两个例题
从上面我们可以知道,虽然两个例题结构体内的成员都是一样的,但是成员的顺序的不同也会影响总的字节大小。
例题3:
代码自取:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S1
{
char c1;
char c2;
int i;
}s1;
struct S2
{
char c3;
struct S1 s1;
double d;
}s2;
int main()
{
printf("%zd", sizeof(s2));
return 0;
}
依旧是画图解释哦:
针对上述例题,我们不难得出,在设计结构体时,既要满足对齐,又要节省空间,第一种方法,可以让占空间小的成员尽量集中在一起。第二种方法,我们可以修改系统默认的对齐数。修改方法如下:#pragma pack(n)这个预处理指令,可以将编译器的默认对齐数修改为n。紧接着在其下面再加一条#pragma pack()则可以取消设置的默认对齐数。
4,结构体的位段
位段式是专门用来节省空间的。
struct S
{
int _a : 2;//2代表_a只占两个bit,下面的数字意义以此类推
int _b : 5;
int _c : 20;
int _d : 30;
};
位段的内存分配:
1.位段的成员可以是int,unsigned int ,signed int或是char等类型。
2.位段的空间是按需要以四个字节(int)或一个字节(char)的方式来开辟的。
3.位段在不同的平台下可能是不同的:16位平台下,int是2个字节,如果int _a:27;则出现错误,因为27>2*8.
4.不能对位段的成员取地址(&)。
位段的跨平台问题:
1.int位段被当成有符号数还是⽆符号数是不确定的。
2.位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会 出问题。
3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4.当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃 剩余的位还是利⽤,这是不确定的。
下面在vs上解析下面代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10; //00001010
s. b = 12;//00001100
s.c = 3; //00000011
s.d = 4; //00000100
return 0;
}
结构体的内容就到这里啦!码字不易,请留下你的点赞,关注,评论吧!谢谢咯!雯雯文下次再和大家见面咯!