Is undefined in C11 modifying the result of a function call or accessing it after the next point in the sequence?

In C99§6.5.2.2p5 there is this little gem I have highlighted to emphasize the question:

If the expression denoting the function to be called has a type pointer to a function that returns the object type, the function call expression has the same type as that object type and has the value defined in 6.8.6.4. Otherwise, the function call is void. If an attempt is made to modify the result of a function call or to access it after the next point in the sequence, the behavior is undefined.

This allowed us to return structs

, for example:

struct foo { int foo;
             char bar[2]; };

struct foo get_foo() {
    struct foo return_value = { .foo = 42,
                                .bar = "x" };
    return return_value;
}

      

... and assign that return value somewhere else inside the caller, like:

int main(void) {
    struct foo bar = get_foo(); /* Well defined because the return value
                                 * is copied -before- the sequence point
                                 * that terminates its storage duration */
    printf("%s\n", bar.bar);
    printf("%d\n", get_foo().foo); /* Again, well defined because the access
                                    * occurs before the next sequence point
                                    * (the function call). */
}

      

... until the following examples are shown:

int main(void) {
    printf("%s\n", get_foo().bar); /* UB because there a sequence point
                                    * between the evaluation of the sub-
                                    * expression `get_foo().bar` and the
                                    * entrace to the function `printf` */
    get_foo().bar[0]++; /* UB because an attempt is made to modify the
                         * result of a function call */
}

      

-

C11§6.5.2.2p5 , however, in essence, this is the same paragraph, but without the bold text.

If the expression denoting the function to be called has a type pointer to a function that returns the object type, the function call expression has the same type as that object type and has the value defined in 6.8.6.4. Otherwise, the function call is void.


Are the above examples of undefined behavior in C99 yet undefined in C11? If so, which paragraphs are invalid? If not, I'm going, there should be some extension to the storage duration of the automatic values ​​/ objects returned; which section of the standard indicates that the extension is the storage duration?

+3


source to share


1 answer


Are the above examples of undefined behavior in C99 yet undefined in C11?

The examples above, which are well defined, are still well defined.

The object's temporary lifetime in this case "ends when the evaluation of the containing full expression or declarator ends", so this previously undefined example is now well defined:

printf("%s\n", get_foo().bar);

      

This example is still undefined as it tries to modify an object with a temporary lifetime:

get_foo().bar[0]++;

      

If so, which paragraphs are invalid? If not, I'm going, there should be some extension to the storage duration of the automatic values ​​/ objects returned; which section of the standard indicates that the extension is the storage duration?

As Jens Gustedt pointed out in a commentary, C11§6.2.4p8 seems to have a slightly different meaning to the suggestion that C99§ 6.5.2.2p5 contains what C11§6.5.2.2p5 is omitted:



A non-lvalue expression with a structure or union type, where the structure or union contains a member with an array type (including recursively, members of all contained structures and unions) refers to an object with automatic storage time and lifetime. 36) Its lifetime starts when the expression is evaluated, and its initial value is the value of the expression. Its lifespan ends when the evaluation of the content of the full expression or full declarator ends. Any attempt to modify an object with a temporary lifetime results in undefined behavior.

36) The address of such an object is taken implicitly when accessing an array element.

There seems to be a slight reorganization; The C99 "Extend Storage Duration" clause has been modified and moved from the Function Call section to the Storage Duration section where it is better suited.

The only question that remains is whether the result of the function call is considered an lvalue. For every operator that produces an lvalue, it seems like it is explicitly mentioned that the operator produces an lvalue. For example, C11§6.5.3.2p6 states that the unary operator *

creates an lvalue by providing its operand points on the object.

The function call operator, however, says nothing about creating an lvalue, so we must assume that it does not give an lvalue. If that's not enough, consider C11§6.5.2.3p3 and p7, which say:

A postfix expression followed by a statement .

and identifier denotes a member of a structure or union object. The value has the named member value 95) and is an lvalue if the first expression is an lvalue.

If f

is a function that returns a structure or union and x

is a member of that structure or union, f().x

is a valid postfix expression, but not an lvalue.

We can also infer from these two paragraphs that the result of the function is not an lvalue, thus meeting the criteria for C11§6.2.4p8 (quoted above).

Footnote 95 is interesting but tangential for discussion:

95) If the element used to read the contents of the union object is not the same as the element that was last used to store the value in the object, the corresponding part of the value object's representation is reinterpreted as a representation of the object in a new type, as described in 6.2.6 (process, sometimes called "type" pun). This can be a trap show.

+4


source







All Articles