Утечка Valgrind и ошибка сегментации с определенным выводом при попытке malloc - PullRequest
1 голос
/ 06 мая 2019

Итак, у меня есть программа, которая получает в качестве входных данных строку в формате 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 по какой-то причине. Я хотел знать, почему это происходит, спасибо!

1 Ответ

3 голосов
/ 06 мая 2019

Вы не проверяете, нашел ли strtok все токены. Вы должны проверить указатель, возвращенный strtok() перед его использованием. Если вы этого не сделаете, неверный ввод вызовет неопределенное поведение.

В вашем случае длина ввода превышает 682 байта, первые 681 байт считываются в массив, и этот фрагмент не содержит достаточно токенов, что делает один из вызовов strtok() для возврата NULL, вызывая неопределенное поведение когда вы разыменовываете этот нулевой указатель с помощью strlen().

Всегда проверяйте и сообщайте об ошибках, вы сэкономите бесчисленные часы отладки.

Когда происходит сбой программы, утечки памяти, о которых сообщает valgrind, не имеют смысла, поскольку программа не завершила свое нормальное выполнение и, конечно, не освободила выделенную память должным образом. Память все еще возвращается в операционную систему после завершения программы, но valgrind сообщает о не освобожденных блоках, вызывая free().

Чтобы избежать произвольного ограничения длины строки, вы можете использовать стандартную функцию POSIX 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;
}

...