一、一个C/C++程序占用的内存分为以下几个部分:
-
栈区(Stack):
-
由编译器自动分配释放,其操作方式类似于数据结构中的栈,用于存放函数的形参、返回地址、返回数据,局部变量的值等。(函数形参、局部变量、返回地址、返回数据)
-
-
堆区(Heap):
-
一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。它与数据结构中的堆完全不同,其存储方式类似于链表(malloc、calloc、realloc、free)
-
-
全局(静态区)(Static):
-
程序结束后由系统释放,用于存放全局变量、静态变量,已初始化的全局变量和未初始化的静态变量放在一块区域,未初始化的全局变量和未初始化的静态变量放在相邻另一块区域。(全局变量、Static)
-
-
文字常量区
-
程序结束后由系统释放,用于存放常量字符串等
-
-
程序代码区
-
存放函数体(类成员函数和全局函数)的二进制代码
-
示例:
int a = 10; //全局变量---全局(静态)初始化区
int i; //全局变量---全局(静态)未初始化区
main()
{
int b; //局部变量---栈
char *p = “123”; //局部变量---栈(123\0存在文字常量区,p在栈上)
char arr[10] = "def"; //栈
static int c = 1; //静态变量---全局(静态)初始区
p =(char*)malloc(10*sizeof(char)); //动态分配的空间在堆区
strcpy( p, "123" ) ; //123\0存在常量区,编译器可能将它与str指向的“123\0”优化成一个地方
}
char s1[] = "abcdef"; //s1 在静态区,“abcdef”无需额外存放,存在数组s1内部
const char *p1 ="abcd"; //p1在静态区,“abcdef”在常量区,可以通过指针p1获取到“abcd”
main()
{
char s2[] ="abc"; //s2在栈区,“abc”在常量区,编译器一般不会把串中的字符编译成立即数,
一个一个装进数组s2
char *p2 ="abc"; //p2在栈区,“abcdef”在常量区,可以通过指针p2获取到“abcd”
char *p3 = (char*)malloc(3); //p3在栈区,malloc动态开辟的空间在堆区,
栈区的p3存储了堆区所开辟空间的首地址
strcpy(p3,"abc"); //字符串的赋值不能用“=”,需要用strcpy函数
//p3="abc"; error //p3在栈区,内部存储的是开辟空间的首地址,而非开辟的整个空间,
不能将字符串常量赋给它
}
二、申请后系统的处理方式
栈:只要栈剩余空间大于所申请的空间,系统将为程序提供内存,否则将报错(栈溢出)
堆:操作系统有一个记录空闲地址的链表,当系统收到程序申请时,会遍历该链表,寻找第一个空间大于所申请空间大小的堆结点,然后从空闲链表中删除该结点,并将其空间分配给程序。对于大多数系统,会在其空间首地址处记录该空间大小,以便于free/delete时释放正确的内存空间,另外,程序获取到的空间可能大于所申请空间,多于部分系统会重新释放至空闲链表中。
三、申请大小限制
栈:在window中,栈由高地址向低地址扩展的一块连续的内存区域,window中栈的大小是2M(不一定),如果申请空间大于栈剩余空间时将会提示stack overflow(栈溢出),因此栈能获取的空间较小。
堆:堆是由低地址向高地址扩展的一块不连续的内存区域,堆的大小受限于计算机中有效虚拟内存,且系统是用链表存储空闲内存地址,空间可不连续,因此堆的存储比较灵活,也比较大。
四、申请效率
栈:栈内存分配运算内置于处理器指令集中,效率很高,但分配的内存容量有限,程序员无法控制
堆:由动态内存开辟,一般速度比较慢,容易产生内存碎片,使用结束后需程序员自行释放内存空间