Почему этот код C генерирует ошибку сегментации в macOS, но не в других системах? - PullRequest
1 голос
/ 12 июля 2019

При попытке реализовать двусвязный список в C я заметил, что следующий фрагмент кода вызовет ошибку сегментации в macOS 10.11 El Capitan. Тем не менее, при тестировании в Linux или Haiku, он будет работать успешно, генерируя ожидаемые результаты.

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

typedef struct node_structure {
    int data;
    struct node_structure *prev;
    struct node_structure *next;
} *node;

node createNode(int value) {
    node newNode = (node) malloc(sizeof(node));
    if (newNode != NULL) {
        newNode->data = value;
        newNode->prev = NULL;
        newNode->next = NULL;
    }
    return newNode;
}

void displayLinkedList(node linked_list) {
    node cursor = linked_list;
    while (cursor != NULL) {
        printf("DATA: %d \tTHIS:%p \tPREV:%p \tNEXT:%p\n", cursor->data, (void*)cursor, (void *)cursor->prev, (void *)cursor->next);
        cursor=cursor->next;
    }
}

int insertAtHead(node *head, int value) {
    node newHead = createNode(value);
    if(newHead != NULL) {
        (*head)->prev = newHead;
        newHead->next = *head;
        *head = newHead;
        return 0;
    }
    else return 1;
}

int main() {
    printf("\nCreating a single element linked list.\n");
    node head = createNode(10);
    displayLinkedList(head);

    printf("\nInserting 10 elements at head.\n");
    for(int i = 0; i < 10; i++) { 
        insertAtHead(&head, 8); 
    }
    displayLinkedList(head);
    return 0;
}

Это вывод консоли:

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

$ gcc -Wall -pedantic 04_doubly_linked_lists__debugging.c

$ ./a.out

Creating a single element linked list.
DATA: 10        THIS:0x7fd19a403390     PREV:0x0        NEXT:0x0

Inserting 10 elements at head.
DATA: 8         THIS:0x7fd19a403430     PREV:0x0        NEXT:0x7fd19a403420
DATA: 8         THIS:0x7fd19a403420     PREV:0x7fd19a403430     NEXT:0x7fd100000008
Segmentation fault: 11

Как вы можете видеть, на последней итерации перед сбоем указатель next кажется перезаписанным значением, которое входит в поле data структуры (в данном примере, целое число со значением 8) .

Что делает это особенно странным, так это то, что тот же код выполняется без каких-либо проблем в других операционных системах, выполняя цикл вставки из 10 элементов и корректное отображение всех элементов и соответствующих адресов памяти на экране.

Я что-то здесь не так делаю?

Ответы [ 2 ]

4 голосов
/ 12 июля 2019

Проблема заключается в следующем:

node newNode = (node) malloc(sizeof(node));

Если вы не хотите изменять что-либо еще, вы можете исправить это следующим образом:

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

Однако есть нескольковещи, которые я хотел бы рассмотреть в вашем коде.Во-первых, не приводите malloc, так как это совершенно не нужно, если только по какой-то причине вы не используете компилятор C ++.

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

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

В-третьих, совершенно нет причин использовать разные имена для node_structure и node.Вместо этого напишите так:

typedef struct node {
    int data;
    struct node *prev;
    struct node *next;
} *node;

В-четвертых, вы можете использовать typedefs, чтобы скрыть структуры и указатели со спуском (некоторые люди спорят о скрытии указателей таким образом), когда вы создаете интерфейс к библиотеке, но делаетене использовать их в коде, фактически манипулируя ими.Ваше создание должно выглядеть следующим образом:

struct node *createNode(int value) {
    struct node *newNode = malloc(sizeof(*newNode));
    // Same as before in the rest

Что особенно странно, так это то, что тот же код работает без проблем в других операционных системах

Это не странно.Это почти 100% верный признак того, что ваш код имеет неопределенное поведение .

4 голосов
/ 12 июля 2019

Вы объявили node типом указателя, поэтому malloc(sizeof node) выделяет достаточно памяти для указателя, но недостаточно для структуры. Если это когда-либо работало, это было чисто случайно.

Хорошая привычка, при которой нужно указывать, - всегда использовать форму:

fooptr = malloc(sizeof *fooptr);

, чтобы вы могли сразу увидеть, что вы выделяете размер объекта, на который указывает указатель, и вы даже можете изменить тип без изменения этого кода.

...