一、C/C++内存分布
C语言阶段我们学习过C程序内存区域是如何划分的,C++也是这样划分程序的,只不过在使用方式上和C有所不同,我们先复习C,先看几道题目:
解析图:
上面的很多相信大家都可以轻松解决,这里只说几道容易出错的题目。
1、*char2存储在什么地方?
很多老铁可能会这样觉得,char2是常量字符串"abcd"的地址,存储在栈上,然后*char2拿到字符串的首字符'a',所以应该存储在代码段(常量区),其实不然。这里我想问老铁一个问题,如果*char2拿到存储在常量区的'a',那么我如果对char2的内容做修改,是否也要修改常量区的内容呢?显然是不可以的,常量区内容不能被修改。所以*char2应该在栈上!!我们只是把常量字符串"abcd"拷贝到了char2上,*char2还在栈上可以被修改。
2、与之相对应的就是*pchar3,pchar3是一个地址,虽然用const修饰,但他就是一个地址,没有实际内容,指向存储在代码段的常量字符串。因为他用const修饰,限制修改,没必要再拷贝。
【说明】:
1、栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2、内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
3、堆用于程序运行时动态内存分配,堆是向上增长的。
4、数据段--存储全局数据和静态数据。
5、代码段--可执行的代码/只读常量。
二、C++内存管理方式
C++兼容C语言,C语言使用malloc、calloc、realloc管理内存,C++中可以继续使用这些内存管理方式,但是有些地方用它就不太方便,于是C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
Ⅰ new和delete操作内置类型
C++提出用new来开辟空间,用delete来释放空间。那么我们怎么使用new和delete呢?
①new和delete的基本使用
new默认不会初始化。
new给初始化的方式是在内置类型后面加(),()里面是要初始化的值。
new想要开辟多个空间,需要加[ ],同样在delete时也需要加[ ]。
new在初始化一个空间时用(),初始化多个空间时,类似于数组,用{ },里面放初始化值,没有给初始化的后续默认是给0,如果压根没有给初始化值,那么开辟的多个空间里面默认随机值。
Ⅱ new和delete操作自定义类型
我们光看new和delete对内置类型,与C语言那一套难分高下,可能有优势的地方在于可以自行给初始值,而calloc最多都设为0。new和delete主要是针对自定义类型的。
我们看到new/delete 和malloc/free最大区别是 new/delete对于自定义类型除了开空间,还会调用构造和析构函数,这一点会给我们带来很大方便。
举个例子:之前在C语言数据结构阶段构造链表时,我们每开辟一个新节点,就要调用函数对它初始化,这里在C++我们开辟节点,就可以自动调用构造初始化,很方便。
struct ListNode { ListNode(int val) :_next(nullptr) , _val(val) { } ListNode* _next; int _val; }; int main() { ListNode* n1 = new ListNode(1); ListNode* n2 = new ListNode(2); ListNode* n3 = new ListNode(3); ListNode* n4 = new ListNode(4); n1->_next = n2; //... //不用写buynode return 0; }
Ⅲ new和delete要匹配使用
new和delete配套使用,用于开辟一块和销毁一块空间。new[ ]和delete[ ]配套使用,用于开辟多块和销毁多块空间。
new[ ]和delete
这里就会报错,析构失败。
但是,这里有个奇怪的现象,我把析构函数屏蔽掉就不会报错!
这里是vs编译器默认的析构函数对此处有优化,它和编译器的实现有关,不是很重要,重要的是这里一定要匹配使用,不要随意匹配,否则任何错误都有可能发生。