在 C99§6.5.2.2p5 有一个小 gem ,我加粗以强调这个问题:
这允许我们返回 structs
,例如:
struct foo { int foo;
char bar[2]; };
struct foo get_foo() {
struct foo return_value = { .foo = 42,
.bar = "x" };
return return_value;
}
...并从调用者内部的其他地方分配该返回值,例如:
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 本质上是相同的段落,但没有粗体文本。
上面那些在 C99 中未定义行为的示例在 C11 中是否仍然未定义?如果是这样,哪些段落使它们无效?如果没有,我认为返回的自动值/对象的存储持续时间必须有所延长;标准的哪一部分规定了存储期限的延长?
最佳答案
上面提出的定义明确的例子仍然是明确定义的。
这个对象的临时生命周期“在包含完整表达式或声明符的评估结束时结束”,所以这个以前未定义的例子现在已经很好地定义了:
printf("%s\n", get_foo().bar);
此示例仍然是未定义的行为,因为尝试修改具有临时生命周期的对象:get_foo().bar[0]++;
正如 Jens Gustedt 在评论中指出的那样,C11§6.2.4p8 似乎传达了与 C99§6.5.2.2p5 包含 C11§6.5.2.2p5 省略的句子略有不同的含义:
似乎进行了一些重组; C99 中的“存储持续时间扩展”语句已更改,从“函数调用”部分移至“存储持续时间”部分,在那里更合适。
剩下的唯一问题是函数调用的结果是否被视为左值。对于每个产生左值的运算符,似乎都明确提到该运算符产生左值。例如,C11§6.5.3.2p6 声明一元
*
运算符产生一个左值,提供它的操作数指向一个对象。然而,函数调用运算符并没有说明产生一个左值,所以我们必须假设它不产生一个左值。如果这还不够好,那么考虑 C11§6.5.2.3p3 和 p7,它们说:
我们还可以从这两段推导出函数的结果不是左值,因此满足 C11§6.2.4p8(上面引用的)的标准。
脚注 95 很有趣,但与手头的讨论无关:
关于c - 在 C11 中是否未定义修改函数调用的结果,或者在下一个序列点之后访问它?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31128852/