在 C11 中是否未定义修改函数调用的结果,或者在下一个序列点之后访问它?
Is it undefined in C11 to modify the result of a function call, or access it after the next sequence point?
在 C99§6.5.2.2p5 有这个小 gem,为了强调这个问题,我把它加粗了:
If the expression that denotes the called function has type pointer to function returning an object type, the function call expression has the same type as that object type, and has the value determined as specified in 6.8.6.4. Otherwise, the function call has type void. If an attempt is made to modify the result of a function call or to access it after the next sequence point, the behavior is undefined.
这使我们能够 return structs
,例如:
struct foo { int foo;
char bar[2]; };
struct foo get_foo() {
struct foo return_value = { .foo = 42,
.bar = "x" };
return return_value;
}
... 并将 return 值分配给调用者中的其他地方,例如:
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). */
}
...同时呈现如下无效的示例:
int main(void) {
printf("%s\n", get_foo().bar); /* UB because there's 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 本质上是同一段落,只是没有粗体文本。
If the expression that denotes the called function has type pointer to function returning an object type, the function call expression has the same type as that object type, and has the value determined as specified in 6.8.6.4. Otherwise, the function call has type void.
上面那些在 C99 中是未定义行为的示例在 C11 中是否仍然未定义?如果是这样,哪些段落使它们无效?如果没有,我认为自动 values/objects returned 的存储时间必须有所延长;标准的哪一部分规定了存储期限的延长?
Are those examples above that are undefined behaviour in C99 still undefined in C11?
上面给出的定义明确的示例仍然定义明确。
这个对象的临时生命周期“在 包含完整表达式或声明符 的计算结束时结束”,因此这个以前未定义的示例现在已明确定义:
printf("%s\n", get_foo().bar);
这个例子仍然是未定义的行为,因为试图修改具有临时生命周期的对象:
get_foo().bar[0]++;
If so, which paragraphs invalidate them? If not, I gather there must be some extension of the storage duration of automatic values/objects returned; which section of the standard specifies that extension of storage duration?
正如 Jens Gustedt 在评论中指出的那样,C11§6.2.4p8 似乎传达了与 C99§6.5.2.2p5 包含而 C11§6.5.2.2p5 省略的句子略有不同的含义:
A non-lvalue expression with structure or union type, where the structure or union contains a member with array type (including, recursively, members of all contained structures and unions) refers to an object with automatic storage duration and temporary lifetime.36) Its lifetime begins when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends when the evaluation of the containing full expression or full declarator ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.
36) The address of such an object is taken implicitly when an array member is accessed.
似乎进行了一些重组; C99 中的“存储持续时间延长”句子已更改并从“函数调用”部分移至“存储持续时间”部分,这样更合适。
剩下的唯一问题是函数调用的结果是否被视为左值。对于每个产生左值的运算符,似乎都明确提到该运算符产生左值。例如,C11§6.5.3.2p6 表示一元运算符 *
产生一个左值,提供其操作数指向的对象。
然而,function call 运算符并未提及生成左值,因此我们必须假设它不会生成左值。如果这还不够好,请考虑 C11§6.5.2.3p3 和 p7,即:
A postfix expression followed by the .
operator and an identifier designates a member of a structure or union object. The value is that of the named member,95) and is an lvalue if the first expression is an lvalue.
If f
is a function returning a structure or union, and x
is a member of that structure or union, f().x
is a valid postfix expression but is not an lvalue.
我们还可以从这两段中推断出函数的结果不是左值,因此符合 C11§6.2.4p8 的标准(上面引用)。
脚注 95 很有趣,但与手头的讨论无关:
95) If the member used to read the contents of a union object is not the same as the member last used to
store a value in the object, the appropriate part of the object representation of the value is reinterpreted
as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type
punning’’). This might be a trap representation.
在 C99§6.5.2.2p5 有这个小 gem,为了强调这个问题,我把它加粗了:
If the expression that denotes the called function has type pointer to function returning an object type, the function call expression has the same type as that object type, and has the value determined as specified in 6.8.6.4. Otherwise, the function call has type void. If an attempt is made to modify the result of a function call or to access it after the next sequence point, the behavior is undefined.
这使我们能够 return structs
,例如:
struct foo { int foo;
char bar[2]; };
struct foo get_foo() {
struct foo return_value = { .foo = 42,
.bar = "x" };
return return_value;
}
... 并将 return 值分配给调用者中的其他地方,例如:
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). */
}
...同时呈现如下无效的示例:
int main(void) {
printf("%s\n", get_foo().bar); /* UB because there's 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 本质上是同一段落,只是没有粗体文本。
If the expression that denotes the called function has type pointer to function returning an object type, the function call expression has the same type as that object type, and has the value determined as specified in 6.8.6.4. Otherwise, the function call has type void.
上面那些在 C99 中是未定义行为的示例在 C11 中是否仍然未定义?如果是这样,哪些段落使它们无效?如果没有,我认为自动 values/objects returned 的存储时间必须有所延长;标准的哪一部分规定了存储期限的延长?
Are those examples above that are undefined behaviour in C99 still undefined in C11?
上面给出的定义明确的示例仍然定义明确。
这个对象的临时生命周期“在 包含完整表达式或声明符 的计算结束时结束”,因此这个以前未定义的示例现在已明确定义:
printf("%s\n", get_foo().bar);
这个例子仍然是未定义的行为,因为试图修改具有临时生命周期的对象:
get_foo().bar[0]++;
If so, which paragraphs invalidate them? If not, I gather there must be some extension of the storage duration of automatic values/objects returned; which section of the standard specifies that extension of storage duration?
正如 Jens Gustedt 在评论中指出的那样,C11§6.2.4p8 似乎传达了与 C99§6.5.2.2p5 包含而 C11§6.5.2.2p5 省略的句子略有不同的含义:
A non-lvalue expression with structure or union type, where the structure or union contains a member with array type (including, recursively, members of all contained structures and unions) refers to an object with automatic storage duration and temporary lifetime.36) Its lifetime begins when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends when the evaluation of the containing full expression or full declarator ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.
36) The address of such an object is taken implicitly when an array member is accessed.
似乎进行了一些重组; C99 中的“存储持续时间延长”句子已更改并从“函数调用”部分移至“存储持续时间”部分,这样更合适。
剩下的唯一问题是函数调用的结果是否被视为左值。对于每个产生左值的运算符,似乎都明确提到该运算符产生左值。例如,C11§6.5.3.2p6 表示一元运算符 *
产生一个左值,提供其操作数指向的对象。
然而,function call 运算符并未提及生成左值,因此我们必须假设它不会生成左值。如果这还不够好,请考虑 C11§6.5.2.3p3 和 p7,即:
A postfix expression followed by the
.
operator and an identifier designates a member of a structure or union object. The value is that of the named member,95) and is an lvalue if the first expression is an lvalue.If
f
is a function returning a structure or union, andx
is a member of that structure or union,f().x
is a valid postfix expression but is not an lvalue.
我们还可以从这两段中推断出函数的结果不是左值,因此符合 C11§6.2.4p8 的标准(上面引用)。
脚注 95 很有趣,但与手头的讨论无关:
95) If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.