文章目录
- 一、c/c++内存分布
- 二、new/delete 的使用
- 三、malloc/free 和 new/delete 的对比
- 四、new/delete 的实现原理
- 五、匹配问题
一、c/c++内存分布
求下面各个变量的位置
// c/c++内存分布
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.代码段(常量区)
1、 globalVar在哪里?____ 2、staticGlobalVar在哪里?____ 3、staticVar在哪里?____ 4、localVar在哪里?____ 5、num1 在哪里?____ 6、char2在哪里?____ 7、*char2在哪里?___ 8、pChar3在哪里?____ 9、*pChar3在哪里?____ 10、ptr1在哪里?____ 11、*ptr1在哪里?____
答案:1-5 CCCAA 6-11AAADAB
说明:
- 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段–存储全局数据和静态数据。
- 代码段–可执行的代码/只读常量。
二、new/delete 的使用
关键字: new
作用:申请空间
关键字:delete
作用:释放空间
如:
//new 和 delete的使用
int main()
{
//new 申请一个变量空间
int* p = new int; //(类型指针) 指针名 = new 类型
//delete 释放一个变量空间 // delete 释放的指针
delete p;
//new 申请一个数组空间
int* pp = new int[10]; //(类型指针) 指针名 = new 类型[(申请的数组大小)]
//delete 释放一块数组的空间
delete[]pp; //delete [] 释放的指针
return 0;
}
注意:
释放数组的时候 delete 一定要和 [ ] 配对使用,不然只会释放该指针指向的第一个位置,这样就会造成内存泄漏。
三、malloc/free 和 new/delete 的对比
1、malloc
和free
是库函数,而new
和delete
是运算符(类似sizeof
属于运算符而不属于库函数) 。
2、malloc
使用比new
更复杂
如:
int main()
{
//malloc申请一个变量空间
int* p = (int*)malloc(sizeof(int));
//new申请一个变量空间
int* pp = new int;
return 0;
}
3、malloc
不能对申请的空间进行初始化,而 new
能
如:
int main()
{
//对一个变量初始化
int* p = new int(10); //在后面加括号再加初始化的值
//对数组进行初始化
int* pp = new int[5] {1, 2, 3, 4, 5}; //在后面加花括号再加初始化的值
cout << *p << endl;
for (int i = 0; i < 5; i++)
{
cout << pp[i] << " ";
}
return 0;
}
4、对于自定义类型,new
能调用其构造函数,delete
能调用其析构函数,而malloc/free
不行。
如:
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
};
int main()
{
//用malloc申请
A* a = (A*)malloc(sizeof(A));
free(a);
cout << "划分线" << endl;
//用new申请一个自定义类型空间
A* p = new A;
//用new申请一个自定义类型数组
A* pp = new A[10];
//释放
delete p;
delete[]pp;
return 0;
}
5、在申请空间失败时malloc
会返回NULL
,而new
则会抛出异常。
四、new/delete 的实现原理
operator new
函数(底层还是使用malloc申请空间)是new
的底层实现。
operator delete
函数(底层还是使用free释放空间)是delete
的底层实现。
operator new[]
函数是new
类型[ ]
的实现。
operator delete[]
函数是delete[]
的实现。
对于内置类型
如果申请的是内置类型的空间,
new
和malloc
,delete
和free
基本类似,不同的地方是:
new/delete
申请和释放的是单个元素的空间,new[]
和delete[]
申请的是连续空间,而且new
在申
请空间失败时会抛异常,malloc
会返回NULL
。
对于自定义类型
new的原理
1、 调用operator new
函数申请空间 。
2、在申请的空间上执行构造函数,完成对象的构造 。
delete的原理
1、在空间上执行析构函数,完成对象中资源的清理工作 。
2、调用operator delete
函数释放对象的空间new
。
T[N]的原理
1、调用operator new[]
函数,在operator new[]
中实际调用operator new
函数完成N个对象空间的申请。
2、在申请的空间上执行N次构造函数 。
delete[]的原理
1、 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。
2、调用operator delete[]
释放空间,实际在operator delete[]
中调用operator delete
来释空间。
练习1
使用 char* p = new char[100]
申请一段内存,然后使用delete p
释放,有什么问题?( )
A.会有内存泄露
B.不会有内存泄露,但不建议用
C.编译就会报错,必须使用delete []p
D.编译没问题,运行会直接崩溃
答案:B
A.对于内置类型,此时
delete
就相当于free
,因此不会造成内存泄漏
B.正确
C.编译不会报错,建议针对数组释放使用delete[]
,如果是自定义类型,不使用方括号就会运行时错误
D.对于内置类型,程序不会崩溃,但不建议这样使用
练习2
ClassA *pclassa=new ClassA[5];delete pclassa
;c++语言中,类ClassA的构造函数和析构函数的执行次数分别为( )
A.5,1
B.1,1
C.5,5
D.程序可能崩溃
答案:D
A.申请对象数组,会调用构造函数5次,delete由于没有使用[],此时只会调用一次析构函数,但往往会引发程序崩溃
B.构造函数会调用5次
C.析构函数此时只会调用1次,要想完整释放数组空间,需要使用[]
D.正确
五、匹配问题
关于malloc/free 和 new/delete 申请和释放匹配
1、对于内置类型,是可以的,但是不建议,最好还是配对使用
//内置类型
int main()
{
//new 申请
int* p = new int;
//free 释放
free(p);
//malloc 申请
int* pp = (int*)malloc(sizeof(int));
//delete 释放
delete pp;
return 0;
}
会报警告:
2、对于自定义类型来说很容易出现问题,如下面
(1)申请数组
class A
{
public:
A():_a(10)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
cout << sizeof(A) << endl; //大小为 4
// 用 malloc 申请 申请空间的大小为 80字节
A* pp = (A*)malloc(sizeof(A) * 20);
//用 new 申请 申请的空间大小为 88字节(原本是应为 80 字节,但是多出来的8字节为保存申请多少个A )
A* p = new A[20];
//用 free 释放
free(p); //直接崩掉,因为p的地址为申请的88个字节中的第8个字节地址,所以有8字节无法释放并且无法调用析构
//用delete 释放
delete [] pp; //因为不知道应该析构多少个A(用new会保存申请了多少个A,这时delete就会知道)
// ,所以出现死循环析构
return 0;
}
new A[20]
申请需要的字节
malloc(sizeof(A) * 20)
申请需要的字节
(2)申请一个自定义类型
//申请一个自定义类型
// 用 malloc 申请 申请空间的大小为 4字节
A* pp = (A*)malloc(sizeof(A)); //少了构造
//用delete释放
delete pp; //可以但是不建议
用 new 申请 申请的空间大小为 4 字节
A* p = new A;
free(p); //不会报错,少了析构,当自定义类型里有需要释放的资源是就会出现内存泄漏
(3)总结:
尽量不要用这种不配对的情况,不然可能会出现各种各样的问题,最好配对使用,这样才能保证程序不会在这方面出错。