目录
一、内存四区
在计算机科学中,特别是在c或c++语言编程时,内存通常大致分为四个区域,而不同的区域存放的数据赋予不同的生命周期,给我们更大的灵活编程:
- 代码区:存储程序的可执行代码(二进制代码),也就是程序编译后的机器指令,通常是只读,防止运行时意外修改指令
- 全局区(数据区):存放全局变量和静态变量以及常量,这个区域在程序启动时被初始化,并且具有固定的内存分配
- 栈区:由编译器自动分配释放,存放函数调用时的局部变量、函数参数、返回地址等等,并且每次函数调用时栈都会增长,函数返回时,栈会收缩
- 堆区:堆区是程序运行时动态分配内存的区域,由程序员分配和释放,如果程序员不释放,程序结束时由操作系统回收
二、程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:
- 代码区:
- 存放cpu执行的机器指令(二进制代码)
- 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在代码中有一份代码即可
- 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
- 全局区(数据区):
- 全局变量和静态变量存放在此
- 全局区还包含了常量区,字符串常量和其他常量也存放在此
- 该区域的数据在程序结束后由操作系统回收
代码验证全局区:
int main()
{
//静态变量
static int a3;
static int b3;
//局部变量
int a4;
int b4;
//局部常量
const int a5=10;
const int b5=10;
//打印地址
cout << "全局变量a1的地址:" << (int)&a1 << endl;
cout << "全局变量b1的地址:" << (int)&b1 << endl;
cout << "全局常量a2的地址:" << (int)&a2 << endl;
cout << "全局常量b2的地址:" << (int)&b2 << endl;
cout << "静态变量a3的地址:" << (int)&a3 << endl;
cout << "静态变量b3的地址:" << (int)&b3 << endl;
cout << "局部字符串常量a6的地址:" << (int)&"hello" << endl;
cout << "局部字符串常量b6的地址:" << (int)& "hello" << endl;
cout << "局部变量a4的地址:" << (int)&a4 << endl;
cout << "局部变量b4的地址:" << (int)&b4 << endl;
cout << "局部常量a5的地址:" << (int)&a5 << endl;
cout << "局部常量b5的地址:" << (int)&b5 << endl;
}
结果:
全局变量a1的地址:-868223152
全局变量b1的地址:-868223148
全局常量a2的地址:-868238320
全局常量b2的地址:-868238316
静态变量a3的地址:-868223144
静态变量b3的地址:-868223140
局部字符串常量a6的地址:-868238312
局部字符串常量b6的地址:-868238312
局部变量a4的地址:-1634731644
局部变量b4的地址:-1634731612
局部常量a5的地址:-1634731580
局部常量b5的地址:-1634731548
根据结果显示的地址距离不难发现全局区存放有:全局变量、静态变量(static修饰)、常量(字符串常量、全局常量(const修饰)),而局部变量和局部常量(const修饰)都不在全局区中
总结:
- c++中在程序运行前分为全局区和代码区
- 代码区特点是共享和可读
- 全局区中存放全局变量、静态变量、常量
- 常量区中存放const修饰的全局常量和字符串常量
三、程序运行后
栈区:
- 由编译器自动分配释放,存放函数的形参、局部变量、返回地址等
- 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
代码举例:
int* func(int a)//形参
{
int b = a;//局部变量
return &b;//返回地址
}
int main()
{
int*p =func(10);
cout << *p << endl;
cout << *p << endl;
}
结果:
10
2297916
由前文知func函数中的数据都在栈区当中,当这个函数执行结束之后内存就自动释放了,但是编译器会对返回的地址会做一次保留,所以第一次输出时就是10,但是第二次就是一个随机值了,所以证明了不能返回局部变量的地址,这会造成野指针的问题
堆区:
- 由程序员分配和释放,如果程序员不释放,程序结束时就由操作系统回收
- 在c++中主要利用new关键字在对堆区开辟空间
代码举例:
int* func(int a)//形参
{
//利用new关键字将数据开辟到堆区
//这里的指针p本质也是局部变量,放在栈上,但是指针保存的数据是放在堆区的,所以返回的值也是在堆区
int *p=new int(a);
return p;//返回地址
}
int main()
{
int*p =func(10);
cout << *p << endl;
cout << *p << endl;
}
四、new操作符
在c++中,主要用new操作符在堆区开辟空间,堆区开辟的数据由程序员手动开辟和释放,而释放就要利用操作符delete,利用new 创建的数据会返回一个指向该数据类型的指针(地址),下面是代码介绍语法:
class p//定义类
{
public:
p()
{
cout<<"默认构造函数的调用"<<endl;
}
~p()
{
cout<<"析构函数的调用"<<endl;
}
}
int main()
{
//分配单个对象
int* p1 = new int;//在堆分配一个int类型的内存并返回其指针
//分配数组
int* arr = new int[10];//分配一个包含10个整型的数组
//初始化对象
int* p2 = new int(5);//利用new开辟空间时可以顺便初始化整型为5
//分配并构造对象
p* p3=new p();
//释放内存
delete p1;
delete p2;
delete []arr;
delete p3;
}
注意事项:
- 每次调用new操作符都应该有一个对应的delete或delete[]操作符来释放内存
- 忘记释放内存将会导致内存泄漏
- 注意区分分配数组和单个对象的区别,并且在释放内存时也有所区别