Исправлена ​​ошибка загрузки нулевого указателя типа 'Node *'

Я работаю над реализацией структуры данных связанного списка в C. Ниже приведены мои текущие функции для моего файла реализации связанного списка (llist.c).

#include "llist.h"

// Frees all allocated memory associated with the list pointers iteratively
void deleteList(Node **list) {
    Node* ptr = *list;
    Node* temp;

    while(ptr != NULL) {
        free(ptr->data);
        temp = ptr;
        ptr=ptr->next;
        free(temp);
    }
}

// Frees all allocated memory associated with a single node

void deleteNode(Node **toDelete) {
    Node * del = *toDelete;
    free(del->data);
    free(del);
}

// Allocates memory for a new string and returns a pointer to the memory

Node *newNode(char *string) {
    unsigned long len = strlen(string);
    printf("length : %lu \n\n", len);

    Node *temp = (Node*)malloc(sizeof(Node));
    temp->data = (char*)malloc(len + 1*sizeof(char));
    strcpy(temp->data, string);
    temp->next = NULL;

    return temp;
}

// Removes a node from the front of a list

Node *pop(Node **list) {
    Node *newptr = (*list)->next;
    deleteNode(list);
    return newptr;
}

// Adds a node to the front of a list

void push(Node **list, Node *toAdd) {
    toAdd->next = *list;
    *list = toAdd;
}

// Return a list of pointers in order

void reverseOrder(Node **list) {
    Node* prev = NULL;
    Node* current = *list;
    Node* next;

    while (current != NULL) {
        next = current->next;  
        current->next = prev;
        prev = current;
        current = next;
    }

    *list = prev;
}

// Prints the string stored in a single node

void printNode(Node *singleNode) {
    printf("Data : %s", singleNode->data);
}

// Prints an entire linked list. Nodes are printed from first to last

void printLinkedList(Node *linkedList) {
    Node *temp = linkedList;
    
    while(temp!=NULL) {
        printf("Data : %s", temp->data);
        temp = temp->next;
    }
}

При тестировании реализации в моем файле драйвера я получаю следующую ошибку

ошибка времени выполнения: загрузка нулевого указателя типа «узел *» (он же «структура listNode *») ОБЗОР: UndefinedBehaviorSanitizer: undefined-behavior llist.c:49:19

где строка 49 соответствует toAdd->next = *list в файле llist.c

Я изо всех сил пытаюсь понять, почему эта ошибка возникает, когда я вызываю свою функцию push с соответствующими параметрами для изначально пустого (NULL) связанного списка.

файл драйвера (testllist.c) для справки:

#include "llist.h"

int main (int argc, char *argv[]) {
    printf("argc: %d", argc);
    printf("\n\n");

    int num_inputs = argc;
    Node **list = NULL;

    if (argc == 1) {
        printf("No arguments passed.\n");
    } else {
        for (int i = 1; i < num_inputs; i++) {
            printf("String is: %s\n", argv[i]);
            Node *n = newNode(argv[i]);

            printf("String is : %s\n\n", argv[i]);

            push(list, n);

            printLinkedList(*list);
        }


        reverseOrder(list);
        pop(list);
        deleteList(list);
    }
    
    return 0;
}

заголовочный файл (llist.h), в котором определяются тип данных и функции Node.

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

// The listNode data type for storing entries in a linked list
typedef struct listNode Node;
struct listNode {
char *data;
Node *next;
};

// Frees all allocated memory associated with the list pointers iteratively
void deleteList(Node **list);

// Frees all allocated memory associated with a single node
void deleteNode(Node **toDelete);

// Allocates memory for a new string and returns a pointer to the memory
Node *newNode(char *string);

// Removes a node from the front of a list and returns a pointer to said node
Node *pop(Node **list);

// Adds a node to the front of a list
void push(Node **list, Node *toAdd);

// Return a list of pointers in order
void reverseOrder(Node **list);

// Prints the string stored in a single node
void printNode(Node *singleNode);

// Prints an entire linked list. Nodes are printed from first to last
void printLinkedList(Node *linkedList);

person Mana    schedule 08.02.2021    source источник
comment
Где вы присваиваете значение Node **list? Он инициализирован как NULL и, вероятно, не назначен.   -  person i486    schedule 08.02.2021


Ответы (1)


Когда вы инициализировали список указателей как нулевой указатель

Node **list = NULL;

то внутри функции push вы не можете разыменовывать этот указатель

void push(Node **list, Node *toAdd) {
    toAdd->next = *list;
    *list = toAdd;
}

И сообщение об ошибке сообщает об этой проблеме.

Вы должны объявить указатель как

Node *list = NULL;

и передать его функциям, которые ожидают объект типа Node **, например,

push( &list, n );

И было бы намного лучше, если бы функция была объявлена ​​как

int push( Node **list, const char * );

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

Обратите внимание, например, что функция deleteNode не имеет особого смысла.

void deleteList(Node **list) {
    Node* ptr = *list;
    Node* temp;

    while(ptr != NULL) {
        free(ptr->data);
        temp = ptr;
        ptr=ptr->next;
        free(temp);
    }
}

Указатель на головной узел списка передается по ссылке. Однако внутри функции его значение не изменяется. Таким образом, после выхода из функции указатель на головной узел по-прежнему будет иметь исходное значение.

Функция может быть определена следующим образом

void deleteList( Node **list ) 
{
    while ( *list != NULL )
    {
        Node *ptr = *list;
        *list = ( *list )->next;
        free( ptr->data );
        free( ptr );
    }
}

Функция pop не проверяет, равен ли переданный указатель на головной узел списка NULL.

Node *pop(Node **list) {
    Node *newptr = (*list)->next;
    deleteNode(list);
    return newptr;
}

Также он возвращает указатель на следующий узел в списке, который становится текущим головным узлом. Но возвращенный указатель не используется в основном

pop(list);

Обратите внимание, что выражение 1 * sizeof( char ) не имеет смысла в выражении, используемом в качестве инициализатора в этом объявлении.

temp->data = (char*)malloc(len + 1*sizeof(char));

Либо напишите

temp->data = (char*)malloc(len + 1);

или нравится

temp->data = (char*)malloc( ( len + 1 )*sizeof(char));
person Vlad from Moscow    schedule 08.02.2021