为何通过非默认构造函数创建的全局变量会被分配到.bss段?
关于全局对象在.data/.bss段分配的C++疑问
测试代码
test1.cpp(使用非默认构造函数)
// test1.cpp代码 class Derived { private: int non_const_int_val=1; public: Derived(int a) : non_const_int_val(a) { } } dev(0x2f2f2f2f); int main() { Derived derivedObj(0x55555555), derivedObj_copy(dev); return 0; }
test2.cpp(使用默认构造函数)
// test2.cpp代码 class Derived { private: int non_const_int_val=1; public: } dev; int main() { Derived derivedObj, derivedObj_copy(dev); return 0; }
现象对比
编译后通过objdump查看全局变量dev的段分配:
- test2.out中
dev位于.data段:
>> objdump -x test2.out | c++filt | grep -E -i "dev" 0000000000404028 g O .data 0000000000000004 dev
- test1.out中
dev位于.bss段:
>> objdump -x test1.out | c++filt | grep -E -i "dev" 000000000040115a l F .text 0000000000000015 _GLOBAL__sub_I_dev 000000000040402c g O .bss 0000000000000004 dev
疑问
- 如何解释这种特殊行为?
- 构造与初始化之间是否存在深层差异?(若我混淆了核心概念请见谅)
- 希望提供有用的参考资料。
解答
1. 行为差异的原因
这是C++中静态初始化和动态初始化的区别导致的:
- test2.cpp中,
dev使用默认构造函数,且类成员non_const_int_val的类内初始值是编译期常量(1)。这种情况下,编译器可在编译阶段直接完成dev的初始化,把最终值写入.data段——这属于常量初始化(静态初始化的一种),无需运行时执行构造函数。 - test1.cpp中,
dev调用带参数的构造函数,即便参数是常量,构造函数的执行仍属于动态初始化:编译器无法在编译阶段直接确定对象的内存最终值,会生成全局构造函数(即你看到的_GLOBAL__sub_I_dev),在程序启动(main函数执行前)时调用该构造函数初始化dev。因此dev的内存先被分配到.bss段(初始值为0),运行时构造函数执行后才会被设置为目标值。
2. 构造与初始化的深层差异
在C++中:
- 初始化是宽泛概念,包含所有为对象设置初始值的操作,比如类内成员初始化、列表初始化、构造函数初始化等。
- 构造特指通过构造函数创建并初始化对象的过程,是初始化的一种方式。
核心差异体现在执行时机:
- 静态初始化(含常量初始化)在编译/链接阶段完成,对象初始值直接写入可执行文件的
.data(非0值)或.bss(0值)段,运行时无需额外操作。 - 动态初始化(如调用非默认构造函数、使用非编译期常量初始化)必须在程序运行时(main前的全局构造阶段)执行,对象先分配到
.bss段(初始化为0),再由构造函数覆盖为目标值。
3. 参考资料
- C标准中
[basic.start.static]和[basic.start.dynamic]章节:明确了静态初始化、动态初始化的划分规则与执行时机(C11及以后版本)。 - GCC官方文档中关于全局对象初始化的说明:涵盖GCC对两种初始化的实现逻辑,以及
-fno-static-initialization等编译选项的调整作用。
内容的提问来源于stack exchange,提问作者Y.H.Qian
相关产品推荐
相关产品推荐

