【问题标题】:Valgrind leak and Segmentation Error with a specific output when trying to malloc尝试 malloc 时具有特定输出的 Valgrind 泄漏和分段错误
【发布时间】:2019-09-24 11:23:25
【问题描述】:

所以我有一个程序,它接收一个格式为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, "\0");
    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 个分配器,只有一个空闲,并且泄漏\错误发生在代码中提到的行中,特别是 strlen。它还说由于某种原因我无法访问内存位置 0x0。我想知道为什么会这样,谢谢!

【问题讨论】:

    标签: c list pointers memory-leaks valgrind


    【解决方案1】:

    您不测试strtok 是否找到了所有令牌。使用前必须检查strtok() 返回的指针。如果不这样做,无效的输入将导致未定义的行为。

    在您的情况下,输入长度超过 682 个字节,前 681 个字节被读入数组,并且该片段不包含足够的令牌,导致 strtok() 调用之一返回 NULL,导致未定义的行为当您使用 strlen() 取消引用此空指针时。

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

    当程序崩溃时,valgrind 报告的内存泄漏是没有意义的,因为程序没有完成它的正常执行,当然也没有正确释放分配的内存。程序退出后内存仍然归还给操作系统,但是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")] = '\0';
            if (*input == 'a')
                add(input);
                ...
            }
            ...
        }
        free(input);
        ...
        return 0;
    }
    
    

    【讨论】:

    • 到目前为止,OP 从未接受过答案......可能会建议 OP 使用 ssize_t getline(char **lineptr, size_t *n, FILE *stream); 而不是 fgets 来取消限制? (使用 valgrind 他可能使用符合 POSIX 的编译器)
    猜你喜欢
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-24
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    相关资源
    最近更新 更多