1. malloc 与 free
1.1 简介
malloc 函数与 free 函数均为 C 库函数。
- malloc
原型:
void* malloc(size_t bytes);
功能:按字节为单位动态申请字节数为 bytes 的堆空间内存
返回:申请成功时返回内存的首地址,申请失败时返回 NULL
由于 malloc 返回的为 void 类型指针,因此在函数调用后需要进行对返回指针进行强类型转换,且应判定返回指针是否为 NULL 即检查是否申请内存失败。同时,由于申请到的内存空间并未初始化,内存里的内容为脏的,应使用 memset 进行清零初始化。
- free
原型:
void free(void *p);
功能:释放 malloc 申请的内存空间
free 与 malloc 必须成对出现,只申请不释放,便会造成内存泄漏。且在调用 free 后,应把原指向堆空间的指针设置为指向 NULL,防止出现野指针。
1.2 示例
int main(void)
{
char* p = (char*)malloc(sizeof(char)*1024); // 申请 1kb 内存
if (p == NULL)
{
assert("malloc fail!");
}
memset(p, 0, 1024);
...
free(p);
p = NUll;
}
1.3 底层原理
操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆节点,然后就将该节点从空闲结点链表中删除,最后将该内存空间的起始地址返回给程序。
需要注意的是,使用 malloc 申请一段大内存时,如果未完全使用,内存管理系统并不会把所申请的未使用部分的堆空间映射到内存中占据内存。
2. new 与 delete
2.1 简介
new 与 delete 都是 C++ 特有的关键字。
- new
new 以数据类型为单位申请堆空间内存,可在申请的同时进行对内存空间进行初始化操作。在申请内存失败后,new 的行为在不同平台上表现不同,在 Linux 系统上会抛出异常。由于 C++ 为强类型语言,所以接收指针类型必须与申请内存的数据类型一致。
例如:
//char* p = new int; // error: cannot convert ‘int*’ to ‘char*’ in initialization
int* p_A = new int; // 申请 sizeof(int) 大小的内存,未初始化
int* p_B = new int(3); // 申请 sizeof(int) 大小的内存,并初始化为 3
int* p_C = nwe int[3]; // 申请 3*sizeof(int) 大小的内存,未初始化
int* p_D = nwe int[3](); // 申请 3*sizeof(int) 大小的内存,并均初始化为 0
//int* p_E = new int[3](3); // error: parenthesized initializer in array new
string* ps_A = new string(); // 申请一段空的字符串
string* ps_B = new string("132"); // 申请一段字符串且初始化字符内容为 "132"
- delete
delete 用于释放 new 所申请的内存,当申请的内存为类对象时,会自动调用析构函数等操作。在使用 delete 后,需要把指针指向 nullptr(C++11) 或 NULL 防止出现野指针。虽然 free 也能够释放 new 申请的内存,但无法自动调用析构函数等操作。因此当使用 new 申请内存时,应使用 delete 进行释放而非 free。
2.2 示例
int* p_A = new int;
int* p_B = new int[3];
string* ps = new string("132");
...
delete p_A;
delete []p_B;
delete ps;
p_A = nullptr;
p_B = nullptr;
ps = nullptr;
2.3 底层原理
new 底层仍然为通过 malloc 的方法进行内存申请,不同的是,获取到所分配的内存空间后,当申请内存为类对象时,会触发类的构造函数,然后对该内存标记为类对象。相应的,delete 与 free 的释放内存方法相同,不同的是,在释放内存前会先查看该内存是否标记为类对象,如果是则触发类的析构函数,再释放所分配的内存空间。
当使用 new [n] 的方式申请 n 个对象的内存空间时,实际上在标记内存为类对象的地方会同时标记该类对象的数量。使用 delete[] 进行释放时,会查看该类对象的数量并调用相应次数的析构函数。但如果只使用 delete 进行释放时,则默认只调用一次析构函数。因此,使用 new [n] 的方式申请 n 个对象的内存空间时,应使用 delete[] 的方法进行释放,否则该行为为危险的(linux 下发生了段错误)。
当然,对于非类对象的内容申请时,使用 new、delete、malloc、free 混用并不会出现什么问题,但为了编程的严谨性,还应区分调用。
3. malloc 与 new 区别
关于 malloc 与 new 是面试的高频题,必须全面深刻地认识两者的区别。
不同角度 | 区别 |
---|---|
语言 | new 为 C++ 特有的关键字,无法在 C 中使用。而 malloc 在 C、C++ 中都可以调用。 |
语法 | new 为关键字,malloc 为库函数。对于申请的是类对象的内存空间而言,用 malloc 无法满足要求。 |
申请内存 | new 以数据类型为单位申请内存,申请的同时可以对内存进程初始化。malloc 以字节为单位申请内存,内存内容为脏的,需要使用 memset 进行初始化。 |
申请内存失败 | new 的行为在不同平台上表现不同,可能抛出异常 std::bad_alloc 也可能返回 NULL。malloc 会返回 NULL,需要对返回值进行检查。 |