C++的内存分区分为程序执行前(代码区和全局区)和程序执行后(栈区和堆区)四类。
1.代码区
存放函数体的二进制代码,由操作系统进行管理。它有以下两个特点:
1.代码区是共享的。对于频繁执行的程序,只会在内存中生成一份代码,不会造成资源浪费。
2.代码区是只读的。目的是为了防止程序意外的修改了它的指令,若为可读可写,会造成某些损失。
2.全局区(静态区)
存放全局变量、静态变量和常量。
全局区包含常量区,字符串常量和其他常量都在这里。
该区域的数据在程序结束之后由操作系统释放。
注意:局部常量仍然在栈区。
下面通过一段代码让我们清晰认识全局区。
#include<iostream>
using namespace std;
int x = 2;
int y = 3;
static int z = 7;//const修饰的全局常量
int main() {
//全局区:全局变量、静态变量、常量
int a = 2, b = 3;//局部变量
cout << "局部变量a的地址为:" << (int)&a << endl;
cout << "局部变量b的地址为:" << (int)&b << endl;
cout << "全局变量x的地址为:" << (int)&x << endl;
cout << "全局变量y的地址为:" << (int)&y << endl;
static int c = 5;//静态变量
static int d = 5;
cout << "静态变量c的地址为:" << (int)&c << endl;
cout << "静态变量d的地址为:" << (int)&d << endl;
//常量:普通常量和cosnt修饰的常量
const int e = 6;//const修饰的局部常量
cout << "字符串常量的地址为:" << (int)&"word" << endl;
cout << "const修饰的局部常量的地址为:" << (int)&e << endl;
cout << "const修饰的全局常量的地址为:" << (int)&z << endl;
return 0;
}
通过打印这些地址信息我们可以发现:栈区和全局区的内存分布还是挺大。通过上述的运行结果,我们可以归纳一下信息:
1.全局变量、静态变量都位于全局区
2.对于常量,分为两种普通常量和const修饰的常量。普通的常量像整型常量、字符串常量都位于常量区。而对于const修饰的常量,其中const修饰的全局常量位于全局区,而const修饰的局部常量仍然位于栈区。所以说,并不是所有的常量都位于全局区。
3.栈区
由编译器自动释放,存放的函数的参数值,局部变量等。
下面我们来看一段代码
#include<iostream>
using namespace std;
int* fun(int a, int b) {
cout << "函数参数a的地址为:" << (int)&a << endl;
cout << "函数参数b的地址为:" << (int)&b << endl;
int c = a + b;
return &c;
}
int main() {
int a = 5, b = 6;
cout << "局部变量a的地址为:" << (int)&a << endl;
cout << "局部变量b的地址为:" << (int)&b << endl;
int* p = fun(a,b);
cout <<"c的地址为:"<<(int)p << endl;
cout << "c的值为:" << (int)*p << endl;
return 0;
}
根据运行结果我们可知,局部变量和函数参数确实位于栈区。但是我们用一个p来接收c的地址,然后打印*p的值,发现并不是11。这是为什么呢?这是因为在fun函数结束的时候,局部变量c会被编译器回收,所以想通过*p得到c的值是一个错误的值。所以不要返回局部变量的地址。
4.堆区
由程序员分配释放,如果程序员没有释放,函数结束时操作系统会进行回收。在C++中常用new在堆区开辟空间。
#include<iostream>
using namespace std;
int x = 8;
int* fun() {
int* p = new int(5);
return p;
}
int main() {
int a = 6;
cout << "局部变量a的地址为:" << (int)&a << endl;
cout << "全局变量x的地址为:" << (int)&x << endl;
int* p = fun();
cout << "p的地址为:" << (int)p << endl;
cout << "p的值为:" << (int)*p << endl;
return 0;
}
通过运行上述的代码,我们发现利用new关键字开辟出来的p位于另外一块区域(堆区)。