【问题标题】:Why does this C code generate a segmentation fault on macOS, but not on other systems?为什么这个 C 代码在 macOS 上会产生分段错误,而在其他系统上却不会?
【发布时间】:2019-11-21 16:02:58
【问题描述】:

在尝试在 C 中实现双向链表时,我注意到以下 sn-p 会在 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 个元素的插入循环,并将所有元素和各自的内存地址正确显示到屏幕上。

我在这里做错了吗?

【问题讨论】:

  • Windows 7 上的 MSVC 也会崩溃。不安全行为的典型症状是 a) 在这里有效,b) 在那里无效。
  • typedefs 中隐藏指针会混淆代码,使其难以理解。
  • 错误malloc(sizeof(node)) 是其结果之一。
  • 参见Is it a good idea to typedef pointers — TL;DR 是“否”。

标签: c macos data-structures segmentation-fault doubly-linked-list


【解决方案1】:

问题是这样的:

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;

第四,当你创建库的接口时,你可以使用 typedef 来隐藏结构和具有下降(有些人争论以这种方式隐藏指针)有效性的指针,但不要在实际操作它们的代码中使用它们。您的创建应如下所示:

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

让这件事特别奇怪的是,相同的代码在其他操作系统中运行没有任何问题

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

【讨论】:

  • node newNode = malloc(sizeof(*newNode)); 确实解决了我的代码中的问题,但我也在考虑您的其他建议。谢谢。
【解决方案2】:

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

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

fooptr = malloc(sizeof *fooptr);

这样您就可以一眼看出您正在分配指针指向的东西的大小,您甚至可以在不更改代码的情况下更改类型。

【讨论】:

  • 实际上,这在这种情况下没有帮助,因为结构包含struct node * 指针。所以结构的大小总是大于2个指针的大小。
猜你喜欢
  • 2016-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 2018-04-19
  • 1970-01-01
相关资源
最近更新 更多