结构体使c语言有能力描述复杂对象。
例:描述一个人 —— 名字、性别、年龄 ……
要把多个类型整合在一起才能描述一个人,此时可以创建人的结构体类型
结构体类型声明
结构体成员类型可以是float、int、数组、指针、甚至其它结构体.
结构体变量声明
如果想省略struct,可以使用类型重命名typedef.
注意 结构体类型声明时并没有创建空间,创建结构体变量才会占用空间.
结构体变量初始化
结构体成员访问
1 结构体变量.结构体成员
2 结构体指针->结构体成员
typedef struct point
{
int x;
int y;
}point;//声明结构体类型坐标,并重命名
typedef struct power
{
point way;
int time;
char arr[5];
}power;
int main()
{
point a1;
power a3;
scanf("%d %d", &a1.x, &a1.y);
scanf("%d %d %d", &(a3.way.x), &(a3.way.y), &a3.time);
scanf("%s", a3.arr);//一般情况下,数组名本身是地址
a1.x = 3;
(a3.arr)[0] = 'c'; //a3.arr找到a3结构体变量的成员arr,
//这是个数组名,可以用[ ]访问数组中的元素
a3.way.x = 30; //a3.way找到a3结构体变量的成员way
return 0; //因为way本身又是结构体变量,所以.x找到它的成员
}
结构体传参
如果函数传参传结构体变量:
形参是实参的临时拷贝,即会在栈区重新创建一个和该结构体变量一样大的空间,
把结构体变量的内容拷贝一份放进去。如果结构体过大,栈区的内存开销会比较大,性能会下降。
如果函数传参传结构体指针:
指针的大小是固定的,32位平台4个字节,64位平台8个字节,传指针依然能找到结构体变量的数据。
因此结构体传参时最好传结构体指针!!!
结构体内存对齐(计算结构体大小)
这说明:结构体内部的成员必然存在某些对齐现象,使得空间更大。
对齐规则
结构体变量开辟空间时,在内存中的起始位置也已经对齐了
如果起始位置没对齐,那下面的对齐就没有意义
(1) 每一个结构体成员都有一个对齐数.
对齐数 = 【编译器默认的对齐数】与【该成员大小】的 较小值.
(2) 偏移量:当前位置相对于结构体起始位置的距离.如:
(3)【第一个结构体成员的地址】与【结构体变量的地址】偏移量为0.
(4)【其它结构体成员的地址】与【结构体变量的地址】偏移量为 自身对齐数的整数倍.
(5)结构体总大小为最大对齐数(每个成员变量都有一个对齐数,不考虑默认对齐数)的整数倍.
(5)如果嵌套了其它结构体,嵌套的结构体要对齐到自己最大对齐数的整数倍处
结构体的整体大小就是最大对齐数(要和嵌套结构体的最大对齐数做比较)的整数倍.
简单理解:【嵌套结构体】的对齐数就是【嵌套结构体】内部的最大对齐数.
#include <stdio.h>
struct A
{
char aChar; //1 < 8 ? 1 : 8 对齐数为1
int aInt; //4 < 8 ? 4 : 8 对齐数为4
};
struct B
{
char bChar; //对齐数为1
struct A bStructA;//内部最大对齐数为4
int bInt; //对齐数为4
};
int main()
{
printf("%d\n", sizeof(struct A));
printf("%d\n", sizeof(struct B));
return 0;
}
struct A结构体类型变量的内存存储:
struct B结构体类型变量的内存存储:
为什么存在内存对齐?
平台原因
某些硬件平台只能在某些地址处取某些特定类型,否则硬件异常。
比如某些硬件只能在4的倍数地址处进行数据读取或存储.
所以在存储数据时最好存放在对齐的位置上,使读取数据方便且不出错.
性能原因
内存对齐是一种以空间换时间的做法.
为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问只需一次.
例:在32位机器中,一次可以操作32bit/4byte的数据,想拿出结构体成员b的数据,需要2次操作.
对于b的读取,不考虑内存对齐要两次读取才能完整读出来,
而考虑内存对齐只需要一次就全部读取出来,第一次的读取没有意义.
这样对应b的读取,虽然浪费了一点空间,但效率会有提高.
合理设计结构体
在设计结构体时,我们既想满足内存对齐,又想节省空间:
方案1:让占用空间小的成员尽量集中在一起
由于占用空间较小,对齐数也通常较小,那么那些可能浪费的空间就放得下这些小成员,减少浪费.
方案2:修改默认对齐数
#pragma pack(数字)
#pragma pack(4)//设置默认对齐数为4
struct A
{
char aChar; //1 < 4 ? 1 : 4 对齐数为1
double aDouble; //8< 4 ? 8 : 4 对齐数为4
};
#pragma pack()//取消设置的默认对齐数,还原为编译器默认
struct B
{
char bChar; //1 < 8 ? 1 : 8 对齐数为1
double bDouble; //8< 8 ? 8 : 8 对齐数为8
};
int main()
{
printf("%d\n", sizeof(struct A));
printf("%d", sizeof(struct B));
return 0;
}