【C语言】内存分布详解

一、一个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(栈溢出),因此栈能获取的空间较小。

堆:堆是由低地址向高地址扩展的一块不连续的内存区域,堆的大小受限于计算机中有效虚拟内存,且系统是用链表存储空闲内存地址,空间可不连续,因此堆的存储比较灵活,也比较大。

四、申请效率

栈:栈内存分配运算内置于处理器指令集中,效率很高,但分配的内存容量有限,程序员无法控制

堆:由动态内存开辟,一般速度比较慢,容易产生内存碎片,使用结束后需程序员自行释放内存空间

你可能感兴趣的:(C语言)