为何出现segmentation fault?heap-use-after-free错误排查求助
问题根源分析
你遇到的heap-use-after-free错误,核心原因是C语言参数按值传递的特性导致指针失效:
str_explode函数中的parts是局部指针副本,而非指向main中原始指针的引用。- 执行
parts = reallocarray(...)时,新内存地址只会赋值给这个局部副本,main里的原始parts指针仍指向被reallocarray释放的旧内存块。 - 函数返回后,main用旧指针访问
parts[i],就触发了对已释放内存的非法访问,也就是ASAN检测到的堆内存使用后释放错误。
另外还有一个潜在问题:main里的parts[0] = "\0"将字符串字面量赋值给堆分配的指针位置,后续free(parts[i])会尝试释放只读的字面量内存,大概率触发新错误。
修复方案
要让函数能修改main中的原始指针,需要传递指针的指针(char ***parts),让函数直接操作原始指针的地址。具体步骤:
- 修改函数声明,将
char **parts改为char ***parts - 函数内部所有访问
parts的地方,替换为*parts(解引用操作原始指针) - main调用时,传递
parts的地址&parts - 删除main中错误的
parts[0] = "\0"语句
完整修正代码
#include <stdio.h> #include <stdlib.h> #include <string.h> enum EXPLODE_FLAGS { NO_FLAGS=0, FLAG_TRIM=1, // trim each line of the output }; typedef enum RESULT { E_SUCCESS=0, // not a failure but a SUCCESS E_ERROR=1, // failure due to generic error E_ARGS=2, // failed due to arguments E_MALLOC=3 } RESULT; enum EXP_RESULT { EXP_ERROR=-E_ERROR, // generic error EXP_ARGS=-E_ARGS, // generic error with the arguments EXP_MALLOC=-E_MALLOC, // failure due to generic error EXP_SEP=-4, // separator is null EXP_INPUT=-5, // input is a null pointer EXP_OUTPUT=-6, // output is a null pointer }; int str_explode(char *input, char ***parts, const char separator) { int partCounter = 0; int currentPartLength = 0; char *currentPart = NULL; // Check for input validity if (!input) return EXP_INPUT; if (!parts || !*parts) return EXP_OUTPUT; if (separator == '\0') return EXP_SEP; char *start = input; char *currentPartStart = input; char *end = input + strlen(input); fprintf(stdout,"Inside the function\n"); for (char *thischar = start; thischar <= end; thischar++) { if (*thischar == separator || *thischar == '\0') { printf("Inside check; current char is: %c\n",*thischar); // Allocate memory for the length of the current part + null terminator currentPart = calloc(1, currentPartLength + 1); if (!currentPart) { // Use goto for cleanup goto cleanup; } // Copy the current part into the allocated memory if (currentPartLength > 0) { strncpy(currentPart, currentPartStart, currentPartLength); currentPart[currentPartLength] = '\0'; // Null-terminate the string } else { currentPart[0] = '\0'; // Empty string for the case of consecutive separators } // Reallocate memory for another char pointer char **new_parts = reallocarray(*parts, partCounter+1, sizeof(char*)); if (!new_parts) { // Use goto for cleanup goto cleanup_current_part; } *parts = new_parts; printf("About to add current part (%s) to the pile\n",currentPart); // Add the new string part (*parts)[partCounter++] = currentPart; printf("About to check current part from the pile: %s\n",(*parts)[partCounter-1]); // Reset variables for the next part currentPart = NULL; currentPartStart = thischar + 1; // Skip the separator currentPartLength = 0; if('\0'==*thischar) break; } else { ++currentPartLength; } } // currentPart is NULL here, free is harmless but unnecessary free(currentPart); return partCounter; // Label for cleanup cleanup_current_part: fprintf(stderr,"Unable to allocate memory for another part\n"); free(currentPart); cleanup: fprintf(stderr,"Unable to allocate memory for current part\n"); // Free previously allocated memory before returning error for (int i = 0; i < partCounter; i++) { free((*parts)[i]); } free(*parts); *parts = NULL; // Set original pointer to NULL to avoid dangling in main return EXP_MALLOC; } int main(void) { char *input = "apple;orange;banana;grape"; // Initialize with empty array (calloc(1, sizeof(char*)) is okay, but we'll realloc it) char **parts = calloc(1, sizeof(char*)); int partCount = str_explode(input, &parts, ';'); if (partCount < 0) { printf("Error code #%d\n", -partCount); return 1; } printf("Original string: %s\n", input); printf("Number of parts: %d\n", partCount); for (int i = 0; i < partCount; i++) { printf("About to print part #%d:\n",i+1); printf("Part %d: %s\n", i + 1, parts[i]); free(parts[i]); } free(parts); return 0; }
额外优化建议
- 可将
reallocarray替换为更通用的realloc:realloc(*parts, (partCounter+1)*sizeof(char*)) - 函数开头的
if (!*parts)检查能确保传入的指针有效 - cleanup时将
*parts设为NULL,避免main后续误操作已释放的指针
内容的提问来源于stack exchange,提问作者Aethalides
相关产品推荐
相关产品推荐

