阅读耗时大约1分半
C++ 在执行的时候,内存大体分了4个区域
- 代码区 :存放函数体的二进制代码,由操作系统进行管理
- 共享的,目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
- 只读的
- 全局区: 存放全局变量和静态变量以及常量
- 栈区: 由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
- 主要是利用new在堆区开辟内存
分区的目的也是为了更好的管理它们的生命周期
程序运行前
在程序编译后,生成exe 可执行程序,未执行该程序前分为两个区域
代码区
- 存放CPU执行的机器指令
- 代码区共享,对于频繁被执行的程序,只需要在内存中有一份代码即可。例如双击exe 多次,它在内存中只有一份代码,不会放多份。
- 代码区只读,可以防止程序意外修改它的指令。第一次执行exe,修改了数值,第二次执行exe 发现数值变了怎么办?所以是只读的。
全局区
- 全局变量和静态变量
- 全局区包含了常量区、字符串常量和其他常量
- 在程序结束后,由操作系统释放
全局区的示例
#include<iostream>#include<string.h>using namespace std;//全局变量int global_a = 10;int global_b = 20;//const 修饰的全局变量const int const_global_a = 110;const int const_global_b = 111;int main(){//普通局部变量int a = 10;int b = 10;cout << "局部变量a的地址 = " << (long long)&a << endl;cout << "局部变量b的地址 = " << (long long)&b << endl;cout << "全局变量global_a的地址 = " << (long long)&global_a << endl;cout << "全局变量global_b的地址 = " << (long long)&global_b << endl;//静态变量static int static_a = 10;static int static_b = 20;cout << "静态变量static_a的地址 = " << (long long)&static_a << endl;cout << "静态变量static_b的地址 = " << (long long)&static_b << endl;//常量//字符串常量cout << "字符串常量的地址 = " << (long long)&"hello_world" << endl;//const修饰的变量//const 修饰 全局变量cout << "const修饰全局变量const_global_a的地址 = " << (long long)&const_global_a << endl;cout << "const修饰全局变量const_global_b的地址 = " << (long long)&const_global_b << endl;//const修饰局部变量const int const_local_a = 1111;const int const_local_b = 1112;cout << "const修饰局部变量const_local_a的地址 = " << (long long)&const_local_a << endl;cout << "const修饰局部变量const_local_b的地址 = " << (long long)&const_local_b << endl;return 0;}输出结果:局部变量a的地址 = 6422044局部变量b的地址 = 6422040全局变量global_a的地址 = 4206608全局变量global_b的地址 = 4206612静态变量static_a的地址 = 4206616静态变量static_b的地址 = 4206620字符串常量的地址 = 4210862const修饰全局变量const_global_a的地址 = 4210692const修饰全局变量const_global_b的地址 = 4210696const修饰局部变量const_local_a的地址 = 6422036const修饰局部变量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在堆区开辟内存
你会发现和之前栈区示例代码区别只是在于 func函数中 a是 new 出来的,然后可以编译。因为此时 a是在堆区开辟了一块内存,但是 a 变量是在存在于栈区,指向的是堆区,a 变量依旧是在func上就会被释放。#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; }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。
