I wrote a small demo program
#include <stdlib.h>
#include <stdio.h>
typedef struct tag_node {
struct tag_node *left, *right, *prev;
int value;
} node;
int main()
{
node *root_node = malloc(sizeof(node));
root_node->value = 1;
node **position = &root_node->right;
*position = malloc(sizeof(node));
(*position)->value = 2;
(*position)->prev = root_node;
printf("root node is %d, right node is %d\n",
root_node->value, root_node->right->value);
node **ptr = &(*position)->prev->prev->prev;
printf("This is fine\n");
return 0;
}
and compiled it with gcc -Wall -Wextra -g -O0 example.c -o example
.
In my opinion the program must crash at the node **ptr = &(*position)->prev->prev->prev;
line because:
*position
is the root->right
address;(*position)->prev
is the root
address;(*position)->prev->prev
is the root
previous
address, i.e., NULL;(*position)->prev->prev->prev
is NULL dereferencing, which must cause a segfault
error.Nevertheless the program feels great, prints "This is fine" and returns exit code 0.
Could someone please explain this black magic to me?
The line
node **ptr = &(*position)->prev->prev->prev;
is meaningless and can be eliminated by the optimization.
Interestingly, adding a line
printf("%p\n",(void*)ptr);
after the line to make the line meaningfull also didn't invoke segmentation fault.
This looks like because we can read (*position)->prev->prev
(this means root_node->prev
) and can calculate the address &(*position)->prev->prev->prev
by just adding the offset of the member prev
to the value of (*position)->prev->prev
without actually accessing (*position)->prev->prev->prev
.
Also note that malloc
doesn't initialize the allocated buffer and the value of (*position)->prev->prev
need not be NULL
.