C语言:数组堆栈中的内存对齐如何发生

问题描述 投票:2回答:1

全部,我对C语言中的数组的内存对齐有一个有趣的问题。我的操作系统是32位Ubuntu,我使用gcc -S -fno-stack-protector选项进行编译。

代码:

char array1[5] = "aaaaa";
char array2[8];
array2[0] = 'b';

汇编代码:

pushl %ebp
move %esp, %ebp.         # esp and ebp are pointing to the same words
subl    $16, %esp        # move esp to lower 16
movl    $1633771873, -5(%ebp)       # input "aaaa"
movb    $97, -1(%ebp).              # input 'a'
movb    $98, -13(%ebp)              # input 'b'
movl    $0, %eax
leave

我有GDB检查内存,

[%ebpefe8

[%espefd8

[&buf1efe3

[&buf2efdb

在GDB中,我运行x/4bd 0xbfffefd8,它显示

0xbfffefd8:    9  -124   4  98

如果我运行x / bd 0xbfffefd8,则显示

0xbfffefd8:    9

如果我运行x / bd 0xbfffefdb,则显示

0xbfffefd8:    98

所以内存看起来像这样

## high address ##
?                       efe8 <-- ebb
97  97  97  97          efe4 
0  -80  -5  97(a)       efe0
0    0   0   0          efdc
9 -124   4  98(b)       efd8 <-- esp
^            ^
|            |
efd8       efdb

现在我的问题是:

  1. 为什么字符'b'(98)在efdb,而%espefd8?我认为“ b”也应位于efd8,因为它是4字节字的开头。此外,如果我继续从buf2开始向efdb填充更多的'b',它只能填充5'b',而不能填充8。为什么呢?那'\ 0'呢?

buf1发生了相同的事情,它从efe3开始,而不是efe0。这是什么对齐方式?这对我来说没有意义。

  1. 从汇编代码来看,它没有显示我从其他地方看到的16个对齐,像这样,
andl $-16, %esp     # this aligns esp to 16 boundary

何时显示[[andl命令,何时不显示?这很常见,所以我希望在每个程序中都能看到它。

从上面的汇编代码中,我看不到内存对齐。总是这样吗?我的理解是,汇编代码只是将高级代码(非常可读)解释为非常不可读的代码,但仍会转换准确的消息,因此char[5]不会解释为考虑内存对齐的方式。然后,内存对齐应在运行时发生。我对吗?但是GDB调试显示的代码与汇编代码完全相同。完全不对齐。

谢谢。

c x86 gdb stack memory-alignment
1个回答
0
投票
我在这里没有发现任何问题。

TLDR答案:char数组对齐到1个字节,编译器正确。

进一步挖掘。在我的64位计算机上,将GCC 7与-m32选项一起使用,我运行并调试了相同的代码,获得了相同的结果:

(gdb) x/4bd $esp+12 0xffffcdd4: 97 97 97 97 (gdb) x/4bd $esp+8 0xffffcdd0: 0 -48 -7 97 (gdb) x/4bd $esp+4 0xffffcdcc: 0 0 0 0 (gdb) x/4bd $esp+0 0xffffcdc8: 41 85 85 98

地址不同,当然可以。现在,让我尝试解释。首先,按预期将$esp对齐4个字节:

(gdb) p $esp $9 = (void *) 0xffffcdc8

到目前为止,很好。现在,由于我们知道char数组默认情况下使用

1

作为对齐方式,因此让我们尝试弄清楚在编译时发生了什么。首先,编译器看到array1[5]并将其放在堆栈上,但是由于它是5字节宽,因此将其扩展到了第二个双字。因此,第一个双字充满了“ a”,而仅使用了第二个双字的一个字节。现在,将array2[8]放置在array1[5]之后(或之前,具体取决于您的外观)。它在3个双字上扩展,以$esp指向的双字结尾。所以,我们有:

[esp + 0] <3 bytes of garbage>, 'b', [esp + 4] 0, 0, 0, 0, [esp + 8] <3 bytes of garbage>, 'a', [esp + 12] <4 times 'a'>.

如果在char[2]之后添加array2数组,您将使用$esp指向的相同双字看到它,并且从$esparray3[2]仍有1个字节的垃圾。

绝对允许编译器执行此操作。如果希望将char数组对齐为4字节(但是需要

good原因!),则必须使用特殊的编译器属性,例如:

__attribute__ ((aligned( value )))
© www.soinside.com 2019 - 2024. All rights reserved.