While trying to implement a doubly linked list in C, I noticed that the following snippet would throw a segmentation fault on macOS 10.11 El Capitan. However, when testing in Linux or Haiku, it would run happily, generating the expected results.
#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;
}
This is the console output:
$ 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
As you can see, in the last iteration before the crash, the next
pointer seems to be overwritten by the value what goes into the data
field of the struct (in this example, an integer with value 8).
What makes this especially weird is that the same code runs without any trouble in other operating systems, completing the 10 elements insertion loop and the correct display of all elements and respective memory addresses to the screen.
Am I doing anything wrong here?