如何在C中返回匿名结构?

时间:2015-01-09 10:11:17

标签: c struct return-value c99 return-type

尝试一些代码我意识到以下代码编译:

struct { int x, y; } foo(void) {
}

似乎我们正在定义名为 foo 的函数,该函数返回匿名 struct

现在,我的问题是:它是否只发生在使用我的编译器编译或者这是合法的C(99)?如果是这样,return语句的正确语法是什么,以及如何正确地将返回值赋给变量?

7 个答案:

答案 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

我认为这是完全合法的。