c++动态内存申请(new和malloc)深入讲解,以及常考面经。

c++中new的用法:

定义

new其实就是计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。

语法

①变量申请:

Type* pointer = new Type;
//...
delete pointer;

表达式用于分配内存以包含一个类型类型的单个元素。

②数组申请:

Type* pointer = new Type[N];
//...
delete[] pointer;

表示用于分配类型类型的元素的块(数组),其中N是表示这些元素的量的整数值。

Example:

int * foo;
foo = new int [5];

类对象申请。

int *a = new[5];
class A { ... };  // 声明一个类 A
A *obj = new A();  // 使用 new 创建对象
delete []a;  // new 和 delete 必须对应使用,new 创建一组对象,那么 delete 就要释放一组对象
delete obj;

c++中malloc的用法:

1. 使用malloc()函数需要链接的头文件:
头文件 #include <malloc.h> 或 #include <alloc.h> ,这两个头文件内容一致,用哪个都行

2. malloc()函数原型是 void* malloc(unsigned int num_bytes);
功能:

(1)malloc函数的功能是内存上分配一段长度为 num_bytes 个 byte 大小的空间 (num_bytes的数值大小最大为 unsigned int)

(2)如果成功在内存上分配一段空间则返回一个 void类型的指针 ,因此,如果我们想使用一个int型指针指向这段空间那么需要将返回值进行强转成int*类型

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


若上述代码不使用(int*)进行强转,那么会报错,因为不可以将void*直接赋值给int*类型的变量

(3)若申请内存不成功,则会返回一个空指针 NULL 

(4)当申请的这段内存不再使用时,用free()函数释放掉

free(p);

常考面经

new和malloc的区别

1、 new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持;

2、 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。

3、 new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

4、 new内存分配失败时,会抛出bad_alloc异常。malloc分配内存失败时返回NULL。

5、 new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

6、new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象。

new和delete是如何实现的?

new的实现过程是:首先调用名为operator new的标准库函数,分配足够大的原始为类型化的内存,以保存指定类型的一个对象;接下来运行该类型的一个构造函数,用指定初始化构造对象;最后返回指向新分配并构造后的的对象的指针。

delete的实现过程:对指针指向的对象运行适当的析构函数;然后通过调用名为operator delete的标准库函数释放该对象所用内存。

既然有了malloc/free,C++中为什么还需要new/delete呢?直接用malloc/free不好吗?

malloc/free和new/delete都是用来申请内存和回收内存的。

在对非基本数据类型的对象使用的时候,对象创建的时候还需要执行构造函数,销毁的时候要执行析构函数。而malloc/free是库函数,是已经编译的代码,所以不能把构造函数和析构函数的功能强加给malloc/free,所以new/delete是必不可少的。

new和delete的实现原理, delete是如何知道释放内存的大小的?

1、 new简单类型直接调用operator new分配内存;

而对于复杂结构,先调用operator new分配内存,然后在分配的内存上调用构造函数;

对于简单类型,new[]计算好大小后调用operator new;

对于复杂数据结构,new[]先调用operator new[]分配内存,然后在p的前四个字节写入数组大小n,然后调用n次构造函数,针对复杂类型,new[]会额外存储数组大小;

① new表达式调用一个名为operator new(operator new[])函数,分配一块足够大的、原始的、未命名的内存空间;

② 编译器运行相应的构造函数以构造这些对象,并为其传入初始值;

③ 对象被分配了空间并构造完成,返回一个指向该对象的指针。

2、 delete简单数据类型默认只是调用free函数;复杂数据类型先调用析构函数再调用operator delete;针对简单类型,delete和delete[]等同。假设指针p指向new[]分配的内存。因为要4字节存储数组大小,实际分配的内存地址为[p-4],系统记录的也是这个地址。delete[]实际释放的就是p-4指向的内存。而delete会直接释放p指向的内存,这个内存根本没有被系统记录,所以会崩溃。

3、 需要在 new [] 一个对象数组时,需要保存数组的维度,C++ 的做法是在分配数组空间时多分配了 4 个字节的大小,专门保存数组的大小,在 delete [] 时就可以取出这个保存的数,就知道了需要调用析构函数多少次了。

malloc申请的存储空间能用delete释放吗?

不能,malloc /free主要为了兼容C,new和delete 完全可以取代malloc /free的。malloc /free的操作对象都是必须明确大小的,而且不能用在动态类上。 new 和delete会自动进行类型检查和大小,malloc/free不能执行构造函数与析构函数,所以动态对象它是不行的。当然从理论上说使用malloc申请的内存是可以通过delete释放的。不过一般不这样写的。而且也不能保证每个C++的运行时都能正常。

C++中有几种类型的new

在C++中,new有三种典型的使用方法:plain new,nothrow new和placement new

void* operator new(std::size_t) throw(std::bad_alloc);

void operator delete(void *) throw();
 

因此plain new在空间分配失败的情况下,抛出异常std::bad_alloc而不是返回NULL,因此通过判断返回 值是否为NULL是徒劳的,举个例子:

#include <iostream>
#include <string>
using namespace std;
int main()
{
     try
     {
     char *p = new char[10e11];
     delete p;
     }
     catch (const std::bad_alloc &ex)
     {
         cout << ex.what() << endl;
     }
     return 0;
}
//执行结果:bad allocation

(2)nothrow new

nothrow new在空间分配失败的情况下是不抛出异常,而是返回NULL,定义如下:

void * operator new(std::size_t,const std::nothrow_t&) throw();
void operator delete(void*) throw();

举个例子:

#include <iostream>
#include <string>
using namespace std;
int main()
{
     char *p = new(nothrow) char[10e11];
     if (p == NULL) 
     {
     cout << "alloc failed" << endl;
     }
     delete p;
     return 0;
}
//运行结果:alloc failed

(3)placement new 这种new允许在一块已经分配成功的内存上重新构造对象或对象数组。placement new不用担心内存分配失败,因为它根本不分配内存,它做的唯一一件事情就是调用对象的构造函数。定义如下:

void* operator new(size_t,void*);
void* operator delete(void*,void*);

使用placement new需要注意两点:

  • palcement new的主要用途就是反复使用一块较大的动态分配的内存来构造不同类型的对象或者他们的数组.

  • placement new构造起来的对象数组,要显式的调用他们的析构函数来销毁(析构函数并不释放对象的内存),千万不要使用delete,这是因为placement new构造起来的对象或数组大小并不一定等于原来分配的内存大小,使用delete会造成内存泄漏或者之后释放内存时出现运行时错误。

举个例子:

#include <iostream>
#include <string>
using namespace std;
class ADT{
     int i;
     int j;
public:
     ADT(){
     i = 10;
     j = 100;
     cout << "ADT construct i=" << i << "j="<<j <<endl;
     }
     ~ADT(){
     cout << "ADT destruct" << endl;
     }
};
int main()
{
     char *p = new(nothrow) char[sizeof ADT + 1];
     if (p == NULL) {
         cout << "alloc failed" << endl;
     }
     ADT *q = new(p) ADT; //placement new:不必担心失败,只要p所指对象的的空间足够ADT创建即
可
     //delete q;//错误!不能在此处调用delete q;
     q->ADT::~ADT();//显示调用析构函数
     delete[] p;
     return 0;
}
//输出结果:
//ADT construct i=10j=100
//ADT destruct
malloc与free的实现原理?

1、 在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk、mmap、,munmap这些系统调用实现的; 2、 brk是将数据段(.data)的最高地址指针_edata往高地址推,mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然 后建立虚拟内存和物理内存之间的映射关系; 3、 malloc小于128k的内存,使用brk分配内存,将_edata往高地址推;malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配;brk分配的内存需要等到高地址内存释放以后才能释 放 , 而 mmap 分 配 的 内 存 可 以 单 独 释 放 。 当 最 高 地 址 空 间 的 空 闲 内 存 超 过 128K ( 可 由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩。 4、 malloc是从堆里面申请内存,也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

malloc、realloc、calloc的区别
  1. malloc函数

    void* malloc(unsigned int num_size);
    ​
    int *p = malloc(20*sizeof(int));申请20个int类型的空间;
  2. calloc函数

void* calloc(size_t n,size_t size);
​
int *p = calloc(20, sizeof(int));

省去了人为空间计算;malloc申请的空间的值是随机初始化的,calloc申请的空间的值是初始化为0的;

   3.realloc函数

void realloc(void *p, size_t new_size);

给动态分配的空间分配额外的空间,用于扩充容量。

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值