计算结构体大小的三条规则:
- 结构体变量的首地址必须是“结构体最大成员变量所占字节数的整数倍”
- 结构体变量的成员的偏移量必须是该成员变量所占字节数的整数倍
- 结构体变量的总大小必须是最大结构体成员变量所占字节数的整数倍
字节对齐问题:
- 内存的基本变量是字节,理论上说是可以从任何地址访问内存,但实际上不是,CPU访问内存是以2、4或8 的倍数来读取字节块的,因此就会对一些基本类型的地址做出一些限制,既
他们的内存地址必须是2、4或者8的倍数,这就是内存对齐
2.有些平台是从偶数位开始读,有的则是奇数位
3.由于不同的平台读取的方式可能不同,相同的结构在不同的平台上的大小也不一样,所以在无意识的情况下,不同平台的数据相互传递有可能会产生严重的后果。
指令对齐值:
预处理指令#pragma pack()可以改变默认对齐数,可改为1、2、4、8、16等。
vs的默认对齐数是8,gcc 是4.
总结
1.结构体的首地址=MIN{“结构体最大基本数据类型所占字节数”,指定对齐值}大小的整数倍。
2.结构体每个成员相对于结构体首地址的偏移量=MIN{基本数据类型成员,指定对齐方式}大小的整数倍
3.结构体变量的总大小={“结构体最大基本数据类型所占字节数”,制定对齐方式}大小的整数倍。
在设计结构体时既要满足对齐又要节省空间,如何做到?
设计的时候将所占字节数少的成员放到前面,所占字节数大成员放到后面,这样就减少了因为字节对齐而浪费掉的空间。
不定义结构体变量如何计算结构体成员的相对偏移量
((struct node_t *)0)->c
上述代码应该比较好理解,由于我们知道结构体的内存地址编号为0,所以我们就可以直接通过内存地址的方式来访问该结构体的成员变量,相应的代码的含义就是 获取内存地址编号为0的结构体struct node_t的成员变量c。
注:此处只是利用了编译器的特性来计算结构体偏移,并未对内存地址0有任何操作,有些同学对此可能还有些疑问,详细的了解该问题可参考关于c语言结构体成员变量访问方式的一点思考。
此时,我们的偏移求法就消除了struct node_t node这个自定义变量,直接一行代码解决,:
(unsigned long)(&(((struct node_t *)0)->c))
这里我们将上面的代码功能定义为一个宏,该宏的作用是用来计算某结构体内成员变量的偏移:
#define OFFSET_OF(type, member) (unsigned long)(&(((type *)0)->member))
使用上面的宏,就可以直接得到成员变量c在结构体struct node_t中的偏移为:
OFFSET_OF(struct node_t, c)