🎬 胖咕噜的稞达鸭 :个人主页
🔥 个人专栏 : 《数据结构》《C++初阶高阶》
《Linux系统学习》
《算法日记》
⛺️技术的杠杆,撬动整个世界!
malloc,calloc和realloc的区别
malloc:
按字节数分配:void * malloc(size_t size);
返回的是未初始化的内存
失败之后返回的是nullptr;
cpp
int *p = static_cast<int*>(std::malloc(10 * int));
if(p == nullptr)
{
perror("malloc failed");
}
std::free(p);
calloc:
分配并清零:void* calloc(size_t n, size_t size);
分配 n * size 字节,并把这块内存 全部置 0
失败返回 nullptr
cpp
int *p = static_cast<int*>(std::calloc(10, sizeof(int)));
if(p == nullptr)
{
perror("calloc failed");
}
std::free(p);
realloc
调整已分配块大小:void* realloc(void* ptr, size_t newSize);
只适用于 malloc/calloc/realloc 得到的指针(或 nullptr)
可能原地扩/缩;也可能 搬家到新地址并复制旧内容
失败返回 nullptr,原指针仍然有效(不会被释放)
注意:realloc(p, 0) 行为实现相关(有的等价 free,有的返回可释放指针),不建议这么写。
cpp
int *p = static_cast<int*>(std::calloc(10, sizeof(int)));
if(!p)return;
//此时calloc在申请内存的过程中成功了,原指针
//此时应该用realloc原地扩容,或者缩小
int *q = static_cast<int*>(std::realloc(p, 20 * sizeof(int)));
if(!q)
{
//realloc失败了,calloc不会变;realloc成功了就得释放原指针p;
std::free(p);
return;
}
p = q;//覆盖p的指针
std::free(p);
三者核心区别总结
- 是否清零
malloc:不清零(未初始化)
calloc:清零
realloc:扩容时 新增部分不保证清零;缩容则截断
- 参数形式
malloc(size)
calloc(count, elemSize)(更不容易写错"元素个数 * 大小")
realloc(ptr, newSize)
- 是否改变地址
malloc/calloc:新分配,必然是新指针
realloc:可能返回同一地址,也可能返回新地址(旧指针失效)
- 失败语义
malloc/calloc:失败返回 nullptr
realloc:失败返回 nullptr,但 原内存块还在(所以要用临时指针接)
在 C++ 里用它们的"坑"(很重要)
不能和 new/delete 混用
malloc/calloc/realloc 分配的必须 free
new 分配的必须 delete(数组是 delete[])
C++ 更推荐的替代写法
数组/动态缓冲区:std::vector
字符串:std::string
智能指针:std::unique_ptr<T[]>
需要手动增长缓冲:std::vector 的 resize/reserve(代替 realloc 思路)
常见的动态内存的错误:
- 对NULL指针的解引用操作
- 对动态开辟空间的越界访问
- 对非动态开辟内存使用free释放
4.使用free释放一块动态开辟内存的一部分
-
对同一块动态内存多次释放
-
动态开辟内存忘记释放(内存泄漏)
cpp
#include <cstdlib>
#include <cstdio>
static void case1_null_deref() {
int* p = nullptr;
// 1) 对 NULL 指针解引用:崩溃/未定义行为
*p = 123;
}
static void case2_out_of_bounds() {
int* p = static_cast<int*>(std::malloc(3 * sizeof(int)));
// 2) 越界访问:未定义行为
p[3] = 10; // 合法下标是 0,1,2
std::free(p);
}
static void case3_free_non_heap() {
int x = 7;
int* p = &x;
// 3) 对非动态开辟内存 free:未定义行为
std::free(p);
}
static void case4_free_partial_block() {
int* p = static_cast<int*>(std::malloc(10 * sizeof(int)));
// 4) free 动态内存的一部分:未定义行为
std::free(p + 3);
// std::free(p); // 这里再 free 也不行:p 已经处于不可用状态
}
static void case5_double_free() {
int* p = static_cast<int*>(std::malloc(4 * sizeof(int)));
std::free(p);
// 5) 重复释放同一块内存:未定义行为(常见是直接报错/崩溃)
std::free(p);
}
static void case6_memory_leak() {
int* p = static_cast<int*>(std::malloc(100 * sizeof(int)));
p[0] = 1;
// 6) 忘记释放:内存泄漏(程序结束才会被 OS 回收,但长期运行会越占越多)
(void)p;
}
int main() {
std::puts("Pick ONE case to run by uncommenting it.");
// case1_null_deref();
// case2_out_of_bounds();
// case3_free_non_heap();
// case4_free_partial_block();
// case5_double_free();
// case6_memory_leak();
return 0;
}
面试:柔性数组(Flexible Array Member, C99)
- 是什么 :结构体最后一个成员写
T data[];,长度运行时决定;sizeof(结构体)不包含这段尾部数据。 - 怎么用 :一次分配"头 + 数据"连续内存:
malloc(sizeof(Struct) + n),用len记录实际长度,free(p)一次释放。 - 用在哪:变长消息/协议包(header 固定、payload 可变)、序列化二进制块、变长字符串缓冲。
- 好处 :一次
malloc/free(更少碎片/更少开销)、内存连续(cache 友好,拷贝/发送方便)。 - 注意 :必须放在结构体最后;不要
free(p->data);sizeof(data)得不到运行时长度;C++ 标准不支持(部分编译器可扩展支持)。
c
typedef struct {
size_t len;
unsigned char data[];
} Packet;
Packet* p = (Packet*)malloc(sizeof(Packet) + n);
if (!p) return;
p->len = n;
// memcpy(p->data, src, n);
free(p);
C/C++中的内存分配

1.选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?__C__全局变量在数据段
staticGlobalVar在哪里?_C_静态全局变量在静态区
staticVar在哪里?__C__静态局部变量在静态区
localVar在哪里?__A__局部变量在栈区
num1 在哪里?__A__局部变量在栈区
char2在哪里?__A__局部变量在栈区
*char2在哪里?__A__数组在栈区
pChar3在哪里?__A__局部变量在栈区
*pChar3在哪里?D *pChar3得到的是字符串常量字符在代码段
ptr1在哪里?__A__局部变量在栈区
*ptr1在哪里?B*ptr1得到的是动态申请空间的数据在堆区
2.填空题:
sizeof(num1) = 40 ; //数组大小,10个整形数据一共40字节
sizeof(char2) = 5 ; //包括\0的空间 strlen(char2) = 4 ;//不包括\0的长度
sizeof(pChar3) = 4 ; //pChar3为指针 strlen(pChar3) = 4 ;//不包括\0的长度
sizeof(ptr1) = 4;//ptr1是指针
总结:
- 局部变量一般在栈上。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
- 动态申请出来的内存在堆上。
- 全局变量、静态变量在静态区/数据段。程序结束后由系统释放。
- malloc/free 是 C 的动态内存管理方式。
- new/delete 是 C++ 的动态内存管理方式。
- new/delete 比 malloc/free 多了"构造和析构"这层能力。
问题:C++中malloc申请的内存可以用delete来释放吗?
malloc 申请的内存不能用 delete释放。malloc和 delete分别属于C标准库和C++的内存管理机制,它们的实现原理不同,混用会导致未定义行为。
正确的用法是使用free释放由malloc申请的内存,delete释放new申请的内存,delete[]释放new[]申请的内存。
深入理解这个问题,需要从内存管理的角度来看malloc 和 delete的本质区别。
malloc是C语言中的函数,用于分配一块未初始化的内存。它只是简单地分配内存,不调用构造函数。所以,用malloc分配的内存只能用free来释放。- new是C++中的运算符,用于分配内存并调用构造函数初始化对象。与之对应,delete既会调用析构函数来清理对象,又会释放内存。这也是为什么用new 分配的内存必须用delete释放的原因。
- C++提供了类型安全和自动管理内存的方式。例如,智能指针(如
std::unique_ptr和std::shared_ptr)能够帮助管理动态分配的内存,防止内存泄漏,推荐使用智能指针来管理内存,而不要使用裸指针。