我有一段非常简单的代码:

// test.c
int main(){
  int a = 0;
  char b[10];
  int c = 0;

  return 0;
}


用gcc(6.2.1)编译:

$ gcc -g -o test test.c


并使用gdb进行分析: >
有原因吗?

评论

可以使用-O0进行编译(禁用优化)吗?

不幸的是结果相同。

#1 楼

我最好的猜测:内存对齐。

C中的整数是4个字节,一个char 1个字节。因此,您的声明如下:4B&1x10 = 10B&4B。这个顺序意味着10B不会在不浪费空间的情况下以两个内存边界的幂对齐。当阵列在16B / 32B / 64B上对齐时,在x86机器上的数据访问速度更快-BTW 64B是高速缓存行的大小)。

因此,编译器发现将前两个4B变量0x --- ECCB8和0x --- ECBC对齐(相差4字节)更为理想。然后为阵列选择最接近的对齐内存位置0x --- ECC0(0x --- ECC0-0x --- ECBC = 4B);末尾的零表示该地址可被2的整数除。如果您忘记了7F ...,然后将ECC0转换为10,您将得到60608,可以将其除以64、32、16。

您应该阅读Ulrich Drepper关于内存的文档,并查看Agner Fog的软件优化手册。金矿!

附录:

如果您想尝试内存对齐,建议您检查一下编译器如何在C中执行数据布局重组和结构填充。填充意味着编译器有时添加额外的字节以达到两个边界的幂。例如,假设您有一个包含以下代码的代码:此声明包含三个4B变量= 12B,而不是2的幂。该编译器很可能会添加另一个4B将其与2边界的最接近幂次对齐:16B。因此,编译后,您的声明将如下所示:

    typedef struct { int x; int y; int z; } point3D;


关于您的评论,我建议尝试以下方法:

    typedef struct {int x; int y; int z; char[4] padding; } point3D;


malloc有许多变体(以及普通malloc的技巧),可以使内存在看起来适合代码的边界上对齐。
请记住,二进制程序有两种类型的内存:使用malloc类型的函数操作的堆和由编译器处理的堆栈(函数参数,寄存器保存,局部变量等)。 )。可以控制堆栈使用和数据对齐的唯一方法是自己使用汇编代码进行操作,或者,如果编译器可以处理,则使用编译器指令和参数。

您的代码向main分配了三个局部变量。因此,它们将在堆栈中分配,并且假定您的代码通过编译器,它将执行所有必要的操作以使用启发式分析(优化的预测性内存位置)对齐这些变量。如果编译上面提供的代码,您会注意到地址不相似(堆与栈)。 0x7FF--对于堆栈变量和0x000 ---?用于堆变量。您可以使用这些功能,自己动手做很多事情。如有疑问,请参阅文档:英特尔软件优化手册,阿格纳(Agner),...更好的对齐要求:缓存行大小,内存分段和BSI(基本+比例*索引;比例只能采用以下值:1、2、4和8)。

我希望本附录能使您更清楚一些。

评论


有趣的是,我尝试更改数组大小以使其大小为2的幂。但是结果保持不变。 (感谢链接)

– nobe4
17年1月20日,11:50

好吧,数组大小不是关键,而是对齐。检查更新。

– Yaspr
17年1月20日在13:08

“ C中的整数是4个字节,一个char是8个字节。”这是不正确的。在中,char类型的位数为8,而不是64。char中的字节数永远不会超过int中的字节数。

– julian♦
17年1月22日在16:29



b是指向char数组的常量指针,因此b在x86-64上为8个字节,b [0]为1个字节。

– julian♦
17年1月22日在16:43



我错了。我想说8位= 1个字节。感谢您指出。固定!

– Yaspr
17年1月22日在23:11