返回类型可以被const修饰吗?

5

请思考:

typedef struct { int a[1]; } S;
const S foo(void) { return (S) {{3}}; }
void bar(void) { int *p = foo().a; }

根据Per C 2018 6.2.4 8,表达式foo()引用一个有临时存活期的对象:

结构体或联合体类型的非左值表达式(其中,结构体或联合体包含一个具有数组类型的成员(包括所有包含的结构体和联合体的成员)),指向具有自动存储期和临时生存期的对象。它的生存期始于评估该表达式时,并且其初始值是表达式的值。当包含完整表达式的评估结束时,其生存期结束。任何试图修改具有临时生存期的对象将导致未定义行为。对于有效类型的目的,具有临时生存期的对象的行为好像是使用其值的类型声明的。这样的对象不需要唯一的地址。

注意,代码没有尝试修改对象,但它确实使用foo().a的第一个元素的地址初始化了int *,我们希望这个地址应该是const限定的。这违反了6.5.16.1 1中所需求的指向目标类型必须具有指向源类型的所有限定符的约束条件。(该段落适用于赋值,但已由6.7.9 11纳入其中,该标准涵盖了初始化。)

但是,无论是GCC还是Clang都没有对这种初始化进行投诉。实际上,使用-Wextra时,Clang会抱怨foo的返回类型,称“返回类型上的'const'限定符没有效果”。

GCC和Clang将foo()视为未被const限定,是错误的吗?还是C标准中有关于返回类型上的限定符被忽略的规定?

(如果有人抱怨没有用处,请考虑baz(foo().a),其中数组被传递给一个在其为临时对象时大量使用它的函数。然而,如果baz修改了数组的任何部分,则该行为将不被C标准定义。程序员可能希望通过声明foo的返回类型为const来防止此错误,因此期望在不带相应const的函数参数声明中意外传递临时对象的地址时收到编译器错误消息。)


1
在函数声明中,对于“ident”指定的类型为“derived-declarator-type-list function returning **the unqualified version of T**”。因此,foo的类型为S(void)。请参考https://www.iso-9899.info/n3047.html#6.7.6.3p4和c17_updated_proposed_fdis.pdf。 - Language Lawyer
@LanguageLawyer:谢谢,那应该就是答案了。这在官方的C 2018版本中是第5段。 - Eric Postpischil
这是否是由于不同原因(例如生命周期)而导致的未定义行为?在 int *p = foo().a; baz(); printf("%d\n",*p); 中,调用 baz 是否破坏了从 foo 返回的值?通常的做法是:S copy = foo(); 然后,弹出栈以回收临时区域。但是,仅仅取 S.a 的地址就足以保留隐式的栈上副本吗? - Craig Estey
@user3386109:这是一个关于语言规则的问题,而被问到的问题在帖子的顶部。底部不是“实际考虑的问题”,仅供激励使用。 - Eric Postpischil
@EricPostpischil 很有趣。感谢您的时间。 - user3386109
显示剩余2条评论
2个回答

5

§6.7.6.3p5 "函数声明(包括原型)> 语义":

如果在声明"T D1"中,D1的形式为

D (parameter-type-list)

D (identifier-listopt)

并且声明 "T D" 中指定的 ident 的类型是 "derived-declarator-type-list T", 则指定给 ident 的类型是 "derived-declarator-type-list function returning the unqualified version of T"

所以foo的类型与其被声明为S foo(void)时相同。

另请参见§6.7.3p5 "类型限定符 > 语义":

与限定类型相关联的属性仅对 lvalue 表达式有意义。

因此,即使第一段引用的内容未更改类型,在非lvalue表达式foo()上的const修饰也没有意义。


0

未定义的行为?

6.7.3 类型限定符
...
9 如果一个数组类型的规范包含任何类型限定符,则元素类型具有相应的限定符,而不是数组类型。 如果函数类型的规范包括任何类型限定符,则其行为是未定义的。136)

或者我完全误解了重点?


4
该段落涉及到类似于typedef int FunctionType(void); const FunctionType x;的内容,这个定义名义上是一个被const修饰的函数类型,这与返回类型是const-qualified的函数是不同的。 - Eric Postpischil
@EricPostpischil:明白了,这不是我经常探索的语言领域。 - John Bode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接