我有一个问题,我知道填充是为了有效地访问内存中的变量,但想象一下,如果我们只能访问8字节的内存块(if是一台64位计算机),我们有3个变量,1char1int和1char,不在结构体中,想象堆栈指针指向那些8字节chuck之一的开头编译器就不能不加填充吗?我已经理解了填充的建议,但在很多情况下,乍一看似乎是不必要的内存浪费。
piwo6bdm1#
如果你分配单个变量,编译器可以自由地按它的意愿分配它们,在任何地址或以任何顺序。当然,它会在考虑对齐的情况下这样做,但也可能与优化器设置有关:优化速度与尺寸。根据CPU的不同,它可能支持也可能不支持未对齐的访问,或者它可能根本没有对齐要求。在编写与硬件相关的程序时,经常需要按照一定的顺序分配变量。该代码可以对数据通信协议、硬件寄存器Map、图像存储器布局等进行建模。C提供的保证最低有效位->最高有效位地址分配顺序的唯一机制是struct。因此,C显式禁止编译器更改struct成员的分配顺序。(In理论上,C也应该支持struct的位域成员,尽管在实践中,标准对该功能的规定太少,以至于不能可靠地使用。因此,编译器对struct的填充优化是不可能的--它必须按照程序员指定的顺序分配成员,即使这意味着添加大量无用的填充字节来适应对齐。但是,只有当结构使用的成员小于CPU支持的对齐要求时,才会出现此问题。当不需要结构体分配顺序时,内存优化的一个技巧是,创建 * n * 个不同的数组,而不是具有 * n * 个成员的结构体,每个数组具有其中一个成员的类型。然后,数组索引将成为属于同一组的数组成员之间的关联,而不是mystruct.member语法。也有一些非标准的方法来禁用填充,但它们不是可移植的,通常是最后的手段-因为填充是出于某种原因添加的。无论是否启用填充,如果您需要真正可移植的结构,您可能必须编写手动序列化/反序列化例程,memcpy逐个成员。
struct
mystruct.member
memcpy
kokeuurv2#
任何编译器都会产生尽可能少的填充量。C++编译器有时可以重新排列结构体成员的顺序以减少填充。你需要先检查处理器实际上能做什么。假设你的处理器可以在任何地址读取字节,在偶数地址读取16位字,在4的倍数读取32位,在8的倍数读取64位。如果你的结构是byte / eight bytes / byte,那么你的偏移量是0,8,16。将其更改为byte / byte / eight bytes,则偏移量为0,1,8。现在数组元素必须正确对齐,数组元素之间没有间隙。所以在第一种情况下,结构体必须是8字节的倍数,所以它是24字节。在第二种情况下,它也必须是8字节的倍数,但只需要16字节。C编译器对此无能为力。在堆栈上,编译器可以自由地对变量重新排序,因此它可以(使用这个处理器)将两个字节放入相邻字节中而无需填充。填充本身是无法避免的,因为你不能有不存在的内存。如果一个变量位于位置100,另一个位于位置108,则两者之间必须有其他变量。
2条答案
按热度按时间piwo6bdm1#
如果你分配单个变量,编译器可以自由地按它的意愿分配它们,在任何地址或以任何顺序。当然,它会在考虑对齐的情况下这样做,但也可能与优化器设置有关:优化速度与尺寸。根据CPU的不同,它可能支持也可能不支持未对齐的访问,或者它可能根本没有对齐要求。
在编写与硬件相关的程序时,经常需要按照一定的顺序分配变量。该代码可以对数据通信协议、硬件寄存器Map、图像存储器布局等进行建模。C提供的保证最低有效位->最高有效位地址分配顺序的唯一机制是
struct
。因此,C显式禁止编译器更改struct
成员的分配顺序。(In理论上,C也应该支持
struct
的位域成员,尽管在实践中,标准对该功能的规定太少,以至于不能可靠地使用。因此,编译器对
struct
的填充优化是不可能的--它必须按照程序员指定的顺序分配成员,即使这意味着添加大量无用的填充字节来适应对齐。但是,只有当结构使用的成员小于CPU支持的对齐要求时,才会出现此问题。当不需要结构体分配顺序时,内存优化的一个技巧是,创建 * n * 个不同的数组,而不是具有 * n * 个成员的结构体,每个数组具有其中一个成员的类型。然后,数组索引将成为属于同一组的数组成员之间的关联,而不是
mystruct.member
语法。也有一些非标准的方法来禁用填充,但它们不是可移植的,通常是最后的手段-因为填充是出于某种原因添加的。无论是否启用填充,如果您需要真正可移植的结构,您可能必须编写手动序列化/反序列化例程,
memcpy
逐个成员。kokeuurv2#
任何编译器都会产生尽可能少的填充量。C++编译器有时可以重新排列结构体成员的顺序以减少填充。
你需要先检查处理器实际上能做什么。假设你的处理器可以在任何地址读取字节,在偶数地址读取16位字,在4的倍数读取32位,在8的倍数读取64位。如果你的结构是byte / eight bytes / byte,那么你的偏移量是0,8,16。将其更改为byte / byte / eight bytes,则偏移量为0,1,8。
现在数组元素必须正确对齐,数组元素之间没有间隙。所以在第一种情况下,结构体必须是8字节的倍数,所以它是24字节。在第二种情况下,它也必须是8字节的倍数,但只需要16字节。C编译器对此无能为力。
在堆栈上,编译器可以自由地对变量重新排序,因此它可以(使用这个处理器)将两个字节放入相邻字节中而无需填充。填充本身是无法避免的,因为你不能有不存在的内存。如果一个变量位于位置100,另一个位于位置108,则两者之间必须有其他变量。