你遇到了语言的一个微妙角落。
在大多数情况下,数组类型的表达式会隐式转换为指向数组对象第一个元素的指针。例外情况(此处均不适用)是:
- 当数组表达式是一元运算符的操作数时
&
(产生整个数组的地址);
- 当它是一元
sizeof
或(从 C11 开始)_Alignof
运算符的操作数时(sizeof arr
产生数组的大小,而不是指针的大小);和
- 当它是用于初始化数组对象的初始化程序中的字符串文字时(
char str[6] = "hello";
不转换"hello"
为char*
.)
(N1570草案错误地添加_Alignof
到例外列表中。事实上,由于不清楚的原因,_Alignof
只能应用于类型名称,而不是表达式。)
请注意,有一个隐含的假设:数组表达式首先引用了一个数组对象。在大多数情况下,它确实如此(最简单的情况是数组表达式是声明的数组对象的名称)——但在这种情况下, 没有数组对象。
如果函数返回结构,则结构结果由 value返回。在这种情况下,结构包含一个数组,至少在逻辑上给我们一个没有对应数组对象的数组值。所以数组表达式衰减为指向...的第一个元素的指针,呃,嗯,...一个不存在的数组对象。bar().c
2011 ISO C 标准通过引入“临时生命周期”解决了这个问题,它仅适用于“具有结构或联合类型的非左值表达式,其中结构或联合包含具有数组类型的成员”(N1570 6.2.4p8)。这样的对象不能被修改,并且它的生命周期在包含完整表达式或完整声明符的末尾结束。
因此,从 C2011 开始,您的程序的行为已明确定义。该printf
调用获取指向数组的第一个元素的指针,该数组是具有临时生命周期的结构对象的一部分;该对象将继续存在,直到printf
调用完成。
但是从 C99 开始,行为是未定义的——不一定是因为您引用的子句(据我所知,没有中间序列点),而是因为 C99 没有定义数组对象对于工作printf
。
如果您的目标是让该程序运行,而不是了解它可能失败的原因,您可以将函数调用的结果存储在显式对象中:
const struct s result = bar();
printf("%s", result.c);
现在您有了一个具有自动而非临时printf
存储持续时间的 struct 对象,因此它存在于调用执行期间和之后。