“神奇地”避免段错误

问题描述 投票:0回答:1

我写了一个小演示程序

#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;
行崩溃,因为:

  1. *position
    root->right
    地址;
  2. (*position)->prev
    root
    地址;
  3. (*position)->prev->prev
    root
    previous
    地址,即 NULL;
  4. (*position)->prev->prev->prev
    是 NULL 取消引用,这必定会导致
    segfault
    错误。

尽管如此,程序感觉很棒,打印“This is Fine”并返回退出代码 0。

有人可以向我解释一下这个黑魔法吗?

c pointers tree segmentation-fault pointer-to-pointer
1个回答
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

© www.soinside.com 2019 - 2024. All rights reserved.