赋值和指针,未定义的行为?

hel*_*ell 8 c pointers variable-assignment undefined-behavior

int func(int **a)
{
    *a = NULL;
    return 1234;
}

int main()
{
    int x = 0, *ptr = &x;
    *ptr = func(&ptr);      // <-???
    printf("%d\n", x);      // print '1234'
    printf("%p\n", ptr);    // print 'nil'

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是一个未定义行为的例子还是与序列点有关?为什么行:

*ptr = func(&ptr);
Run Code Online (Sandbox Code Playgroud)

表现不像:

*NULL = 1234;
Run Code Online (Sandbox Code Playgroud)

编辑:我忘了提到我用gcc 4.7获得输出'1234'和'nil'.

Ker*_* SB 6

由于赋值运算符的左侧和右侧的评估之间没有序列点,因此不指定是先评估*ptr还是func(&ptr)首先评估.因此,不能保证*ptr允许评估,并且程序具有未定义的行为.


Jon*_*ler 5

我相信这是不明确的行为.该标准没有规定何时评估指配的LHS与RHS相比.如果*ptr在调用函数后进行求值,则将取消引用空指针; 如果在调用函数之前对其进行求值,那么你就会得到理智的行为.

该代码完全没有声誉.不要在实际代码中尝试使用它或任何类似的东西.

请注意,在评估其参数之后,在调用函数之前有一个序列点; 在函数返回之前还有一个序列点.因此,存在与函数参数及其返回值的评估相关的序列点,但是......这在此上下文中是至关重要的......它仍然不会告诉您*ptr在调用函数之前或之后是否进行了求值.要么是可能的; 两者都是正确的; 代码取决于发生的情况,这使得它依赖于未定义的行为.


AnT*_*AnT 5

该语言不保证您在右侧子表达式func(&ptr)

*ptr = func(&ptr);
Run Code Online (Sandbox Code Playgroud)

首先评估,然后评估左侧子表达式*ptr(这显然是您预期发生的事情).打电话之前,可以先合法地评估左侧func.这正是你的情况中发生的事情:*ptr在通话之前得到评估,当时ptr仍然指向x.之后,分配目的地最终确定(即,已知代码将分配给x).一旦发生,更改ptr不再更改分配目的地.

因此,由于未指定的评估顺序,您的代码的直接行为指定.但是,一个可能的评估计划通过导致空指针取消引用而导致未定义的行为.这意味着在一般情况下,行为是未定义的.

如果我不得不用C++语言来模拟这段代码的行为,我会说在这种情况下评估过程可以分成这些基本步骤

1a. int &lhs = *ptr;      // evaluate the left-hand side
1b. int rhs = func(&ptr); // evaluate the right-hand side
2.  lhs = rhs;            // perform the actual assignment
Run Code Online (Sandbox Code Playgroud)

(尽管C语言没有引用,但在内部它使用相同的"运行时绑定左值"概念来存储赋值左侧的评估结果.)语言规范允许足够的自由来制作步骤1a和1b以任何顺序发生.你期望1b首先发生,而你的编译器决定从1a开始.