为什么不使用堆栈内存?

Why is stack memory allocated when it is not used?

提问人:Dr. Gut 提问时间:1/8/2020 最后编辑:curiousguyDr. Gut 更新时间:1/9/2020 访问量:497

问:

请看以下示例:

struct vector {
    int  size() const;
    bool empty() const;
};

bool vector::empty() const
{
    return size() == 0;
}

生成的汇编代码(通过 clang,经过优化):vector::empty

push    rax
call    vector::size() const
test    eax, eax
sete    al
pop     rcx
ret

为什么要分配堆栈空间?它根本没有使用。可以省略 和。MSVC 和 gcc 的优化版本也为此函数使用堆栈空间(参见 godbolt),因此一定是有原因的。pushpop

C++ 代码生成 调用约定 ABI 堆栈分配

评论

7赞 dan04 1/8/2020
您是否考虑了隐式参数?this
1赞 Dr. Gut 1/8/2020
@Bob__:没有。我为什么要这样做? 未在示例中定义,以模拟它不是内联的。vector::size()
1赞 Bob__ 1/8/2020
那么,编译器如何优化它不知道的东西呢?
1赞 Dr. Gut 1/8/2020
@Bob__:我认为,了解 的实现与分配或不分配堆栈帧无关。在它只是被召唤,不管它是什么。vector::size()vector::empty()empty()
1赞 Bob__ 1/8/2020
好吧,您正在调用一个返回某些内容的函数,您需要为此留出空间(如果您不知道更好的话)。

答:

12赞 geza 1/8/2020 #1

它分配堆栈空间,因此堆栈是 16 字节对齐的。这是必需的,因为返回地址需要 8 个字节,因此需要额外的 8 字节空间来保持堆栈 16 字节对齐。

对于某些编译器,可以使用命令行参数来配置堆栈帧的对齐方式。

  • MSVC文档指出堆栈始终是 16 字节对齐的。任何命令行参数都无法更改此设置。godbolt 示例显示,从函数的开头减去 40 个字节,这意味着其他因素也会影响这一点。rsp
  • clang:-mstack-alignment 选项指定堆栈对齐方式。默认值似乎是 16,尽管没有记录。如果将其设置为 8,则堆栈分配 ( 和 ) 将从生成的汇编代码中消失。pushpop
  • gcc:-mpreferred-stack-boundary 选项指定堆栈对齐方式。如果给定值为 N,则表示 2^N 字节的对齐。默认值为 4,表示 16 个字节。如果将其设置为 3(即 8 字节),则堆栈分配 ( 和 for ) 将从生成的汇编代码中消失。subaddrsp

godbolt 上查看。

评论

0赞 1/8/2020
这就是为什么 c++ 大师、专家们总是警告:将结构/类成员按最长/最大大小到最小的顺序排列......只有这样,它才会正确有效
0赞 Dr. Gut 1/9/2020
@geza:谢谢。我为其他两个编译器做了一些研究,并把它写到你的答案中。喜欢吗?
2赞 geza 1/9/2020
@Dr.Gut:谢谢,你让答案变得更好、更完整。请注意,堆栈对齐通常记录在系统的 ABI 中(例如,对于某些系统,以下是文档:github.com/hjl-tools/x86-psABI/wiki/X86-psABI)。
0赞 Dr. Gut 1/9/2020
@geza:谢谢。