C/C++动态内存管理,malloc,calloc,realloc的区别,动态内存中的错误汇总

🎬 胖咕噜的稞达鸭个人主页
🔥 个人专栏 : 《数据结构《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);

三者核心区别总结

  1. 是否清零

malloc:不清零(未初始化)
calloc:清零
realloc:扩容时 新增部分不保证清零;缩容则截断

  1. 参数形式

malloc(size)

calloc(count, elemSize)(更不容易写错"元素个数 * 大小")

realloc(ptr, newSize)

  1. 是否改变地址

malloc/calloc:新分配,必然是新指针

realloc:可能返回同一地址,也可能返回新地址(旧指针失效)

  1. 失败语义

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 思路)

常见的动态内存的错误:

  1. 对NULL指针的解引用操作
  2. 对动态开辟空间的越界访问
  3. 对非动态开辟内存使用free释放

4.使用free释放一块动态开辟内存的一部分

  1. 对同一块动态内存多次释放

  2. 动态开辟内存忘记释放(内存泄漏)

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是指针

总结:

  1. 局部变量一般在栈上。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
  2. 动态申请出来的内存在堆上。
  3. 全局变量、静态变量在静态区/数据段。程序结束后由系统释放。
  4. malloc/free 是 C 的动态内存管理方式。
  5. new/delete 是 C++ 的动态内存管理方式。
  6. new/delete 比 malloc/free 多了"构造和析构"这层能力。

问题:C++中malloc申请的内存可以用delete来释放吗?

malloc 申请的内存不能用 delete释放。malloc和 delete分别属于C标准库和C++的内存管理机制,它们的实现原理不同,混用会导致未定义行为。

正确的用法是使用free释放由malloc申请的内存,delete释放new申请的内存,delete[]释放new[]申请的内存。

深入理解这个问题,需要从内存管理的角度来看mallocdelete的本质区别。

  1. malloc是C语言中的函数,用于分配一块未初始化的内存。它只是简单地分配内存,不调用构造函数。所以,用malloc分配的内存只能用free来释放。
  2. new是C++中的运算符,用于分配内存并调用构造函数初始化对象。与之对应,delete既会调用析构函数来清理对象,又会释放内存。这也是为什么用new 分配的内存必须用delete释放的原因。
  3. C++提供了类型安全和自动管理内存的方式。例如,智能指针(如 std::unique_ptrstd::shared_ptr)能够帮助管理动态分配的内存,防止内存泄漏,推荐使用智能指针来管理内存,而不要使用裸指针。
相关推荐
澈2072 分钟前
C++多态编程:从原理到实战
开发语言·c++
今天又在写代码9 分钟前
并发问题解决
java·开发语言·数据库
聆风吟º10 分钟前
【C标准库】深入理解C语言strcat函数:字符串拼接的利器
c语言·开发语言·strcat·库函数
6Hzlia13 分钟前
【Hot 100 刷题计划】 LeetCode 24. 两两交换链表中的节点 | C++ 精准指针舞步
c++·leetcode·链表
带娃的IT创业者14 分钟前
深度解析:从零构建高性能 LLM API 中转网关与成本优化实战
开发语言·gpt·llm·php·高性能·成本优化·api网关
TechWayfarer26 分钟前
IP归属地运营商能解决什么问题?风控/增长/数据平台落地实践(附API代码)
开发语言·网络·python·网络协议·tcp/ip
Alice-YUE44 分钟前
【JS高频八股】什么是闭包?
开发语言·javascript·笔记·学习
微学AI44 分钟前
Claude-Code-python 前端改造项目工作流程详解
开发语言·前端·python
汉克老师44 分钟前
GESP2025年6月认证C++五级( 第一部分选择题(9-15))
c++·贪心算法·分治算法·二分算法·gesp5级·gesp五级·高精度除法