与对齐有关的问题

在函数中的局部范围内声明时,将所有相同类型的变量组合在一起是一种好的做法吗? 如果是,为什么? 它是否解决了内存对齐问题?

我认为它与我20年前使用的VAX C编译器有关,但不适用于任何现代编译器。 假设局部变量将以任何特定顺序排列是不安全的,当然不能安全地假设它们将按照您声明的顺序排列。 我肯定看到MSVC编译器重新排序它们。

对相同类型的变量进行分组确实有助于它们是结构的字段,因为结构的字段顺序保证与声明的顺序相匹配。

这取决于编译器; 即编译器将按其认为合适的方式布局内存。 所以除了好的风格之外,它没有任何影响(至少在我使用的任何现代编译器中)。

一般来说,它对局部变量没有帮助。 存在可由编译器应用的优化规则以及可用于操纵对齐的附加“pragma”指令。

它不会解决对齐问题,因为不应该存在对齐问题 – 编译器会正确地对齐排列局部变量,因此应该没有对齐问题。

分配类似对齐类型的唯一问题是减少堆栈的使用,但编译器可以自由地重新排序堆栈上的变量布局(甚至可以在不同时间重用不同局部变量的位置,或者保留本地在寄存器中,并没有将它们放在堆栈中),所以你通常不会为优化编译购买任何东西。

如果您要成为堆栈中的“打字”项,则需要使用与堆栈数据相同的对齐安全性方法 – 可能更多,因为malloc()分配的malloc()或保证new适合任何类型的对齐 – 不保证分配给自动变量的存储。

‘类型惩罚’就是你绕过类型系统的时候。 例如通过将char*转换为int*来访问char数组中的字节作为int*

 int x; char data[4]; fill_data( data, sizeof(data)); int x = *(int*) data; 

由于char[]的对齐要求可能与int不同,因此上述通过int*访问data可能不是“对齐安全”。 但是,由于malloc()被指定为返回适合任何类型的指针,因此以下内容不应出现任何对齐问题:

 int x; char* pData = malloc( 4); if (!pData) exit(-1); fill_data( pData, 4); x = *(int*) pData; 

但是,请注意sizeof(int)可能不是4, int类型可能是little-endian或big-endian,因此上述代码仍存在可移植性问题 – 只是没有对齐问题。 还有其他方法可以执行类型惩罚,包括通过union不同成员访问数据,但这些方法可能有自己的可移植性问题,特别是访问不是最后一个成员的成员是未指定的行为。

填充和对齐问题仅对结构而非局部变量有关,因为编译器可以按照所需的顺序放置局部变量。 至于为什么它在结构中很重要 –

许多C编译器将通过在它们之间插入填充字节来对齐struct成员。 例如,如果你有一个结构S {int a; char b; int c; char d; int e; 并且目标硬件要求int在4字节边界上对齐,然后编译器将在b和c之间以及d和e之间插入三个填充字节,每个实例浪费6个字节的内存。 另一方面,如果成员按顺序ac,e,b,d,那么它将在末尾插入两个字节的填充(这样S的大小整体上是4的倍数,因此成员将在数组中正确对齐),每个实例只浪费2个字节。 规则是非常平台和编译器特定的; 一些编译器可能会重新排列成员以避免填充,有些编译器可以扩展来控制填充和对齐规则,以防您需要二进制兼容性。 一般来说,你应该只关心对齐,如果你是直接读取/写入结构,并且取决于它们具有相同的布局(这通常是一个坏主意),或者你希望有很多实例和内存是非常珍贵的。