数据结构拓展:详解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++;
}
相关推荐
Dream it possible!5 小时前
LeetCode 热题 100_字符串解码(71_394_中等_C++)(栈)
c++·算法·leetcode
Kurbaneli6 小时前
深入理解 C 语言函数的定义
linux·c语言·ubuntu
Archer1946 小时前
C语言——链表
c语言·开发语言·链表
My Li.6 小时前
c++的介绍
开发语言·c++
夜晚中的人海6 小时前
【C语言】------ 实现扫雷游戏
android·c语言·游戏
面会菜.6 小时前
C语言(队列)
c语言·开发语言
开心比对错重要6 小时前
leetcode69.x 的平方根
数据结构·算法·leetcode
小猿_007 小时前
C语言单链表头插法
c语言·开发语言
Doopny@7 小时前
数字组合(信息学奥赛一本通-1291)
数据结构·算法·动态规划
君莫愁。7 小时前
【Unity】搭建基于字典(Dictionary)和泛型列表(List)的音频系统
数据结构·unity·c#·游戏引擎·音频