Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assignment and pointers, 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;
}

Is this an example of undefined behavior or has to do with sequence points? why the line:

*ptr = func(&ptr);

doesn't behave like:

*NULL = 1234;

EDIT: I forgot to mention that I get the output '1234' and 'nil' with gcc 4.7.

like image 564
hello_hell Avatar asked Jun 14 '13 18:06

hello_hell


2 Answers

Since there is no sequence point between evaluations of the left and right hand sides of the assignment operator, it is not specified whether *ptr or func(&ptr) is evaluated first. Thus it is not guaranteed that the evaluation of *ptr is allowed, and the program has undefined behaviour.

like image 144
Kerrek SB Avatar answered Sep 27 '22 21:09

Kerrek SB


The language does not guarantee you that the right-hand side subexpression func(&ptr) in

*ptr = func(&ptr);

is evaluated first, and the left-hand side subexpression *ptr is evaluated later (which is apparently what you expected to happen). The left-hand side can legally be evaluated first, before call to func. And this is exactly what happened in your case: *ptr got evaluated before the call, when ptr was still pointing to x. After that the assignment destination became finalized (i.e. it became known that the code will assign to x). Once it happens, changing ptr no longer changes the assignment destination.

So, the immediate behavior of your code is unspecified due to unspecified order of evaluation. However, one possible evaluation schedule leads to undefined behavior by causing a null pointer dereference. This means that in general case the behavior is undefined.

If I had to model the behavior of this code in terms of C++ language, I'd say that the process of evaluation in this case can be split into these essential steps

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

(Even though C language does not have references, internally it uses the same concept of "run-time bound lvalue" to store the result of evaluation of left-hand side of assignment.) The language specification allows enough freedom to make steps 1a and 1b to occur in any order. You expected 1b to occur first, while your compiler decided to start with 1a.

like image 39
AnT Avatar answered Sep 27 '22 20:09

AnT