为什么我会收到堆释放后使用错误?

问题描述 投票:0回答:1

为什么我会出现分段错误?当我使用 sanitize=address 进行编译时,我得到了一个我不太明白的堆使用后释放(原因)。 我在地址 xyz 上得到堆释放后使用。

在 ... test.c:22 中读取大小 8(打印 parts[i] 的行)

...位于此处释放的 8 字节区域内的 0 字节(strings.c:175,这是 reallocarray 行)

...之前在这里分配(在 main test.c:9 中,即 char **parts=calloc.. 行)

此示例独立编译:

#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) 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
            parts=reallocarray(parts,partCounter+1,sizeof(char*));
            if (!parts) {
                // Use goto for cleanup
                goto cleanup_current_part;
            }
            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;
        }
    }

    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);

    return EXP_MALLOC;
}

int main(void) {
    char *input = "apple;orange;banana;grape";
    char **parts = calloc(1,1*sizeof(char*));
    parts[0]="\0";
    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;   
}

请记住,我不是一位经验丰富的 C 程序员。我有指针的工作知识,但我无法理解我在这里做错了什么。 这个小程序的目的是提高我对 C 中字符数组的理解。

c malloc dynamic-memory-allocation realloc
1个回答
0
投票

在 C 中,参数按值传递,这意味着对

parts
的更改在返回
str_explode()
时会丢失(并泄漏)。最小的修复方法是传入
parts
的地址并将参数更改为
char ***parts
并将所有用途更新为
(*parts)

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) 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') {
            currentPartLength++;
            continue;
        }
        printf("Inside check; current char is: %c\n",*thischar);
        currentPart = calloc(1, currentPartLength + 1);
        if (!currentPart)
            goto cleanup;
        if (currentPartLength > 0) {
            strncpy(currentPart, currentPartStart, currentPartLength);
            currentPart[currentPartLength] = '\0';
        } else
            currentPart[0] = '\0';
        *parts=reallocarray(*parts,partCounter+1,sizeof(char*));
        if (!*parts)
            goto cleanup_current_part;
        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;
    }

    free(currentPart);
    return partCounter;
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");
    for (int i = 0; i < partCounter; i++)
        free((*parts)[i]);
    free(*parts);
    return EXP_MALLOC;
}

int main(void) {
    // ...
    int partCount = str_explode(input, &parts, ';');
    // ....
}

这会让你被贴上三星级程序员的标签(不是正面的)。两个好的重新设计是返回

parts
并使用哨兵
NULL
来表示没有更多元素。另一个不错的选择是创建一个结构体来保存指针和计数:

struct result {
   char **parts;
   int partCount;
}

您可以返回或用作输出参数(即传递它的地址)。

© www.soinside.com 2019 - 2024. All rights reserved.