C++中内存的五大区域
栈、(内存映射段)自由存储段、堆、(数据段)全局/静态存储区、(代码段)常量存储区
1.堆区
由程序员分配和释放,若不释放,程序结束时由会由OS回收
-
堆仅存在动态分配,没有静态分配的堆,所以在申请空间之后,在使用完后最好甚至必须手动对其进行释放,以避免内存泄漏
- 堆不手动free在如服务器等长期运行的项目中往往存在极大危害,因为在运行过程中申请的空间不断增多未释放,且程序不结束也不会强制释放空间 ,最终会导致系统崩溃
-
基本的malloc/realloc/free 函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部存储空间中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。
2.自由存储区
该概念由C++引入,除去该区域即C中的内存分区
- 其在形式上与堆十分相似,由new与delete手动进行内存申请与释放;
需要注意的是,其在某些时刻几乎可以等同于堆,而有时与其存在又有所不同,本文在结尾进行讨论
“Note that while the default global new and delete might be implemented in terms of malloc and free by a particular compiler, the heap is not the same as free store and memory allocated in one area cannot be safely deallocated in the other.” --Herb Sutter《exceptional C++》
3.栈
由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
- 栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
- 以下举例,栈上局部变量s分配的内存空间在离开作用域后已经被销毁,此时无法再使用s的地址对其原本指向的内容进行相关操作
char* f()
{
//s数组存放于栈上
char s[4] = {'1','2','3','0'};
return s; //返回s数组的地址,但程序运行完s数组就被释放了
}
int main()
{
char *s;
s = f();
printf ("%s", s); //打印出来乱码。因为s所指向地址已经没有数据
}
-
但值得一提的是,指针和数组在指向字符串时,还存在较大的区别,关于C/C++中数组与指针指向字符串的特性不同处的分析具体可以看看这篇博客http://t.csdn.cn/pTOPZ
-
由此我们不难理解,对于返回值为指针类型的函数,除非其指针的地址指向静态区或常量区或堆区,否则都会其打印值都会是随机值
-
注意:不是要求指针存储在静态区/常量区/堆区,局部指针变量除static修饰,其余指针都存储在栈上,必须的是指针指向内容存在静态区/常量区/堆区,其指向内容不会自动销毁,外部通过同一地址仍能调用
PS:对于 char* arr = “hello world”; //字符常量 该行代码c++中无法直接赋值,因为const char*->char*权限放大;但c中允许如此赋值
//指针指向内容存在常量区/静态区
char* test()
{
char* arr = "hello world"; //字符常量 c++中无法直接赋值,因为const char*->char*权限放大;但c中允许如此赋值
//static char arr[] = "hello world!";
return arr;
}
void test_()
{
char* s = test();
printf("s = %s\n", s);
}
int main()
{
test_();
return 0;
}
——————————————————————————————————————————————————————————————————————————————————————————————
//指针指向对象存在堆区
char *getstring()
{
char *p;
p = (char*)malloc(100);
strcpy(p, "hello");
return p;
}
void test()
{
char *ret = getstring();
printf("%s\n", ret);
free(ret);
ret = NULL;
}
int main()
{
test();
return 0;
}
4.全局/静态存储区
存储全局变量、静态变量,全局区的数据在程序结束后由OS释放
-
没什么疑惑点的点,存储全局变量、static修饰的所有变量、const修饰的全局变量;
在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。 -
其中BSS存储未初始化变量,DATA存储已初始化变量
5.(代码段)常量区
用以存储可执行代码的二进制代码与只读常量,在程序结束后由OS释放
6.堆与自由存储区的关系
表面上似乎明显的区别在于:malloc在堆上分配的内存块,使用free释放内存,而new所申请的内存则是在自由存储区上,使用delete来释放。
但再进一步思考,大部分编译器new与delete的底层默认实现方式还是malloc和free,由此看来似乎堆和自由存储区又没有区别了,或者说自由存储区是基于堆实现的?
但是C++还存在operator重载的语法,基于此,程序员可以通过operator new/delete用自己希望的方式来实现,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。
- 《exceptional C++》中如此描述
- 自由存储区(free store)是C++两个动态内存区域之一,使用new和delete来予以分配和释放。在自由存储区(free store)中,对象的生存周期可以比存放它的内存区的生存周期短;这也就是说,我们可以获得一片内存区而不用马上对其进行初始化;同时,在对象被销毁之后,也不用马上收回其占用的内存区。在对象被销毁而其占用的内存区还未被收回的这段时间内,我们可以通过void*型的指针访问这片区域,但是其原始对象的非静态成员以及成员函数(即使我们知道了它们的地址)都不能被访问或者操纵。
- 堆(heap)区是另一个动态存储区域,使用malloc、free以及他们的变形体来进行分配和回收。要注意,虽然在特定的编译器里缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,但是堆(heap)与自由存储区(free store)是不同的——在某一个区域内被分配的内存不能在另一个区域内被安全的回收。堆(heap)中被分配的内存一般用于对类对象进行placement-new的构造和explicit的析构。堆中对象的生存周期与自由存储区(free store)中的类似。
实际上在C++标准草案中关于这两种区域是否有联系的问题一直很谨慎地没有给予详细说明,而且特定情况下new和delete是按照malloc和free来实现,但这两种内存区域的访问与运作方式不完全同,所以也不应该被完全当成一样的东西来使用。