c/c++内存管理
c/c++内存管理
文章目录
1、 程序内存的分区
-
栈, 存放局部变量、函数参数、局部数组等,生命周期随函数调用结束而销毁(遵循 “先进后出”),栈是向下增长的
-
堆, 存放malloc/calloc/realloc/new动态分配的内存,需要手动free,delete释放,堆是向上增长的
-
代码段(常量区),存放字符串常量,只读常量(防止意外修改),编译时确定大小
-
数据段(静态区),存放全局变量、静态变量(static),生命周期为整个程序运行期间
我们来看一个练习题
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
选择题:
A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____
staticGlobalVar在哪里?____
staticVar在哪里?____
localVar在哪里?____
num1 在哪里?____
char2在哪里?____
*char2在哪里?___
pChar3在哪里?____
*pChar3在哪里?____
ptr1在哪里?____
*ptr1在哪里?___
答案CCCAA AAADA B
globalVar是全局变量,staticGlobalVar,staticVar是静态变量,三者都储存在数据段
localVar是局部变量,num1是局部数组变量,char2是局部字符数组变量,pChar3是局部指针变量,四者都储存在栈
char2,内容虽然是字符串“abcd“,但是局部数组会将常量区字符复制到栈上,所以char2在栈上
*pChar3,指针指向字符串常量“abcd”,储存在代码段
Ptr1局部指针变量,储存在栈上
*ptr1指向的动态分配内存(malloc),储存在堆上
2、 C语言中动态内存管理方式,malloc,calloc,realloc,free
#include<iostream>
using namespace std;
int main()
{
/1、malloc在堆上分配一块指定大小的连续内存空间,不初始化内存
/malloc返回值为void*,需要强转为int*类型
/分配能储存10个int的空间
int* p1 = (int*)malloc(10 * sizeof(int));
/检查分配是否成果
if (p1 == NULL)
{
perror("malloc fail");
return -1;
}
/2、calloc与malloc类似,但是将所有字节初始化为0
/分配5个int,初始化为0
int* p2 =(int*)calloc(5, sizeof(int));
if (p2 == NULL)
{
perror("calloc fail!");
return -1;
}
/3、realloc,扩大缩小已分配内存块的大小
/内存过大,会把内存分配到新地址
int* p3 = (int*)realloc(p1, 20 * sizeof(int));
if (p3 == NULL)
{
perror("realloc fail");
/realloc失败,需要释放原内存,避免内存泄露
free(p1);
p1 = NULL;
return -1;
}
p1 = p3;/扩容成功后,更新p1指向新地址
/4、free释放由malloc/calloc/realloc开辟的内存
int* p = (int *)malloc(sizeof(int));
free(p);/释放内存
p = NULL;/避免野指针
//realloc成果后让p1,p3指向同一块空间,因此只需要释放一次
free(p1);
free(p2);
p1 = p2 = p3= NULL;
return 0;
}
-
内存泄漏,动态分配的内存没有被释放,导致系统资源耗尽,所以每一个动态开辟的内存都应该free
-
野指针,释放内存后没有置为空指针,后续误用会导致程序崩溃,所有free后立即赋值NULL
3、 C++的内存管理方式new/delete
#include<iostream>
using namespace std;
int main()
{
/动态申请一个int类型空间
int* p1 = new int;
/动态申请一个int类型的空间并初始化为10
int* p2 = new int(10);
/动态申请10个int类型的空间
int* p3 = new int[10];
/如果要初始化数组,可以使用列表初始化(C++11)
/ 剩余元素自动补0
/int* p3 = new int[10] {0, 1, 2, 3, 4, 5, 6,};
/new对应delete,new[]对应delete[],申请了多少内存就释放掉多少
delete p1;
p1 = nullptr;
delete p2;
p2 = nullptr;
delete[] p3;
p3 = nullptr;
return 0;
}
- New/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new申请空间失败时会抛异常(这个我们以后再说),malloc会返回NULL
- New和delete是C++的专属内存管理运算符,与C的malloc/dree相比,最大的特点是会自动调用对象的构造函数和析构函数

4、 定位new
- 定位new是一种特殊的new运算符用法,它允许在已分配的内存空间上调用构造函数初始化对象,而不是重新向系统申请新的内存
- 格式:new (指针) 类型(构造函数参数); // 在指针指向的内存上构造对象
- 使用场景,定位new表达式在实际中一般是配合内存池使用,因为内存池分配出的内存没有初始化,如果是自定义类型的对象,需要使用new的定义表达式经行显式调构造函数进行初始化
#include<iostream>
using namespace std;
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int _a;
};
int main()
{
/p1现在指向的是与A对象相应大小的一块空间,还不能算是一个对象
/因为构造函数没有执行
A* p1 = (A*)malloc(sizeof(A));
/定位new,在p1指向的内存上调用A的构造函数(真正的创造对象)
new(p1)A;
/需要手动调用析构函数
p1->~A();
free(p1);
/调用operator new函数,只分配空间,不调用构造
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}
5、 operator new与operator delete函数
- operator new/operator delete 是底层内存管理函数,负责分配/释放原始内存,不涉及对象构造/析构。相当于C的malloc/free
- operator new分配内存失败会抛出std::bad_alloc 异常 ,new操作符符就是调用operator new来申请空间,再调用构造函数来实例化对象
- delete操作符符就是调用析构函数,再调用operator delete来释放内存空间
- new[]会调用operator new[]分配数组内存,再调用每个元素的构造函数,delete[]先调用每个元素的析构函数,再调用operator delete[]释放内存
6、 malloc/free和new/delete的区别
- malloc和free是函数,new和delete是操作符
- malloc和free是负责内存分配和释放,new和delete不仅分配释放内存,还会自动调用构造函数和析构函数
- malloc申请空间时需要手动计算空间大小并传递,new只需在后边跟上空间类型即可,如果时多个对象,[]中指定对象个数即可
- malloc返回值为void*,需要手动强制转换为目标类型,new直接返回目标类型指针(如int *)不需要转换
- malloc内存分配失败时返回NULL指针,所有使用必须判空。new失败时会抛异常,要捕获异常
更多推荐


所有评论(0)