以下是gcc 4.4.4下的简单代码段错误
#include<stdio.h> typedef struct Foo Foo; struct Foo { char f[25]; }; Foo foo(){ Foo f = {"Hello, World!"}; return f; } int main(){ printf("%s\n", foo().f); }
将最后一行更改为
Foo f = foo(); printf("%s\n", f.f);
工作良好。使用编译时,这两个版本均可使用-std=c99。我是在简单地调用未定义的行为,还是在标准中进行了某些更改,从而使代码可以在C99下工作?为什么在C89下崩溃?
-std=c99
我相信C89 / C90和C99中的行为均未定义。
foo().f是数组类型的表达式,特别是char[25]。 C99 6.3.2.1p3说:
foo().f
char[25]
除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为“ array of type ”的表达式转换为类型为“ pointer to type ” 的表达式,指向数组对象的初始元素,并且不是左值。如果数组对象具有寄存器存储类,则该行为是不确定的。
在这种特殊情况下(作为函数返回的结构元素的数组)的问题是没有“数组对象”。函数结果按值返回,因此调用的结果foo()是type 的 值struct Foo,并且foo().f是type 的值(不是左值)char[25]。
foo()
struct Foo
据我所知,这是C语言(最多C99)中唯一可以使用数组类型的非左值表达式的情况。我想说的是,尝试访问它的行为并没有被遗漏所定义,这可能是因为该标准的作者(可以理解的恕我直言)没有想到这种情况。在不同的优化设置下,您可能会看到不同的行为。
新的2011 C标准通过发明新的存储类来修补这种情况。 N1570(链接到最新的C11草案)在6.2.4p8中说:
具有结构或联合类型的非左值表达式,其中结构或联合包含具有数组类型的成员(递归包括所有包含的结构和联合的成员)是指具有自动存储期限和 临时生存期 的对象。它的生命周期从对表达式进行求值开始,并且其初始值为表达式的值。当包含完整表达式或完整声明符的求值结束时,其生存期结束。任何试图使用临时生存期修改对象的尝试都会导致未定义的行为。
因此,程序的行为在C11中得到了很好的定义。但是,直到能够获得符合C11的编译器,最好的选择可能是将函数的结果存储在本地对象中(假设您的目标是工作代码而不是破坏编译器):
[...] int main(void ) { struct Foo temp = foo(); printf("%s\n", temp.f); }