1.结构体内存布局的基本规则
1.1.成员顺序
结构体的成员在内存中按照声明的顺序依次存放。第一个成员的地址与结构体的地址相同。
struct Example {
char a; // 1字节
int b; // 4字节
double c; // 8字节
short d; // 2字节
};
1.2 对齐要求和结构体大小的计算
结构体的大小不是简单地将所有成员的大小相加,而是要考虑对齐和填充。每个基本类型(如int、double、指针等)都有对齐要求。对齐要求通常是该类型的大小(以字节为单位)或者是编译器特定的值。例如,在大多数系统上,int的对齐要求是4字节,double的对齐要求是8字节。
核心规则1: 如果你有一个成员变量_e大小为x需要加入到相对结构体起始地址0偏移y byte的位置,那么需要校验y是否为x的整数倍,若不为整数倍则需加最少的n字节,补齐到整数倍.
核心规则2: 结构体总大小必须是最大成员大小的整数倍
struct S1 {
char a; // 1字节
int b; // 4字节
short c; // 2字节
};
// 内存布局:
// [a][填充3字节][b(4字节)][c][填充2字节]
// 总大小: 12字节
struct S2 {
int b; // 4字节
short c; // 2字节
char a; // 1字节
// 填充1字节
};
// 内存布局:
// [b(4字节)][c][a][填充1字节]
// 总大小: 8字节 (更紧凑!你喜欢吗?)
2.一些案例
关于嵌套结构体:
struct Inner {
int x; // 4字节
char y; // 1字节
// 填充3字节
}; // 大小: 8字节
struct Outer {
short a; // 2字节
// 填充2字节
Inner inner; // 8字节 (从4字节边界开始)
char b; // 1字节
// 填充7字节
}; // 大小: 24字节
3.一个特殊用法!!!
对于任何一个结构体,可以获取到任何一个元素相较于结构体起点的偏移量:
&((struct A*)0->_element)
结构体指针指向的就是结构体的起点, 同时此起点有和起始元素的地址相同;
只要我们拥有结构体某一个元素的指针,那么就可以用这个指针加上偏移量得到整个结构体的指针
可以用预处理指令定义一个宏offsetof:
#define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
4.大小端问题
结构体本身并不决定字节序(大端或小端)也不参与字节序, 字节序是由底层的硬件架构决定的。结构体的内存布局中, 成员按照声明顺序排列(考虑对齐), 但每个成员内部的字节顺序则取决于系统的字节序。
大端存储(Big-endian):高位字节存储在低地址, 低位字节存储在高地址
小端存储(Little-endian):低位字节存储在低地址, 高位字节存储在高地址
/*
结构体内部遵循硬件平台的字节序
x86/x64架构:小端序
ARM(可配置):通常小端序
PowerPC、SPARC:大端序
网络字节序:大端序
*/

被折叠的 条评论
为什么被折叠?



