阅读耗时大约1分半

C++ 在执行的时候,内存大体分了4个区域

  • 代码区 :存放函数体的二进制代码,由操作系统进行管理
    • 共享的,目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
    • 只读的
  • 全局区: 存放全局变量和静态变量以及常量
  • 栈区: 由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
    • 主要是利用new在堆区开辟内存

分区的目的也是为了更好的管理它们的生命周期

程序运行前

在程序编译后,生成exe 可执行程序,未执行该程序前分为两个区域
代码区

  • 存放CPU执行的机器指令
  • 代码区共享,对于频繁被执行的程序,只需要在内存中有一份代码即可。例如双击exe 多次,它在内存中只有一份代码,不会放多份。
  • 代码区只读,可以防止程序意外修改它的指令。第一次执行exe,修改了数值,第二次执行exe 发现数值变了怎么办?所以是只读的。

全局区

  • 全局变量和静态变量
  • 全局区包含了常量区、字符串常量和其他常量
  • 在程序结束后,由操作系统释放

全局区的示例

  1. #include<iostream>
  2. #include<string.h>
  3. using namespace std;
  4. //全局变量
  5. int global_a = 10;
  6. int global_b = 20;
  7. //const 修饰的全局变量
  8. const int const_global_a = 110;
  9. const int const_global_b = 111;
  10. int main(){
  11. //普通局部变量
  12. int a = 10;
  13. int b = 10;
  14. cout << "局部变量a的地址 = " << (long long)&a << endl;
  15. cout << "局部变量b的地址 = " << (long long)&b << endl;
  16. cout << "全局变量global_a的地址 = " << (long long)&global_a << endl;
  17. cout << "全局变量global_b的地址 = " << (long long)&global_b << endl;
  18. //静态变量
  19. static int static_a = 10;
  20. static int static_b = 20;
  21. cout << "静态变量static_a的地址 = " << (long long)&static_a << endl;
  22. cout << "静态变量static_b的地址 = " << (long long)&static_b << endl;
  23. //常量
  24. //字符串常量
  25. cout << "字符串常量的地址 = " << (long long)&"hello_world" << endl;
  26. //const修饰的变量
  27. //const 修饰 全局变量
  28. cout << "const修饰全局变量const_global_a的地址 = " << (long long)&const_global_a << endl;
  29. cout << "const修饰全局变量const_global_b的地址 = " << (long long)&const_global_b << endl;
  30. //const修饰局部变量
  31. const int const_local_a = 1111;
  32. const int const_local_b = 1112;
  33. cout << "const修饰局部变量const_local_a的地址 = " << (long long)&const_local_a << endl;
  34. cout << "const修饰局部变量const_local_b的地址 = " << (long long)&const_local_b << endl;
  35. return 0;
  36. }
  37. 输出结果:
  38. 局部变量a的地址 = 6422044
  39. 局部变量b的地址 = 6422040
  40. 全局变量global_a的地址 = 4206608
  41. 全局变量global_b的地址 = 4206612
  42. 静态变量static_a的地址 = 4206616
  43. 静态变量static_b的地址 = 4206620
  44. 字符串常量的地址 = 4210862
  45. const修饰全局变量const_global_a的地址 = 4210692
  46. const修饰全局变量const_global_b的地址 = 4210696
  47. const修饰局部变量const_local_a的地址 = 6422036
  48. const修饰局部变量const_local_b的地址 = 6422032

从结果上可以看到全局变量和 静态变量、字符串常量以及const修饰的全局变量都在全局区,地址接近,而局部的都在栈区,这些地址彼此接近。
可能有人会问,打印为的地址转成 10 进制,为什么要这样写 ? (long long)&a
还记得C++初始章节里的基础知识点关于一些类型所占字节描述吗?
windows 64 位系统上指针占8个字节,int long 占 4 个字节,如果写成 (int)&a 或者 (long)&a 将会报错 cast from 'int*' to 'int' loses precision [-fpermissive]

程序运行后

栈区

  • 由编译器自动分配释放,存放函数的参数值、局部变量等
  • 不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

    #include<iostream>
    #include<string.h>
    using namespace std;
    int* func(){
      int a = 10;
      return &a;
    }
    int main(){
      int * p = func();
      cout << *p << endl;
      cout << *p << endl;
      return 0;
    }
    

    执行会出错,按道理你func函数返回了a的地址,但是func方法调用完后就会被释放,以前C++能运行的,编译器会认为可能还需要操作,会保留一次,但是第二次再访问就失败了。but 现在,会报

    warning: address of local variable 'a' returned [-Wreturn-local-addr]
       int a = 10;
    

    说明 函数内部定义的变量在函数结束时被释放掉,所以返回是找不到的,编译器过不了。
    堆区

  • 由程序员分配释放,若是没有释放,将在程序结束时由操作系统回收

  • 在C++中主要利用new在堆区开辟内存
    #include<iostream>
    #include<string.h>
    using namespace std;
    int* func(){
      int* a = new int(10);
      return a;
    }
    int main(){
      int * p = func();
      cout << *p << endl;
      cout << *p << endl;
      return 0;
    }
    
    你会发现和之前栈区示例代码区别只是在于 func函数中 a是 new 出来的,然后可以编译。因为此时 a是在堆区开辟了一块内存,但是 a 变量是在存在于栈区,指向的是堆区,a 变量依旧是在func上就会被释放。

    new关键字

    C++ 中利用new在堆区开辟数据,堆区开辟的数据,我们需要手动通过 delete释放 ```cpp int func(){ int a = new int(10); return a; } copy from 之前的堆区例子。这块如果这样写 int a = new int(10),会报错 error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive] int a = new int(10); 所以请记住利用new创建的数据,返回该数据对应的类型指针 在使用完时,调用delete
来测试一波delete
```cpp
#include<iostream>
#include<string.h>
using namespace std;
int*  func(){
    int* a = new int(10);
    return  a;
}
int main(){
    int*   p = func();
    cout <<  p << endl;
    cout << *p << endl;
    delete p;
    cout << "delete " << p << endl;
    cout << "delete " << *p << endl;
    return 0;
}
输出结果:
0x6f6b70
10
delete 0x6f6b70
delete 7279184

为什么delete后,我还能打印出地址,还能取出值呢?你会发现其实这块内存已经释放了,它的值已经都变了。delete只是释放了堆中的空间,并不会将指针 p 指向NULL,仍然指向堆中对应的位置,但是这个位置已经被回收了,可以随时分配给其他对象,所以我们一般delete后最后设置成NULL。