尝试 malloc 时具有特定输出的 Valgrind 泄漏和分段错误

Valgrind leak and Segmentation Error with a specific output when trying to malloc

所以我有一个程序接收格式为 a word1 word2 word3 的字符串作为输入,并将这些词插入到一个结构中,该结构最终进入链表。对于我已经尝试过的所有输入,它运行完美,没有内存泄漏,但是对于这个特定的输出,我得到了 Segmentation Error 以及内存泄漏,这几乎可以肯定是因为 word1 的长度。 这是输入: a Adolph_Blaine_Charles_David_Earl_Frederick_Gerald_Hubert_Irvin_John_Kenneth_Lloyd_Martin_Nero_Oliver_Paul_Quincy_Randolph_Sherman_Thomas_Uncas_Victor_William_Xerxes_Yancy_Zeus_Wolfeschlegelsteinhausenbergerdorffwelchevoralternwarengewissenhaftschaferswessenschafewarenwohlgepflegeundsorgfaltigkeitbeschutzenvorangreifendurchihrraubgierigfeindewelchevoralternzwolfhunderttausendjahresvorandieerscheinenvonderersteerdemenschderraumschiffgenachtmittungsteinundsiebeniridiumelektrischmotorsgebrauchlichtalsseinursprungvonkraftgestartseinlangefahrthinzwischensternartigraumaufdersuchennachbarschaftdersternwelchegehabtbewohnbarplanetenkreisedrehensichundwohinderneuerassevonverstandigmenschlichkeitkonntefortpflanzenundsicherfreuenanlebenslanglichfreudeundruhemitnichteinfurchtvorangreifenvorandererintelligentgeschopfsvonhinzwischensternartigraum foo@bar.zp 2

这是我的代码:

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

#define MAXINPUT 682

typedef struct words {
    char *word1;
    char *word2;
    char *word3;
} words;

typedef struct node{
    words cont;
    struct node *next;
} node;
typedef node *link;

link head;

void add(char c[]) {
    words x;
    char *str;
    link temp = (link)malloc(sizeof(node));
    strtok(c, " ");
    str = strtok(NULL, " ");
    x.word1 = (char *)malloc(sizeof(char) * (strlen(str) + 1));
    strcpy(x.word1, str);
    str = strtok(NULL, " ");
    x.word2 = (char *)malloc(sizeof(char) * (strlen(str) + 1)); /* where the error happens with this input */
    strcpy(x.word2, str);
    str = strtok(NULL, "[=10=]");
    x.word3 = (char *)malloc(sizeof(char) * (strlen(str) + 1));
    strcpy(x.word3, str);
    temp->cont = x;
    temp->next = head;
    head = temp;
}

int main() {
    char input[MAXINPUT] = " ";
    head = NULL;
    while (input[0] != 'x') {
        fgets(input, MAXINPUT, stdin);
        input[strcspn(input, "\r\n")] = 0;
        if (input[0] == 'a')
            add(input);
        ...

当我 运行 使用此代码输入时,我得到一个 Segmentation Error 并且 valgrind 说有 3 个分配,只有一个空闲,并且 leak\error 发生在行中代码中提到,专门用了strlen。它还说我出于某种原因无法访问内存位置 0x0。我想知道为什么会这样,谢谢!

您不测试 strtok 是否找到了所有标记。在使用之前,您必须检查由 strtok() 编辑的指针 return。如果不这样做,无效输入将导致未定义的行为。

在您的情况下,输入超过 682 个字节,前 681 个字节被读入数组并且此片段不包含足够的标记,因此 strtok() 调用之一 return NULL,当您使用 strlen().

取消引用此空指针时导致未定义的行为

始终测试并报告错误情况,您将为自己节省无数小时的调试时间。

当程序崩溃时,valgrind 报告的内存泄漏是没有意义的,因为程序没有完成正常执行,当然也没有正确释放分配的内存。程序退出后内存仍然returned到操作系统,但是valgrind通过调用free().

报告尚未释放的块

为避免对行长度设置任意限制,您可以使用 POSIX standard function getline() 根据需要重新分配数组。

您还应该使用 strdup 在单个函数调用中分配字符串的副本:

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

typedef struct words {
    char *word1;
    char *word2;
    char *word3;
} words;

typedef struct node{
    words cont;
    struct node *next;
} node;

typedef node *link;  // hiding pointers behind typedefs is not recommended

link head;

link add(char c[]) {
    words x = { NULL, NULL, NULL };
    char *str;
    link temp;

    if (strtok(c, " ") != NULL
    &&  (str = strtok(NULL, " ")) != NULL
    &&  (x.word1 = strdup(str)) != NULL
    &&  (str = strtok(NULL, " ")) != NULL
    &&  (x.word2 = strdup(str)) != NULL
    &&  (str = strtok(NULL, "")) != NULL
    &&  (x.word3 = strdup(str)) != NULL
    &&  (temp = malloc(sizeof(*temp)) != NULL) {
        temp->cont = x;
        temp->next = head;
        return head = temp;
    } else {
        free(x.word3);
        free(x.word2);
        free(x.word1);
        return NULL;
    }
}

int main() {
    char *input = NULL;
    size_t input_size = 0;

    head = NULL;
    while (getline(&input, &input_size, stdin) >= 0 && *input != 'x') {
        input[strcspn(input, "\r\n")] = '[=10=]';
        if (*input == 'a')
            add(input);
            ...
        }
        ...
    }
    free(input);
    ...
    return 0;
}