考虑:
\ntypedef struct { int a[1]; } S;\nconst S foo(void) { return (S) {{3}}; }\nvoid bar(void) { int *p = foo().a; }\nRun Code Online (Sandbox Code Playgroud)\n根据 C 2018 6.2.4 8,该表达式foo()引用具有临时生命周期的对象:
\n\n结构体或联合体类型的非左值表达式,其中结构体或联合体包含数组类型的成员(递归地包括所有包含的结构体和联合体的成员),是指具有自动存储期限和临时生存期的对象。它的生命周期从表达式被求值时开始,它的初始值是表达式的值。当包含的完整表达式的计算结束时,它的生命周期结束。任何修改具有临时生命周期的对象的尝试都会导致未定义的行为。具有临时生存期的对象的行为就像是为了有效类型而使用其值的类型进行声明一样。这样的对象不需要有唯一的地址。
\n
请注意,代码不会尝试修改对象,但它会int *使用 的第一个元素的地址初始化 an foo().a,我们希望该元素具有const- 限定。这违反了 6.5.16.1 1 中的约束,即要求指向的目标类型具有指向的源类型的所有限定符。(该段落用于分配,但已被 6.7.9 11 合并,其中涵盖了初始化。)
然而,GCC 和 Clang 都没有抱怨这种初始化。事实上,对于-Wextra,Clang 抱怨 的返回类型foo,说 \xe2\x80\x9c'const' 类型限定符对返回类型没有影响\xe2\x80\x9d。
GCC 和 Clang 被视为foo()不合格是否错误const?或者 C 标准中是否有规定忽略返回类型的限定符?
(为了避免有人抱怨这没有用,考虑一下可以有baz(foo().a),其中数组被传递给一个函数,该函数在它是一个临时对象时大量使用它。但是,如果baz修改了数组的任何部分,行为不会由 C 标准定义。程序员可能希望通过将foo\xe2\x80\x99s 返回类型const声明为 来预防此错误,因此如果他们不小心将临时对象中的地址传递给const函数的参数声明中没有对应的函数。)
\xc2\xa76.7.6.3p5 “函数声明符(包括原型)> 语义”:
\n\n\n如果在声明“ T D1 ”中,D1具有以下形式
\n\n\nD ( 参数类型列表 )
\n或者
\n\n\nD ( 标识符列表选项 )
\n并且声明“ TD ”中为ident指定的类型为“导出声明器类型列表 T ”,则为ident指定的类型为“返回T的非限定版本的导出声明器类型列表函数”
\n
因此 的类型foo与声明的类型相同S foo(void)。
另请参见 \xc2\xa76.7.3p5“类型限定符 > 语义”:
\n\n\n与限定类型关联的属性仅对左值表达式有意义。
\n
因此,即使第一个引用的段落没有更改类型,非左值表达式的 const 限定也foo()没有意义