Подтвердить что ты не робот

Как вернуть анонимную структуру в C?

Попробовав какой-то код, я понял, что следующий код компилируется:

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

Кажется, мы определяем функцию с именем foo, которая возвращает анонимный struct.

Теперь, мой вопрос: происходит ли это только для компиляции с моим компилятором или это законный C (99)? Если да, то какой правильный синтаксис для оператора return и как я могу правильно присвоить возвращаемое значение переменной?

4b9b3361

Ответ 1

Возвращаемая структура не является анонимной структурой. C определяет анонимную структуру как члена другой структуры, которая не использует тег. То, что вы возвращаете, это структура без тега, но поскольку она не является членом, она не является анонимной. Gcc использует имя <anonymous> , чтобы указать структуру без тега.

Предположим, вы пытаетесь объявить идентичную структуру в функции.

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

gcc жалуется на это: несовместимые типы при возврате типа 'struct <anonymous> 'but' 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 < анонимный > '

Ответ 2

Это работает в моей версии 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);
}

Кроме того, он зависит от типаof(), присутствующего в компиляторе - GCC и LLVM, похоже, поддерживают его, но я уверен, что многие компиляторы этого не делают.

Ответ 3

Вероятно, вы не можете явно указать return некоторое суммарное значение из вашей функции (если вы не используете расширение typeof для получения типа результата).

Мораль истории состоит в том, что даже если вы можете объявить функцию, возвращающую анонимный struct, вы должны практически никогда не делать этого.

Вместо этого назовите struct и код:

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

Обратите внимание, что оно синтаксически нормально, но обычно undefined поведение при выполнении, чтобы иметь функцию без return (например, вы могли бы вызвать exit внутри нее). Но почему вы хотите закодировать (возможно, законную):

struct { int xx; int yy; } bizarrefoo(void) { exit(EXIT_FAILURE); }

Ответ 4

Это работает с новейшей версией GCC. Это особенно полезно для создания динамических массивов с макросами. Например:

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

Затем вы можете сделать массив с realloc и т.д. Это полезно, потому что тогда вы можете создать динамический массив с ЛЮБОЙ тип, и есть один способ сделать все из них. В противном случае вы в конечном итоге используете много void *, а затем записываете функции, чтобы фактически вернуть значения с помощью прикладов и т.д. Вы можете сократить все это с помощью макросов; это их красота.

Ответ 5

Или вы можете создать бесконечную рекурсию:

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

Что я считаю вполне законным.

Ответ 6

Здесь можно вернуть анонимные структуры на С++ 14 без каких-либо хаков, которые я только что обнаружил. (С++ 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;