当前位置: 首页 > article >正文

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

前言

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

一、realloc 的核心行为

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

二、realloc 在C++中的风险

1. 不适用于非POD类型
  • 问题realloc 不会调用构造函数或析构函数,可能导致对象生命周期管理错误。
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. 返回值处理不当导致内存泄漏
  • 错误示例
int* ptr = (int*)malloc(100);
ptr = (int*)realloc(ptr, 200);  // 若 realloc 失败,ptr 被设为 NULL,原内存泄漏!

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

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

 

三、C++中的替代方案

1. 使用 new/delete 和手动复制
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
  • 推荐方案:自动处理内存分配、复制和释放。
std::vector<int> vec(10);
vec.resize(20);  // 自动重新分配内存并复制数据

 

3. 智能指针 + realloc 替代方案
  • 若必须使用 realloc,可结合 unique_ptr 自定义删除器:
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

#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

#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

#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++;
}

http://www.kler.cn/a/574418.html

相关文章:

  • 【RabbitMQ】Spring Boot 结合 RabbitMQ 完成应用间的通信
  • HCIA-IP路由动态-RIP
  • 计算机网络笔记(一)——1.1计算机网络在信息时代中的作用
  • SpringBoot集成Sentry日志收集-1 (Sentry安装)
  • 论文粗读——Isometric 3D Adversarial Examples in the Physical World
  • Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
  • Linux网络环境配置及常用命令
  • JavaScript 基础语法
  • 【区块链 + 绿色低碳】东方易电城市微电网智能平台 | FISCO BCOS 应用案例
  • C++ 单词识别_牛客题霸_牛客网
  • flink重启策略
  • JMeter 断言最佳实践
  • plt和cv2有不同的图像表示方式和颜色通道顺序
  • pytorch3d学习(一)——开始(架构概述、输入数据、相机坐标系、纹理渲染)
  • Golang的网络流量控制
  • 【每日八股】Redis篇(三):持久化(上)
  • 自律linux 第 34 天
  • 怎么做数据冷热分离?怎么做分库分表?为什么要用ES?
  • 大模型——模型上下文协议 (MCP)
  • 配电柜/环网柜温湿度控制装置 功能参数介绍