在函数中的局部范围内声明时,将所有相同类型的变量组合在一起是一种好习惯吗?如果是,为什么?它是否解决了内存对齐问题?
答案 0 :(得分:6)
我认为这与我20年前使用的VAX C编译器有关,但不适用于任何现代编译器。 不安全地假设局部变量将按任何特定顺序排列,当然不能安全地假设它们将按照您声明的顺序排列。我肯定看到MSVC编译器重新排序它们。
将相同类型的变量分组时,它们是结构的字段,因为结构的字段顺序保证与声明的顺序相匹配。
答案 1 :(得分:3)
这取决于编译器;即编译器将按其认为合适的方式布局内存。除了好的风格之外,它没有任何效果(至少在我使用过的任何现代编译器中)。
答案 2 :(得分:1)
它不会解决对齐问题,因为不应该存在对齐问题 - 编译器会正确排列你的局部变量,所以应该没有对齐问题。
将类似对齐类型分组的唯一问题可能是减少堆栈的使用,但编译器可以自由地重新排序堆栈上的变量布局(甚至可以重用不同的位置)不同时间的局部变量,或者将本地变量保存在寄存器中,而不是将它们放在堆栈中),所以你通常不会为优化的编译购买任何东西。
如果您要成为堆栈中的“打字”项,则需要使用与堆栈数据相同的对齐安全性方法 - 可能更多,因为由{分配的内存保证{1}}或malloc()
适当地对齐任何类型 - 不保证分配给自动变量的存储。
'类型惩罚'是指你绕过类型系统。例如,通过将new
转换为char
,将int
数组中的字节作为char*
访问:
int*
由于int x;
char data[4];
fill_data( data, sizeof(data));
int x = *(int*) data;
的对齐要求可能与char[]
不同,因此int
通过data
的上述访问权限可能不是“对齐安全”。但是,由于int*
被指定为返回适合任何类型的指针,因此以下内容不应存在任何对齐问题:
malloc()
但请注意,int x;
char* pData = malloc( 4);
if (!pData) exit(-1);
fill_data( pData, 4);
x = *(int*) pData;
可能不是4,sizeof(int)
类型可能是小端或大端,因此上述代码仍然存在可移植性问题 - 只是没有对齐问题。还有其他方法可以执行类型惩罚,包括通过int
的不同成员访问数据,但这些方法可能有自己的可移植性问题,特别是访问不是最后一个成员的成员是未指定的行为。
答案 3 :(得分:1)
一般来说,它对局部变量没有帮助。编译器可以应用优化规则,可以使用其他“pragma”指令来操作对齐。
答案 4 :(得分:0)
填充和对齐问题仅对结构而非局部变量有影响,因为编译器可以按照所需的顺序放置局部变量。至于为什么它在结构中很重要 -
许多C编译器将通过在它们之间插入填充字节来对齐struct成员。例如,如果你有一个结构S {int a; char b; int c; char d; int;并且目标硬件要求int在4字节边界上对齐,然后编译器将在b和c之间以及d和e之间插入三个填充字节,每个实例浪费6个字节的内存。另一方面,如果成员按顺序ac,e,b,d,那么它将在末尾插入两个字节的填充(因此S的整体大小是4的倍数,因此成员将在数组中正确对齐),每个实例只浪费2个字节。规则是非常平台和编译器特定的;一些编译器可能会重新排列成员以避免填充,有些编译器可以扩展来控制填充和对齐规则,以防您需要二进制兼容性。一般来说,你应该只关心对齐,如果你是直接读取/写入结构,并且取决于它们具有相同的布局(通常是一个坏主意),或者你希望有很多实例和内存是非常珍贵的