内存分配时间
例1 局部变量
void Test1()
{
…………
int buf[1024 * 1024];
…………
}
在windows下,由于栈限制为1M,上面代码可能会栈溢出,因此通过单步调试我们可以知道,局部变量是在进入函数时分配栈内存。
编译器在编译的过程中,遇到函数调用时,会加入几条汇编指令。这些汇编指令的作用是:
1 分配一段栈空间,用于存放被调函数的参数和局部变量。
2 call被用函数。
3 当被调函数返回时释放掉这段分配栈空间。
例2 全局/静态变量
class B
{
public:
B(){cout << "class B construct" << endl;}
~B(){}
};
class A
{
public:
A(){cout << "class A construct" << endl;}
~A(){}
static B b;
}
B A::b;
void test()
{
static A a;
}
int main()
{
test();
test();
return 0;
}
上面代码运行,main函数前,会调用B的构造函数,
test函数中第一次static A a时会调用一次A的构造函数
引用自https://segmentfault.com/q/1010000004157283
对于C语言的全局和静态变量,不管是否被初始化,其内存空间都是全局的;如果初始化,那么初始化发生在任何代码执行之前,属于编译期初始化。由于内置变量无须资源释放操作,仅需要回收内存空间,因此程序结束后全局内存空间被一起回收,不存在变量依赖问题,没有任何代码会再被执行!
C++引入了对象,这给全局变量的管理带领新的麻烦。C++的对象必须有构造函数生成,并最终执行析构操作。由于构造和析构并非分配内存那么简单,可以说相当复杂,因此何时执行全局或静态对象(C++)的构造和析构呢?这需要执行相关代码,无法在编译期完成,因此C++标准规定:全局或静态对象当且仅当对象首次用到时才进行构造,并通过atexit()来管理对象的生命期,在程序结束之后(如调用exit,main),按FILO顺序调用相应的析构操作!
总结:
全局变量、文件域的静态变量和类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。
关于全局/静态变量的内存分配问题,上面总结里已经说的很详细了。
但是我有两点不同看法:
- 局部静态变量的内存分配应该和全局变量一样,在main函数之前分配好,在第一次调用的时候只是初始化。
- 如果注释掉 B A::b(即不对b进行显式初始化),结果就不会调用B的构造函数。因此对于类的静态成员来说,static B b只是声明,B A::b才是定义,而类中如果是B b则是声明且定义。
例3 常量
引用自https://blog.youkuaiyun.com/xiazhiyiyun/article/details/71969618
小结
- 对于const全局常量,如果初始值是字面值常量,一般会存放在常量表中,编译器在编译过程中就会直接使用常量表中的值来进行语句的操作。如果经过优化,可能会使得直接使用常而放弃存储在常量表中。
- 对于编译器没有办法处理的初始值常量,一般就需要分配到内存空间,然后等待运行时进行赋值,取值时候也是从内存空间中提取。
- 对于非static 的 const局部变量(如函数中),由于其是局部变量,符号表中将不存在其值,如果需要内存空间也是在栈中进行分配。根据编译器的优化,以及const 变量初始值的不同来决定是否需要分配内存空间。
- 对于static 的 const局部变量(如函数中),会根据编译器的优化能力,以及初始值,来决定其是不进行存储,还是直接存储在常量符号表,或者是栈中等等均有可能。
- 对于第三点和第四点可以字迹尝试验证。