为什么这段 C 代码会在 macOS 上生成分段错误,而在其他系统上不会?

Why does this C code generate a segmentation fault on macOS, but not on other systems?

在尝试用 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 个元素的插入循环,并将所有元素和各自的内存地址正确显示到屏幕上。

我是不是做错了什么?

您已将 node 声明为指针类型,因此 malloc(sizeof node) 为指针分配了足够的内存,但没有为结构分配足够的内存。如果它成功了,那纯属偶然。

分配指针时要养成的一个好习惯是始终使用以下形式:

fooptr = malloc(sizeof *fooptr);

这样一目了然,您正在分配指针指向的对象的大小,甚至可以在不更改代码的情况下更改类型。

问题是这样的:

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

如果您不想修改任何其他内容,您可以更正它:

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

但是,我想在您的代码中解决一些问题。首先,不要强制转换 malloc,因为它完全没有必要,除非您出于某种原因正在使用 C++ 编译器。

其次,将变量而不是类型作为 malloc 的参数要好得多,因为它避免了代码重复。在这种情况下,它也可以解决您的错误。有了这两件事,你可以这样写:

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

第三,完全没有理由为 node_structurenode 使用不同的名称。改为这样写:

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

What makes this especially weird is that the same code runs without any trouble in other operating systems

这并不奇怪。几乎可以 100% 确定您的代码具有 未定义的行为