C++中对象进入作用域时,栈中究竟存入了哪些内容?
栈上对象的底层存储细节
1. 栈上对象的本质
栈上的对象就是一块连续的栈内存区域,大小等于对象的实例总大小(成员变量的大小之和加上编译器为内存对齐添加的填充字节)——成员函数不占实例空间。进入作用域时,栈指针(如x86的ESP、x86_64的RSP)向下移动对应长度,完成空间分配;退出作用域时栈指针上移,自动回收空间。
2. 成员变量的存储规则
- 基础类型与指针:成员变量会按类定义的顺序存储,但编译器会做内存对齐优化。比如32位系统中,
int(4字节)后紧跟double(8字节)时,编译器会在int后填充4字节空白,确保double的起始地址是8的倍数,让CPU能高效访问。 - 嵌套对象成员:如果成员是完整对象(比如示例里的
sub_obj),它的所有成员会直接嵌入当前对象的栈空间,相当于把SomeOtherClass的内存布局完整“嵌入”到MyClass的对应位置,同样遵循内存对齐规则。 - 指针成员:像
some_map这种指针,栈上只存一个指针值(32位4字节,64位8字节),指向堆上实际的std::unordered_map对象,栈里不会放这个map的实际数据。
3. 成员函数的底层处理
成员函数不会存在对象的栈空间里:
- 非虚成员函数:整个类的所有实例共享同一份函数代码,程序启动时就被加载到代码段(Text Segment)。调用时,编译器会把对象的指针/引用作为隐式参数(即C++的
this指针)传递给函数,函数通过this访问对象成员,不需要在栈里存储函数地址。 - 虚函数:类会生成一张虚函数表(vtable,存在只读数据段),每个对象的栈空间开头会有一个指向虚表的指针(vptr)。调用虚函数时,先通过
vptr找到虚表,再从表中取出对应函数地址执行。
4. 示例场景的栈内容分析
当调用function(const MyClass& obj)时,栈上存入的是**obj引用对应的地址值**(32位4字节,64位8字节)——因为引用本质是指针的语法糖。如果是传值调用(function(MyClass obj)),栈上会存入MyClass的完整实例:
- 按内存对齐后的顺序存放
a、填充字节(若需要)、b、some_map指针、sub_obj的所有成员(含对齐空间)。 - 注意:
DoSomething的代码不在栈上,调用它时只是跳转到代码段的对应地址执行,同时传入当前对象的this指针。
拓展阅读
- 《深入理解计算机系统》:详细讲解内存布局、栈的工作机制、编译器内存对齐策略。
- 《C对象模型》:专门剖析C类对象的底层存储、成员函数调用逻辑、虚表实现细节。
- 《汇编语言》:通过汇编代码观察函数调用时的栈变化,理解参数传递和对象存储的底层过程。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

