C语言realloc报invalid pointer错误?实现insertString函数遇问题求助
作业任务要求
- 编写
insertString函数,实现将字符串s2插入到字符串s1的索引n位置的功能 - 约束条件:
s1由malloc动态分配,需要自行调整内存大小,函数返回值类型为void - 现存问题:PC端运行返回
NULL,手机端编译器提示realloc操作的指针无效,无法定位问题原因,需要对照正确实现梳理错误点。
现有代码的核心错误点
- 动态内存地址被直接覆盖,导致
realloc收到非法指针
测试函数中先执行malloc申请了堆内存,随后直接写str1="hoi":这行代码是把代码段中的字符串常量"hoi"的只读地址直接覆盖了str1存储的堆内存地址,原申请的堆内存直接泄漏,后续传给realloc的是只读段的常量地址,根本不是可调整大小的动态内存。这也是不同编译器表现不一致的原因:PC端编译器遇到对只读常量地址执行realloc操作时,往往直接返回NULL,而移动端编译器的内存校验更严格,会直接抛出“realloc操作的指针无效”的崩溃提示。 realloc的内存大小计算完全错误
原代码计算总内存为lengStr2 + n +1,完全没有计入原字符串s1自身的长度需求,既会导致内存分配不足,也会直接覆盖原字符串的后半段内容。- 函数值传递导致内存调整的结果无法传回调用方
C语言中函数参数是值拷贝,insertString的形参str1是函数栈上的局部副本,就算realloc成功返回了新的内存地址,也只会修改这个局部副本,调用方的str1还是保留旧的失效地址,后续打印、释放的时候都会出现非法内存访问。 - 插入逻辑缺失关键步骤
原代码直接从str1+n位置开始覆盖写入s2,没有把原位置及之后的字符整体往后偏移,会直接冲掉原字符串的后半段内容。 - 细节逻辑错误
realloc失败分支中,str1已经是NULL,还执行free(str1)属于无效操作- 函数要求返回值为
void,却写了return -1,不符合语法要求 - 内存拷贝时用
memcpy处理重叠内存,会产生未定义行为。
正确实现参考代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 为了保持返回值为void,通过二级指针将realloc后的新地址同步回调用方 void insertString(char **str1, char *str2, int n){ // 基础参数合法性校验 if (str1 == NULL || *str1 == NULL || str2 == NULL || n < 0) { printf("参数非法,终止插入操作\n"); return; } int lenOriginal = strlen(*str1); int lenStr2 = strlen(str2); // 校验插入索引不能超出原字符串长度 if (n > lenOriginal) { printf("插入索引%d超出原字符串长度%d,操作终止\n", n, lenOriginal); return; } // 计算插入后需要的总内存:原字符串长度 + 插入字符串长度 + 1(存字符串结束符) int totalMemSize = lenOriginal + lenStr2 + 1; // 用临时变量接收realloc结果,避免分配失败时丢失原内存地址造成泄漏 char *reallocRes = (char*)realloc(*str1, totalMemSize * sizeof(char)); if (reallocRes == NULL) { printf("内存重新分配失败\n"); return; } // 将新的内存地址同步回调用方的str1指针 *str1 = reallocRes; // 用memmove处理重叠内存的偏移:把原字符串n位置及之后的内容整体后移lenStr2长度,腾出插入空间 memmove(*str1 + n + lenStr2, *str1 + n, lenOriginal - n + 1); // 将s2拷贝到腾出的位置 memcpy(*str1 + n, str2, lenStr2); } void testInsertString(char *origin, char *str2, int n, char *expect){ // 按原字符串长度申请堆内存 char *str1 = (char*)malloc((strlen(origin) + 1) * sizeof(char)); // 用strcpy将字符串内容拷贝到堆内存,而非直接给指针赋值常量地址 strcpy(str1, origin); printf("\n插入前:str1=%s, str2=%s, 插入位置n=%d\n", str1, str2, n); // 传入str1的地址,让insertString可以修改str1本身的指向 insertString(&str1, str2, n); printf("插入后结果:%s\n", str1); (strcmp(str1, expect) == 0) ? printf("测试通过\n") : printf("测试失败\n"); // 释放调整后的合法堆内存 free(str1); printf("堆内存已安全释放\n"); } int main() { // 测试用例:将"Hallo"插入"hoi"的索引1位置,预期结果为"hHallooi" testInsertString("hoi", "Hallo", 1, "hHallooi"); return 0; }
实现逻辑关键点
- 区分指针赋值和内存拷贝:直接给
char*变量赋值字符串常量地址,会丢失之前申请的堆内存地址,是动态内存操作的高频错误点 realloc操作必须用临时变量存返回值,避免分配失败时丢失原内存的合法地址- 涉及内存块重叠的偏移操作,必须用
memmove替代memcpy,保证拷贝过程不会出现数据覆盖的未定义行为 - 当函数需要修改调用方传入的指针本身的指向(即realloc可能更换内存块地址)时,必须传入二级指针,配合值传递的特性实现地址同步。
内容的提问来源于stack exchange,提问作者Jusauria
相关产品推荐
相关产品推荐

