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

为何出现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),让函数直接操作原始指针的地址。具体步骤:

  1. 修改函数声明,将char **parts改为char ***parts
  2. 函数内部所有访问parts的地方,替换为*parts(解引用操作原始指针)
  3. main调用时,传递parts的地址&parts
  4. 删除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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 19:04:55