我如何 return C 中的匿名结构?

How can I return an anonymous struct in C?

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

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

好像我们正在定义一个名为 foo 的函数,其中 return 是一个匿名函数 struct.

它只是碰巧用我的编译器编译还是这个合法的 C(99)?

如果是这样,return 语句的正确语法是什么?如何正确地将 returned 值分配给变量?

您可能无法显式 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); }

您返回的结构不是匿名结构。 C 标准将匿名结构定义为另一个不使用标记的结构的成员。您返回的是一个没有标签的结构,但由于它不是成员,所以它不是匿名的。 GCC 使用名称 < anonymous > 来指示没有标签的结构。

假设您尝试在函数中声明一个相同的结构。

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

GCC 抱怨它:返回类型 'struct < anonymous>' 时类型不兼容,但 'struct <anonymous>' 是预期的

显然类型不兼容。在标准中我们看到:

6.2.7 Compatible type and composite type

1: Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.6 for declarators. Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are completed anywhere within their respective translation units, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types; if one member of the pair is declared with an alignment specifier, the other is declared with an equivalent alignment specifier; and if one member of the pair is declared with a name, the other is declared with the same name. For two structures, corresponding members shall be declared in the same order. For two structures or unions, corresponding bit-fields shall have the same widths. For two enumerations, corresponding members shall have the same values.

第二个粗体部分解释了如果两个结构都没有标记,例如在本例中,它们必须遵循该部分后面列出的附加要求,它们确实这样做了。但是如果你注意到第一个粗体部分,它们必须在单独的翻译单元中,而示例中的结构不是。所以它们不兼容,代码无效。

不可能使代码正确,因为如果您声明一个结构并在此函数中使用它。您必须使用标签,这违反了两个结构必须具有相同标签的规则:

struct t { int x, y; } ;

struct { int x, y; } foo( void )
{
    struct t var = { 0 } ;

    return var ;
}

GCC 再次抱怨:返回类型 'struct t' 时类型不兼容,但预期 'struct <anonymous>'

这在我的 GCC 版本中有效,但看起来完全是 hack。它可能在自动生成的代码中很有用,在这种情况下您不想处理生成唯一结构标记的额外复杂性,但我什至想出这种合理化。

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 似乎支持它,但我相信很多编译器不支持它。

这适用于最新版本的 GCC。它对于使用宏创建动态数组特别有用。例如:

#define ARRAY_DECL(name, type) struct { int count; type *array; } name

然后你可以用 realloc 等创建数组。这很有用,因为你可以创建一个 any 类型的动态数组,并且有一种方法可以创建所有他们。否则,您最终会使用大量 void *,然后编写函数以通过强制转换等方式实际取回值。您可以使用宏来简化所有这些操作;这就是他们的美。

或者您可以创建无限递归:

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

我认为这是完全合法的。

这是一种在 C++14 中 return 匿名结构的方法,没有我刚刚发现的任何 hack。
(我想 C++ 11 应该足够了)
在我的例子中,函数 intersect() returns 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;

您可以在 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之类的东西。