Почему Valgrind жалуется на этот код? - PullRequest
0 голосов
/ 22 октября 2018

Я пытаюсь реализовать связанный список, в котором хранятся неотрицательные целые числа.Моя реализация выглядит следующим образом:

Мне было любопытно по поводу утечек памяти, поэтому я попробовал этот инструмент под названием Valgrind с командой "valgrind --leak-check = yes".

==2540== error calling PR_SET_PTRACER, vgdb might block
==2540== Invalid write of size 4
==2540==    at 0x10875E: node_create (in LinkedList/bin/main)
==2540==    by 0x108832: list_append (in LinkedList/bin/main)
==2540==    by 0x108920: main (in LinkedList/bin/main)
==2540==  Address 0x522d098 is 0 bytes after a block of size 8 alloc'd
==2540==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2540==    by 0x10874B: node_create (in LinkedList/bin/main)
==2540==    by 0x108832: list_append (in LinkedList/bin/main)
==2540==    by 0x108920: main (in LinkedList/bin/main)
   .
   .
   .
==2540== Invalid read of size 4
==2540==    at 0x1088BA: list_pop (in LinkedList/bin/main)
==2540==    by 0x1089E1: main (in LinkedList/bin/main)
==2540==  Address 0x522d138 is 0 bytes after a block of size 8 alloc'd
==2540==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2540==    by 0x10874B: node_create (in LinkedList/bin/main)
==2540==    by 0x108832: list_append (in LinkedList/bin/main)
==2540==    by 0x108942: main (in LinkedList/bin/main)
   .
   .
   .
==2540== HEAP SUMMARY:
==2540==     in use at exit: 0 bytes in 0 blocks
==2540==   total heap usage: 10 allocs, 10 frees, 584 bytes allocated
==2540==
==2540== All heap blocks were freed -- no leaks are possible

СоответствующийФункции реализованы так:

struct Node {
    struct Node* next;
    int value;
};

struct List {
    struct Node* head;
};

typedef struct Node* Node;
typedef struct List* List;

Node node_create(int value, Node nextNode) {
    if(value < 0) {
        printf("Error: Could not create node, value is negative.\n");
        return NULL;
    }

    Node node = malloc(sizeof(Node));
    if(node != NULL)
    {
        node->value = value;
        node->next = nextNode;
    } else {
        printf("Error: Could not create node, malloc returned NULL.\n");
    }

    return node;
}

int list_append(List listHandle, int value) {
    Node current = listHandle->head;
    Node new = node_create(value, NULL);

    if(new == NULL) {
        return -1;
    }

    if(current == NULL) {
        listHandle->head = new;
    } else {
        while(current->next != NULL) {
            current = current->next;
        }
        current->next = new;
    }

    return value;
}

int list_pop(List listHandle) {
    if(listHandle->head == NULL) {
        printf("Error: Trying to pop an empty list.\n");
        return -1;
    }

    Node temp = listHandle->head;
    int value = temp->value;

    if(temp->next == NULL)
    {
        listHandle->head = NULL;
    } else {
        listHandle->head = temp->next;
    }

    free(temp);
    return value;
}

Что я делаю не так?Как я могу улучшить код?Это даже проблема или Вальгринд просто чрезмерно педантичен?

Ответы [ 2 ]

0 голосов
/ 22 октября 2018
typedef struct Node* Node;

Node node = malloc(sizeof(Node));

Это выделит sizeof(Node) == sizeof(struct Node*) байтов памяти.Таким образом, Node node указывает не на sizeof(struct Node) байтов памяти.В итоге вы получите доступ к памяти из-за ограничения / недействительности.

Чтобы исправить ваш код, разыменуйте указатель на узел структуры или неявно используйте узел структуры с sizeof:

Node node = malloc(sizeof(*node));
Node node = malloc(sizeof(struct Node));

Этотолько исправление.Это делает ваш код более запутанным, и вы только что обнаружили, почему скрытый указатель за typedef - плохая идея.Строка:

Node node = malloc(sizeof(*Node));

не будет работать, так как Node называет тип, не может быть разыменована, как указано @Ctx в комментариях.

Лично я настоятельно рекомендуюПерепишите весь ваш код для использования:

 typedef struct Node Node;

 Node *node_create(int value, Node *nextNode)  {
      ...
      Node *node = malloc(sizeof(Node));
      ...
 }

Теперь любой программист, сразу смотрящий на функцию node_create, будет знать, что он возвращает указатель на некоторые данные, возможно, динамически размещенные.Является более читабельным и не скрывает назначение указателя.

0 голосов
/ 22 октября 2018
Node node = malloc(sizeof(Node));

Node на самом деле struct Node * - указатель .БУМ!Вы просто выделили память для одного указателя, а не для своей структуры, которая требует как минимум sizeof(int) байт больше.Вот почему вы не typedef указатели.

...