尝试一些代码我意识到以下代码编译:
struct { int x, y; } foo(void) {
}
似乎我们正在定义名为 foo
的函数,该函数返回匿名 struct
。
现在,我的问题是:它是否只发生在使用我的编译器编译或者这是合法的C(99)?如果是这样,return语句的正确语法是什么,以及如何正确地将返回值赋给变量?
答案 0 :(得分:24)
您返回的结构不是匿名结构。 C标准将匿名结构定义为另一个不使用标记的结构的成员。你返回的是一个没有标签的结构,但由于它不是一个成员,所以它不是匿名的。 Gcc使用名称<匿名> 表示没有标记的结构。
让我们假设您尝试在函数中声明一个相同的结构。
struct { int x, y; } foo( void )
{
return ( struct { int x, y; } ){ 0 } ;
}
gcc抱怨它:返回类型' struct<时不兼容的类型匿名>'但' struct<匿名>'预计
显然这些类型不兼容。看看标准,我们看到:
6.2.7兼容类型和复合类型
1:如果类型相同,则两种类型具有兼容类型。用于确定两种类型是否兼容的附加规则在6.7.2中描述了类型说明符,在6.7.3中描述了类型限定符,在6.7.6中描述了声明符。 此外,如果它们的标记和成员满足以下要求,则在单独的转换单元中声明的两个结构,联合或枚举类型是兼容的:如果使用标记声明一个,则使用相同的方式声明另一个标签。 如果两者都在各自的翻译单元中的任何地方完成,则以下附加要求适用:其成员之间应存在一对一的对应关系,以便声明每对相应的成员具有兼容类型;如果使用对齐说明符声明该对中的一个成员,则使用等效的对齐说明符声明另一个成员;如果该对的一个成员使用名称声明,则另一个成员使用相同的名称声明。对于两个结构,相应的成员应按相同的顺序声明。对于两个结构或联合,相应的位域应具有相同的宽度。对于两个枚举,相应的成员应具有相同的值。
第二个粗体部分解释说,如果两个结构都没有标记,例如在这个例子中,它们必须遵循该部分之后列出的其他要求,他们这样做。但是如果你注意到它们必须在单独的翻译单元中的第一个粗体部分,那么示例中的结构就不是。所以它们不兼容,代码无效。
无法使代码正确,因为如果声明一个结构并在此函数中使用它,则必须使用一个标记,这违反了两个结构都必须具有相同标记的规则:
struct t { int x, y; } ;
struct { int x, y; } foo( void )
{
struct t var = { 0 } ;
return var ;
}
再次gcc抱怨:返回类型' struct t'时不兼容的类型但' struct<匿名>'预计
答案 1 :(得分:13)
这适用于我的GCC版本,但似乎完全是黑客攻击。也许在自动生成的代码中很有用,你不想处理生成独特结构标记的额外复杂性,但是我甚至可以提出合理化。
struct { int x,y; }
foo(void) {
typeof(foo()) ret;
ret.x = 1;
ret.y = 10;
return ret;
}
main()
{
typeof(foo()) A;
A = foo();
printf("%d %d\n", A.x, A.y);
}
此外,它依赖于编译器中存在的typeof() - GCC和LLVM似乎支持它,但我确信很多编译器都不支持它。
答案 2 :(得分:9)
您可能无法显式地 return
来自您的函数的某些聚合值(除非您使用typeof
扩展名来获取结果的类型)。
故事的寓意是即使你可以声明一个函数返回一个匿名 struct
你应该几乎 永远不会那样做强>
而是命名struct
和代码:
struct twoints_st { int x; int y; };
struct twoints_st foo (void) {
return ((struct twoints_st) {2, 3});
};
请注意,它在语法上是正常的,但在执行时通常是未定义的行为,以使函数没有return
(例如,您可以在其中调用exit
)。但是你为什么要编码(可能是合法的):
struct { int xx; int yy; } bizarrefoo(void) { exit(EXIT_FAILURE); }
答案 3 :(得分:1)
这适用于最新版本的GCC。它对于使用宏创建动态数组特别有用。例如:
#define ARRAY_DECL(name, type) struct { int count; type *array; } name
然后你可以用realloc等来创建数组。这很有用,因为你可以创建一个ANY类型的动态数组,并且有一种方法可以创建所有这些数组。否则你最终会使用很多void *
,然后编写函数来实际使用强制转换等方法取回值。你可以用宏来快速完成所有这些操作;那就是他们的美丽。
答案 4 :(得分:1)
这是一种在C ++ 14中返回匿名结构的方法,没有任何我刚刚发现的黑客攻击
(我认为C ++ 11应该足够了)
在我的情况下,函数intersect()
返回std::pair<bool, Point>
,这不是非常具有描述性的,所以我决定为结果制作自定义类型。
我本可以单独struct
,但这不值得,因为我只需要这个特例;这就是我使用匿名结构的原因。
auto intersect(...params...) {
struct
{
Point point;
bool intersects = false;
} result;
// do stuff...
return result;
}
现在,而不是丑陋的
if (intersection_result.first) {
Point p = intersection_result.second
我可以使用更好看的东西:
if (intersection_result.intersects) {
Point p = intersection_result.point;
答案 5 :(得分:1)
您可以在return参数的规范中定义一个结构。此外,为简洁起见,您可以使用C99中引入的复合文字:
#include<stdio.h>
struct foo { int x, y; }
foo( void )
{
return (struct foo){ 1, 2 } ;
}
int main()
{
struct foo res = foo();
printf("%d %d\n", res.x, res.y);
}
打印:
1 2
对于C99,该代码以学究模式编译,没有任何警告。请注意,标签名称与功能相同。之所以可行,是因为结构和全局对象的名称空间是分开的。这样,您可以将名称意外冲突的可能性降到最低。如果您认为更合适,可以使用foo_result
之类的东西。
答案 6 :(得分:0)
或者你可以创建无限递归:
LoginNetworkOperation
我认为这是完全合法的。