gpt4 book ai didi

C语言 : how memory alignment happened in the stack for array

转载 作者:行者123 更新时间:2023-12-04 10:42:27 24 4
gpt4 key购买 nike

所有,我有一个关于 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 0xbffefd8,它会显示
0xbfffefd8:    9

如果我运行 x/bd 0xbffefdb,它会显示
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

现在我的问题是:
  • 为什么字符 'b'(98) 在 efdb , 而 %espefd8 ?我认为 'b' 也应该在 efd8 ,因为它是 4 字节字的开始。此外,如果我继续向 buf2 填充更多“b”从 efdb 开始,它只能填5'b',不能填8。怎么会? '\0' 呢?

  • 同样的事情发生在 buf1 , 从 efe3 开始,不是 efe0 .这是一种什么样的对齐方式?这对我来说没有意义。
  • 从汇编代码来看,它没有显示我从其他地方看到的 16 对齐方式,就像这样,
  • andl $-16, %esp     # this aligns esp to 16 boundary

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

    从上面的汇编代码中,我看不到内存对齐。它总是真的吗?我的理解是汇编代码只是将高级代码(非常易读)解释为不太易读的代码,但仍然转换了确切的消息,所以 char[5]不被解释为考虑内存对齐的方式。那么内存对齐应该发生在运行时。我对吗?但是 GDB 调试显示与汇编代码完全相同。根本没有对齐。

    谢谢。

    最佳答案

    我看这里没有任何问题。 TLDR 答案:char 数组对齐到 1 个字节 ,编译器是对的。

    再挖一点。在我的 64 位机器上,使用带有 -m32 选项的 GCC 7,我运行和调试了相同的代码,我得到了相同的结果:

    (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 个字节宽,因此将其扩展为第二个双字。因此,第一个 dword 充满了“a”,而仅使用了第二个 dword 的 1 个字节。现在, array2[8]紧随其后(或之前,取决于您的外观) array1[5] .它在 3 个双字上扩展,以 $esp 指向的双字结尾.

    所以,我们有:
    [esp +  0] <3 bytes of garbage /* no var */>, 'b' /* array2 */,
    [esp + 4] 0x0, 0x0, 0x0, 0x0, /* still array2 */
    [esp + 8] <3 bytes of garbage /* still array2 */>, 'a' /* array1 */,
    [esp + 12] 'a', 'a', 'a', 'a', /* still array1 */.

    如果您添加 char[2] array2 之后的数组您将使用 $esp 指向的相同双字看到它并且仍然有来自 $esp 的 1 个字节的垃圾给您的 array3[2] .

    绝对允许编译器这样做。如果您想要您的 char数组要以 4 字节对齐(但您需要一个很好的理由!),您必须使用特殊的编译器属性,例如:
    __attribute__ ((aligned(4)))

    关于C语言 : how memory alignment happened in the stack for array,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59865199/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com