目录
结构体的声明及使用
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。例如要描述一个学生,需要使用年龄,成绩,名字等变量,这时声明一个结构体如下:
struct stu //struct是结构体关键字
{
int age;
char name[20];
int grade;
};
这个结构体的类型就是struct stu,要用该结构体声明变量时,要使用struct stu声明。
要访问结构体中的成员,需使用'.'操作符
int main()
{
struct stu s;
s.age = 12;
return 0;
}
或者这么写
int main()
{
struct stu s;
struct stu* p = &s;
p->age = 12;
return 0;
}
结构体的特殊声明
结构体使用时也可以匿名使用。如
struct //struct是结构体关键字
{
int age;
char name[20];
int grade;
}s;
这样也可以声明一个结构体变量。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
struct //struct是结构体关键字
{
int age;
char name[20];
int grade;
}*p;
struct //struct是结构体关键字
{
int age;
char name[20];
int grade;
}s;
p = &s;
这段代码实际上是非法的 ,编译器会处理成两个不同的类型。
结构体内存对齐
1.对齐规则
结构体在内存中存储时有一套对齐规则。
1. 结构体中的第一个成员对齐到和结构体变量起始位置偏移量为0的地方。
2.其他成员要对齐到某个数字(对齐数)的整数倍处。
对齐数 = 该成员类型的大小和编译器默认对齐数之间较小的值。
vs默认对齐数是8,Linux中gcc没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩。
3.结构体最终的大小必须是成员中最大对齐数(每个成员都有对齐数,最大对齐数就是这些对齐数中最大的)的整数倍。
4.如果有嵌套结构体,嵌套的结构体对齐到自己的成员中最大对齐数的整数倍,整个结构体大小就是所有成员变量(包括嵌套的结构体)中最大对齐数的整数倍。
struct S
{
double d;
char c;
int i;
};
以该结构体为例分析。
第一个成员d的类型大小为8,vs默认对齐数是8,因此对齐数取8
然后是c,类型大小为1,vs默认对齐数为8,取小值则为1,则对齐数为1,而偏移量为8处是1的倍数。
最后是i,类型大小为4,小于vs默认对齐数8,因此对齐数取4,那么i必须从偏移量为4的整数倍的地方开始存放,就应该从12处存放
这就是这个结构体在内存中的存储情况,可以看到他的大小为16个字节。
2.为什么会对齐
平台原因
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因
数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果不是8的倍数会导致访问效率变低,因为8字节内容可能被存放在不同的两个8字节内存块中。
结构体对齐规则是拿时间换空间的做法。
3.修改默认对齐数
可以使用#pragma这个预处理指令。
#pragma pack(1)
将默认对齐数修改为1,若要恢复默认对齐数,再次使用
#pragma pack()