在C99§6.5.2.2p5那里有一个小小的宝石,我为了强调这个问题而加粗:
如果表示被调用函数的表达式具有返回对象类型的函数的类型指针,则函数调用表达式与该对象类型具有相同的类型,并且具有在6.8.6.4中指定的值。否则,函数调用的类型为void。 如果尝试修改函数调用的结果或在下一个序列点之后访问它,则行为未定义。
这使我们可以返回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基本上是相同的段落,但没有粗体文字。
如果表示被调用函数的表达式具有返回对象类型的函数的类型指针,则函数调用表达式与该对象类型具有相同的类型,并且具有在6.8.6.4中指定的值。否则,函数调用的类型为void。
上面那些C99中未定义行为的例子在C11中是否仍未定义?如果是这样,哪些段落使它们无效?如果没有,我收集必须延长自动值/对象返回的存储持续时间;标准的哪一部分规定了存储持续时间的延长?
答案 0 :(得分:4)
上面那些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被省略:
具有结构或联合类型的非左值表达式,其中结构或联合包含具有数组类型的成员(包括,递归地,所有包含的结构和联合的成员)是指具有自动存储持续时间和临时生存期的对象。 36)它的生命周期在评估表达式时开始,其初始值是表达式的值。当包含完整表达式或完整声明符的评估结束时,它的生命周期结束。任何使用临时生命周期修改对象的尝试都会导致未定义的行为。
36)访问数组成员时隐式采用此类对象的地址。
似乎进行了一点重组; C99中的“存储持续时间扩展”句子被更改并从“函数调用”部分移动到“存储持续时间”部分,它更适合。
唯一剩下的问题是函数调用的结果是否被视为左值。对于每个产生左值的运算符,似乎都明确提到运算符产生左值。例如,C11§6.5.3.2p6表示一元*
运算符生成一个左值,为对象提供操作数点。
后缀表达式后跟
.
运算符,标识符指定结构或联合对象的成员。该值是指定成员的值, 95),如果第一个表达式是左值,则为左值。如果
f
是返回结构或联合的函数,并且x
是该结构或联合的成员,则f().x
是有效的后缀表达式,但不是左值。< / p>
我们还可以从这两段中推断出函数的结果不是左值,因此符合C11§6.2.4p8的标准(上面引用)。
脚注95很有趣,但与现有讨论相关:
95)如果用于读取union对象内容的成员与上次使用的成员不同 在对象中存储一个值,该值的对象表示的相应部分被重新解释 作为6.2.6中描述的新类型中的对象表示(有时称为''类型的过程) 双关语””)。这可能是陷阱表示。