【问题标题】:Are stack variables allocated contiguously in virtual memory?堆栈变量是否在虚拟内存中连续分配?
【发布时间】:2015-09-16 06:21:01
【问题描述】:

我正在用 C 语言编译以下程序:

void function(int a, int b, int c) {
char buffer1[11];
char buffer2[3];
char buffer3[1];
char buffer4[1];
}

void main() {
function(1,2,3);
}

使用命令:

gcc -m32 -fno-asynchronous-unwind-tables -fno-stack-protector -S -o example1.s example1.c

下面是我得到的输出:

    .file   "example1.c"
    .text
    .globl  function
    .type   function, @function
function:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $16, %esp
    leave
    ret
    .size   function, .-function
    .globl  main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   $3
    pushl   $2
    pushl   $1
    call    function
    addl    $12, %esp
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

subl $16, %esp 行表示堆栈上分配了 16 个字节。

但是根据不同的教程,我可以看到堆栈空间通常以 4 个字节的块分配。

为什么我看到的行为不同?

我运行的是 64 位 Ubuntu 系统:-

vendor_id   : GenuineIntel
cpu family  : 6
model       : 58
model name  : Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
stepping    : 9
microcode   : 0x1b
cpu MHz     : 1202.636
cache size  : 3072 KB
physical id : 0
siblings    : 4
core id     : 0
cpu cores   : 2
apicid      : 0
initial apicid  : 0
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
bugs        :
bogomips    : 4988.46
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

谁能帮我理解一下。

我提到了以下问题:How is memory allocated for stack variables? 但我找不到具体的答案。

编辑: 1)我想了解整个堆栈是否必须与 4/8/16 字节边界对齐,或者每个堆栈变量都必须对齐。 2) 可以组合成一个栈内存字的局部变量的类型有限制吗?

【问题讨论】:

  • 不确定你的问题是什么,你声明了 16 个字节的局部变量并分配了 16 个字节,这是 4 的倍数。是的,编译器可能会按照它认为合适的方式排列局部变量和对齐方式char1,因此它们可以连续打包。
  • @Jester 我已经编辑了这个问题。可以请您现在检查吗
  • 调用约定可能需要或需要,但我认为指令集不需要对齐。这些对齐有助于提高性能,arm 的 eabi 也需要它,但归根结底,它是特定的编译器、版本、命令行选项,无论什么决定了生成的代码。而且没有任何两个编译器必须做同样的事情的理由。
  • 堆栈保持对齐的原因如下:1) 对齐的内存访问速度更快 2) 某些指令需要对齐内存操作数。 Vars(甚至 1 字节 var)通常分配为 4/8/16 字节的倍数,原因如下:1)使用本机字长(4 或 8 字节)更快(因为它有助于避免错误的依赖关系),甚至对于较小的数据类型 2) 从对齐的堆栈指针开始并仅减去对齐的数量来保持堆栈对齐。架构本身并不关心堆栈对齐
  • 该语言几乎没有说明如何分配局部变量。你问的是编译器特定的。

标签: c linux memory assembly virtual-memory


【解决方案1】:

答案是堆栈变量不必连续分配。在许多情况下,它们不太可能是。

如果你有

{
   struct
   {
      int a ;
      char b ;
      double c ;
      char d ;
   } x, y, z; 
}

我敢打赌大多数编译器不会使 x、y 和 z 连续。

无论如何,您所要求的完全取决于系统和编译器。编译器根本不需要将自动变量放入堆栈。

【讨论】:

  • gcc 将相似类型变量分配在一起的任何具体原因?
  • 在某些系统上有性能优势,因为它需要多个周期来检索未对齐的数据。在其他系统上,禁止未对齐的访问。
【解决方案2】:

"1) 我想了解整个堆栈是否只需与 4/8/16 位边界对齐,或者每个堆栈变量都必须对齐。”

这是一个特定于机器和编译器的问题。通常,变量根据它们的大小和 CPU 字长对齐。在 32 位 CPU 上在 32 位边界上对齐 32 位变量有很大的优势,但在 8 位 CPU(如 8051)上对齐 32 位变量没有优势。 32 位 CPU如果它们在 32 位边界(0x??0、0x??4、0x??8、0x??c)上对齐,则只能将 32 位值作为 32 位值处理。

2) 可以组合成一个栈内存字的局部变量的类型有限制吗?

除了对齐之外没有限制。

【讨论】:

  • 这里我看到的模式是,如果我将字节总数增加到 17,那么 32 个字节会分配到堆栈上。这让我得出结论,对于 64 位系统中的 gcc,当使用 -m32 标志编译时,局部变量如何对齐并不重要,但总堆栈大小将是 16 的倍数。我的理解是否正确?
  • 还有什么特别的原因为什么带有 -m32 标志的 gcc 没有遵循这种将局部变量对齐到 4/8/16 位边界的方法?
  • @nkvp 在 Windows 和 Linux 上,ABI 都要求堆栈在 16 个 byte 边界上对齐。在 32 位机器上没有这样的要求,但堆栈通常是对齐的(例如我的 gcc 版本仍然在 16 字节边界对齐堆栈)。 注意 你声明了四个数组,数组必须是连续的。一个包含 1 个 char 元素的数组和一个包含 16 个 char 元素的数组(在这些机器上)会占用一些空间。此外,当堆栈中“分配”了 16 个字节时,这将为四个 32 位 var 或两个 64 位 var 等腾出空间。
  • @nkvp 对齐的不是本地变量,而是堆栈指针。后者通常暗示前者,但并不总是
  • 如果有,为什么gcc不遵循这种方法?我们可以指定任何标志来使其遵循这种方法吗?
猜你喜欢
  • 2014-02-17
  • 2023-04-08
  • 2013-01-24
  • 2011-07-02
  • 2020-02-19
  • 1970-01-01
  • 2021-04-23
  • 1970-01-01
  • 2015-09-11
相关资源
最近更新 更多