【C++】malloc/free和new/delete

malloc/free

一、C中malloc的使用
在C中我们动态开辟内存的时候用的是malloc函数,回收时用的是free函数。
1、在C中我们一般的使用形式是这样的:

int *p = (int*)malloc(sizeof(int));

这里面存在两个问题:
(1)、sizeof(int)计算的是什么?
sizeof计算的是一个类型单元的大小
(2)、malloc之后为什么要用(int*)强转呢?
要解决这个问题,我们首先要了解malloc的原型

void* malloc(size_t size);

size_t是对ungsined int size_t类型的一个重定义
size:开辟的内存大小
void*:无符号的指针(只知道起始位置)是一个半开半闭的区间

所以这块是用(int *)强转强转成具体的类型来使用。
所以C中之所以这样使用malloc的原因可以这样总结:
首先在malloc调用点给出的实参的类型通过sizeof计算出一个类型的大小,让系统去通过malloc开辟出相对应的内存单元的大小,但是malloc开辟之后返回的是一个void 的类型。由于void类型无法直接使用,所以通过强转转成具体的类型就可以使用

2、malloc使用所出现的问题
(1)实参传递
我们如果这样写malloc函数

int* p = malloc(1);

这块在运行的时候就会报错,如下图所示:
在这里插入图片描述
这是因为在C中,是允许任意指针转化成void*,但是不允许void*转化成任意指针。所以我们改成这样:

int* p = (int*)malloc(1);

完整代码是这样的

int main()
{
	int *p = (int *)malloc(1);
	free(p);
	return 0;
}

这样运行之后,我们发现程序是正确的。但是从逻辑上这段代码是不正确的。我们可以从逻辑上分析一下这段代码

  • malloc接受的是开辟的内存的大小。这个时候我们赋予malloc的是一个单元的内存大小,到时候malloc只会在系统上开辟一个单位的内存大小。
  • (int*)强转之后,代表这块我们定义了一个int*类型的指针P指向它,P中存放这个类型单元的地址。
  • 通过P的层面,我们可以这样理解:P指向的是一个int类型的内存单元,是四个字节。
  • 而malloc这个时候只赋予了1个字节的内存单元,这个时候就会发生越界的情况。
    如下图所示:
    在这里插入图片描述
    之所以上面程序没有崩溃的原因就是,我们对于P没有进行任何操作,只是将它开辟后直接释放掉了。
    如果代码改成这样,就会发生程序崩溃:
int main()
{
	int *p = (int *)malloc(1);
	*p = 20;
	free(p);
	return 0;
}

2、返回值类型不安全
void*本身就是类型不安全
二、free的使用
1、我们给出这样一段代码:

int main()
{
	int* p = (int*)malloc(4);
	double* q = (double*)malloc(8);
	char* c = (char*)malloc(1);
	free(p);
	free(q);
	free(c);
	return 0;
}

这块free释放了三种类型的指针。那么问题来了,free函数中是怎么知道释放多大的内存呢?

  • 所谓的内存属于冯诺依曼体系中五大部件中的存储器。但是存储器是一个硬件。但对于写的应用程序来说是无法操作硬件的。所有硬件的操作都是由操作系统操作的。
  • 首先在应用程序里面写了malloc或者free。这些函数都是应用程序表层的接口。
  • 调用malloc或者free的时候就相当于给操作系统发送了一个请求,由操作系统的API来接受这个请求。接受之后交给硬件驱动,由硬件驱动在内存中开辟出内存单元,最后再返给应用程序。
  • 所以说我们使用malloc或者free时程序要经过操作系统。如下图所示
    在这里插入图片描述
  • 当我们调用malloc时,会在操作系统中有一个记录:开辟的内存的地址和开辟的内存的大小。
  • 当我们调用free时,形参将拿到的内存地址与操作系统中产生记录的内存地址进行匹配,如果匹配成功的话,证明我们的操作是合法的。
  • 匹配成功后,就获取开辟内存的大小
    这就是为什么在free点,我们只需要知道释放的内存地址,而不需要知道释放的内存大小。
    2、释放非法的内存空间
    看这样一段代码:
int* p = (int*)malloc(sizeof(int));
p = (int*)((int)p+1);
free(p);

执行这段代码的时候我们发现程序会出错。原因如下:

  • 通过malloc我们在堆上开辟了int类型的内存块,假设这块地址是0X100。
  • 定义了指针p指向了这个内存单元,p里面存放的就是0x100
  • 针对于第二句代码,先是对p进行类型强转。(p+1)将这块地址变为0x101.又将它转成指针类型赋给了p.
  • 赋值给p后,p中存放的是0x101,p就指向了第二个内存单元
  • 当调用free后free释放的是0x101的地址,在操作系统中找不到。操作系统中只有0x100.
  • 所以free就释放了非法内存地址。
    如下图所示:
    在这里插入图片描述

new/delete

C++中new的使用
来说new的时候我们将new和malloc比较着来说
1、new+类型
new也不需要类型的转换。返回值类型安全
释放的时候只需要delete+数据类型即可。

int main()
{
	int* p = new int;
	*p = 20;
	delete p;
}

2、new不仅能开辟内存还可以做初始化
类型之后加上(),()初始化列表
malloc只能单纯得开辟内存

int* p1 = new int(10);
cout << *p1 << endl;
delete p1;

3、开辟数组
C中

int* p = (int*)malloc(sizeof(int)*10);
free(p);

C++

int* p =new int[10];
delete[] p;//释放一个数组

4、处理内存不足的情况
C

int* p = (int*)malloc(sizeof(int));
if(p == NULL)
{
	printf("out of memory!");
	exit(0);
}

C++

int* q = new int;//抛出异常

如果在C++中出现内存不足的时候,C++直接会抛出异常,在new点退出。
5、数组初始化

int main()
{
	int* p = new int[10]();//这里对数组进行了初始化0,如果不加()这时候数组中打印出来的是随机值
	for(int i = 0; i < 10; i++)
	{
		cout << p[i] << " ";
	}
	cout << endl;
	delete[] p;
	return 0;
}

6、开辟常量堆内存
在C语言中malloc不能开辟一个常量的堆内存。
但是在C++中可以用new开辟一个常量的堆内存。
new也可以开辟一个常量的数组,但是开辟的常量数组是没有意义的,不建议使用。

int main()
{
	const int* p1 = new const int(20);
	delete p1;
	const int* p2 = new const int[10]();
	delete[] p2;
	return 0;
}

7、重定位new
重定位new是通过new后面的地址在对应的内存单元拿到内存供外部使用。可以在任何位置开辟。
普通的new只能在堆上开辟内存。

int main()
{
	int a;
	char* c = new(&a)char('a');//replacement new
	char* c1 = new char('b');//普通的new
}
 

new和malloc的简单区别

1、new是关键字,malloc是一个函数
2、new不需要确定开辟的大小
malloc需要传递开辟的大小
3、new返回值类型安全
malloc返回值类型不安全
4、new不仅能开辟内存,还可以做初始化
malloc单纯开辟内存
5、new[]开辟数组
malloc开辟总大小的方式来处理
6、new开辟内存失败后直接抛出异常
malloc开辟内存失败,返回NULL
7、new开辟的内存位置在自由存储区域
malloc开辟的内存位置在堆上

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值