C++中的全局变量存储在堆栈、堆内存还是两者都不是?

43
起初我非常确定正确答案应该是“没有一个”,因为全局变量存储在数据内存中,但后来我找到了罗伯特·拉福尔(Robert Lafore)的这本书,名为《C++面向对象编程》,它明确指出,根据C++标准,全局变量存储在堆上。现在我感到非常困惑,无法弄清楚对所提出的问题的正确答案。
为什么全局变量会存储在堆上?我错过了什么吗?
编辑:书籍链接 - 书页205 / Google Drive页231

1
“而且它明确指出,根据C++标准,全局变量存储在堆上”,我真的怀疑它是否清楚地表明了这一点。请发表相关文本。 - user2100815
2
@NeilButterworth 我已经编辑了问题并添加了书籍链接,如果您想在那里查找。无论如何,它说:“如果您熟悉操作系统架构,则可能会有兴趣了解局部变量和函数参数存储在堆栈上,而全局和静态变量存储在堆上。” - Edoardo Meneghini
4
这本书是错误的。它们并未存储在堆上,而是存储在数据段或.bss段中。 - David G
8
当然不是真的。C++并没有明确说明它们被存储的位置,但据我所知,没有任何一种实现会将它们存储在堆上。 - user2100815
2
好的,各位,感谢你们的帮助。那真的让我很困惑。 - Edoardo Meneghini
2个回答

62
这里是书上第205页的内容:
如果你熟悉操作系统架构,你可能会对本地变量和函数参数存储在堆栈上,而全局和静态变量存储在堆上感兴趣。
这绝对是书中的错误。首先,应该根据存储持续时间来讨论存储方式,就像C++标准一样:“堆栈”是指自动存储持续时间,而“堆”是指动态存储持续时间。 “堆栈”和“堆”都是分配策略,常用于实现具有各自存储持续时间的对象。
全局变量具有静态存储持续时间。它们存储在与“堆”和“堆栈”不同的区域中。通常,全局常量对象存储在“代码”段中,而非常量全局对象存储在“数据”段中。

5
“栈”和“堆”与存储持续时间有关,但它们不是指这个。栈是一种适用于自动存储持续时间对象的分配策略,而堆则适用于动态存储持续时间对象的分配。当然,对于静态存储持续时间对象,两者都不太适合。 - aschepler
我知道这对于elf格式是有效的,但对于其他格式也有效吗? - AdrianRK
1
@AdrianRK 你是指“数据”和“代码”段吗?大多数格式都有类似作用的段,尽管名称通常不同(例如,“代码”段通常称为“文本”段)。 - Sergey Kalinichenko
许多C工具链都有两个全局变量段:一个(在Unix领域传统上称为“数据”)用于具有编译时常量初始化程序的全局变量,另一个(传统上称为“BSS”)用于在运行时初始化的全局变量。可执行文件包含“数据”段中变量的实际初始值,但对于BSS,它只需要说明要使其多大即可。 - Solomon Slow
1
@DarsenLu 即使是非常量的全局和静态对象(与全局变量相同,但名称可见性较低),也存储在堆栈之外。这就是为什么您的全局变量没有溢出堆栈的原因。 - Sergey Kalinichenko
显示剩余2条评论

1

这本书中相关的引文在第205页:

如果你熟悉操作系统架构,你可能会对以下事实感兴趣:局部变量和函数参数存储在堆栈上,而全局和静态变量存储在堆上。

这是错误的,因为通常全局变量存储在静态内存段,如.data或.bss,并且这些段在程序启动时进行分配,而不是通过例如std::malloc动态分配。

此外,C++标准对堆栈或堆没有任何规定;C++标准只关注存储期限,即对象的存储何时获得和释放。而分配策略是实现在需要时分配存储的方式。以下是这两个概念的对应关系:

存储持续时间 常见分配策略 常见优化
静态存储持续时间
例如全局int
局部static int
静态内存段,
例如.data.rodata.bss
无内存,仅寄存器
用于全局常量,
特别是constexpr
线程存储持续时间
任何thread_local
TLS(线程本地存储)
自动存储持续时间,
即局部int
函数参数等
堆栈内存 经常被优化为仅使用寄存器,
特别是对于像intfloat这样的小类型
动态存储持续时间,
即通过std::mallocnew等获得的存储
堆内存 如果可能,堆消除,
即使用寄存器代替
(需要强大的编译器,
优化通常无法应用)
当然,我还没有提到的一个常见优化是,如果编译器可以确定任何对象都没有被使用,它也可以完全删除它。 在这种情况下,全局变量不会被存储在任何地方,它只是消失了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接