我写了一个小演示程序
#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;
}
并用
gcc -Wall -Wextra -g -O0 example.c -o example
编译它。
在我看来,程序必须在 node **ptr = &(*position)->prev->prev->prev;
行崩溃,因为:
*position
是 root->right
地址;(*position)->prev
是 root
地址;(*position)->prev->prev
是 root
previous
地址,即 NULL;(*position)->prev->prev->prev
是 NULL 取消引用,这必定会导致 segfault
错误。尽管如此,程序感觉很棒,打印“This is Fine”并返回退出代码 0。
有人可以向我解释一下这个黑魔法吗?
线路
node **ptr = &(*position)->prev->prev->prev;
无意义,可以通过优化消除。
有趣的是,添加一行
printf("%p\n",(void*)ptr);
在使该行有意义的行之后没有调用分段错误。
这看起来是因为我们可以读取
(*position)->prev->prev
(这意味着 root_node->prev
并且可以通过将成员 &(*position)->prev->prev->prev
的偏移量添加到 prev
的值来计算地址 (*position)->prev->prev
,而无需实际访问 (*position)->prev->prev->prev
。
另请注意,
malloc
不会初始化分配的缓冲区,并且(*position)->prev->prev
的值不必是NULL
。