You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言realloc缩减内存后旧内容残留及堆溢出问题咨询

realloc缩减内存分配后的字符串行为与堆溢出问题

问题现象

使用realloc缩减内存分配大小后,程序仍会打印原字符串的全部内容,直到手动在字符串末尾添加空字节(NUL)才停止;开启-fsanitize=address编译选项时,会触发heap-buffer-overflow错误。

核心疑问

既然realloc会释放多余的内存块,为何上述代码会出现溢出问题?此外,有人称无法确定realloc是否一定会释放多余块,也有人说无需关心旧内容——既然无需关心旧内容,为何代码仍会打印全部旧内容?(使用C99标准)

示例代码

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

#define NUL '\0'

void check_allocation_success(char *ptr);

int main(void) {
   char *str1, *str2;
   str1 = malloc(sizeof(char)*17);

   check_allocation_success(str1);
   strncpy(str1, "0123456789ABCDEF", 16);

   str2 = realloc(str1, 8);
   check_allocation_success(str2);

   printf("%s", str2);
   free(str2);
   return 0;
}

void check_allocation_success(char *ptr) {
   if (ptr == NULL) {
      printf("%s : %d : allocation failed.\n", __FILE__, __LINE__);
      exit(EXIT_FAILURE);
   }
}

问题解析

1. 为何能打印全部旧内容?

realloc缩减内存时,只是告知内存分配器:程序不再需要超出新大小的内存区域,但它不会主动清除旧数据。这些数据可能暂时留在原内存位置,直到被其他内存操作覆盖——这就是你能看到完整旧字符串的原因。但注意:访问超出新分配大小的内存属于未定义行为,不能依赖这种现象,不同分配器、系统或编译选项都可能导致不同结果。

2. 堆溢出错误的原因

printf("%s", str2)会持续读取内存直到遇到NUL字节。你的原字符串通过strncpy复制了16个字符,没有自动添加NUL终止符;realloc将内存缩减到8字节后,只有前8字节是程序合法可访问的范围。printf继续读取第9字节及以后的非法内存,直接触发了AddressSanitizer的溢出检测——这是完全合理的,因为你确实访问了不属于当前分配的内存。

3. 关于realloc是否释放多余内存的说明

C99标准对realloc的定义是:当新大小小于原大小时,实现可选择释放多余内存,也可保留原内存块,但程序仅能合法访问新大小范围内的区域。也就是说,不能假设多余内存一定会被释放或保留,标准只保证新大小内的数据有效,超出部分的状态是未定义的。

正确解决方式

在realloc后,必须手动在新内存的末尾添加NUL终止符,确保字符串的合法性:

str2 = realloc(str1, 8);
check_allocation_success(str2);
// 仅保留前7个可见字符,第8字节放置NUL终止符
str2[7] = NUL;

这样printf只会读取合法的7个字符,既不会触发溢出错误,也不会依赖未定义的旧数据。

内容的提问来源于stack exchange,提问作者Bishnu Chalise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:01:44