数据结构拓展:详解realloc(C++)

前言

在C++中,realloc 是C标准库提供的一个内存管理函数,用于动态调整已分配内存块的大小 。尽管C++更推荐使用 new/delete 或智能指针,但在某些场景(如与C代码交互或底层内存操作)中仍可能用到 realloc。以下是详细分析:

一、realloc 的核心行为

cpp 复制代码
void* realloc(void* ptr, size_t new_size);
  • 功能 :调整 ptr 指向的内存块大小(原内存块由 malloc/calloc/realloc 分配)。
  • 返回值
    • 成功:返回指向新内存块的指针(可能与原指针不同)。
    • 失败:返回 NULL原内存块保持不变
  • 底层逻辑
    1. new_size 为0且 ptr 非空:等价于 free(ptr),返回 NULL
    2. ptrNULL:等价于 malloc(new_size)
    3. 若当前内存块后有空闲空间:直接扩展内存,原数据保留。
    4. 若空间不足:分配新内存块 → 复制原数据 → 释放旧内存块。

二、realloc 在C++中的风险

1. 不适用于非POD类型
  • 问题realloc 不会调用构造函数或析构函数,可能导致对象生命周期管理错误。
cpp 复制代码
struct NonPOD {
    int* ptr;
    NonPOD() { ptr = new int(42); }    // 构造函数分配内存
    ~NonPOD() { delete ptr; }          // 析构函数释放内存
};

NonPOD* arr = (NonPOD*)malloc(10 * sizeof(NonPOD));
// 错误!构造函数未被调用
arr = (NonPOD*)realloc(arr, 20 * sizeof(NonPOD)); 
// 若内存地址改变,旧内存被释放,但析构函数未执行 → 内存泄漏
2. 返回值处理不当导致内存泄漏
  • 错误示例
cpp 复制代码
int* ptr = (int*)malloc(100);
ptr = (int*)realloc(ptr, 200);  // 若 realloc 失败,ptr 被设为 NULL,原内存泄漏!

正确做法:使用临时变量保存结果。

cpp 复制代码
int* tmp = (int*)realloc(ptr, 200);
if (tmp) {
    ptr = tmp;  // 成功后再赋值
} else {
    // 处理失败,原 ptr 仍有效
}

三、C++中的替代方案

1. 使用 new/delete 和手动复制
cpp 复制代码
int* old_arr = new int[10];
int* new_arr = new int[20];
std::copy(old_arr, old_arr + 10, new_arr);
delete[] old_arr;  // 正确释放旧内存
2. 使用 std::vector
  • 推荐方案:自动处理内存分配、复制和释放。
cpp 复制代码
std::vector<int> vec(10);
vec.resize(20);  // 自动重新分配内存并复制数据
3. 智能指针 + realloc 替代方案
  • 若必须使用 realloc,可结合 unique_ptr 自定义删除器:
cpp 复制代码
auto deleter = [](int* ptr) { free(ptr); };
std::unique_ptr<int[], decltype(deleter)> ptr((int*)malloc(100), deleter);
// 调整大小
int* tmp = (int*)realloc(ptr.get(), 200);
if (tmp) {
    ptr.release();  // 解除原有指针管理
    ptr.reset(tmp); // 接管新指针
}

四、realloc 的适用场景

场景 是否推荐 原因
处理POD类型(如基本类型) 谨慎使用 需手动管理内存,但无构造/析构函数风险
非POD类型(类对象) 禁止使用 破坏对象生命周期,导致未定义行为
高性能动态数组 有限使用 可能比 new + 复制更快,但需确保内存连续且类型简单

五、realloc 的优缺点

优点 缺点
可能原地扩展内存,减少复制开销 不适用于C++对象(无法处理构造/析构)
语法简洁 返回值处理不当易导致内存泄漏
兼容C代码 内存分配失败需手动回退
需显式类型转换(C++中强制要求)

六、总结

  • 在C++中尽量避免直接使用 realloc ,优先选择 std::vectorstd::unique_ptr 或手动 new/delete
  • 若必须使用
    1. 仅用于POD类型(如基本类型、结构体无构造/析构函数)。
    2. 严格检查返回值,避免内存泄漏。
    3. 考虑与智能指针结合,增强安全性。

扩展:代码(初阶)

SeqList.h

cpp 复制代码
#pragma once

#include<stdio.h>
#include<stdlib.h>

//定义动态顺序表结构

typedef int SLDataType;

struct SeqList {
	int* arr;
	int capacity;
	int size;
};

typedef struct SeqList SL;

//初始化

void SLInit(SL* ps);

//销毁

void SLDestory(SL* ps);

//插入数据

void SLPushBack(SL* ps, SLDataType x);

void SLPushFront(SL* ps, SLDataType x);

Test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS  1
#pragma warning(disable:6031)

#include"SeqList.h"

void SLTest01() {
	SL s;
	SLInit(&s);
}

int main() {
	SLTest01();
	return 0;
}

SeqList.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS  1
#pragma warning(disable:6031)

#include"SeqList.h"

//初始化

void SLInit(SL* ps) {
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

//销毁

void SLDestory(SL* ps) {
	if (ps->arr != NULL) {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

//插入数据

void SLPushBack(SL* ps, SLDataType x) {
	//判断空间是否充足

	if (ps->size == ps->capacity) {
		//增容
		ps->arr=realloc
	}

	ps->arr[ps->size] = x;
	ps->size++;
}
相关推荐
「QT(C++)开发工程师」1 小时前
C++语言编程规范-并发
java·linux·c++
1白天的黑夜12 小时前
递归-21.合并两个有序链表-力扣(LeetCode)
c++·leetcode·链表·递归
adny-code2 小时前
[fastgrind] 一个轻量级C++内存监控及可视化开源库
c++·内存·性能分析·高性能计算
坚持编程的菜鸟2 小时前
LeetCode每日一题——在区间范围内统计奇数数目
c语言·算法·leetcode
qiuiuiu4132 小时前
正点原子RK3568学习日志6-驱动模块传参
linux·c语言·开发语言·单片机·学习
盛小夏2 小时前
从零开始学C语言:小白也能轻松上手
c语言
郝学胜-神的一滴3 小时前
Linux系统函数link、unlink与dentry的关系及使用注意事项
linux·运维·服务器·开发语言·前端·c++
赵杰伦cpp3 小时前
list的迭代器
开发语言·数据结构·c++·算法·链表·list
老歌老听老掉牙3 小时前
使用 OpenCASCADE 提取布尔运算后平面图形的外轮廓
c++·平面·opencascade
闻缺陷则喜何志丹3 小时前
【动态规划】数位DP的原理、模板(封装类)
c++·算法·动态规划·原理·模板·数位dp