использование объединений в параметрах функции

Я недавно обнаружил, что что-то компилирует (не уверенный, что это законно хотя). Моя потребность в такой вещи прибывает из этого: Мой выходной машинный код проекта для выбранной дуги. (который может или не может быть той же дугой. как тот, запускающий программу). Так, я хотел бы поддерживать архитектуру на 64 бита прямо сейчас (также поддерживая существующие 32 и 16 битов archs.) Мое текущее решение состоит в том, чтобы 'основой' new_state просто был uint64_t, и вручную бросающий к 16 и 32 битам по мере необходимости. Хотя, я обнаружил, что можно скомпилировать объединения в параметре функции. Так функция, поскольку это компилирует:

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
             union{
    uint16_t b16;
    uint32_t b32;
    uint64_t b64;
}base ,int self_running);

Вообще "законна" такого рода вещь хотя или поддерживаемый какими-либо другими компиляторами? И также, я не могу выяснить, как вызвать эту функцию, также не создавая объединение и затем передав это объединение new_state.

5
задан timrau 14 August 2012 в 23:49
поделиться

5 ответов

Подводя итог: Да, это допустимо в C, хотя является незаконным в C ++. Последний содержит это примечание, объясняющее разницу

Изменение: В C ++ типы не могут быть определены в типах возвращаемых данных или в типах параметров. В C эти определения типов разрешены

Пример:

void f( struct S { int a; } arg ) {} // valid C, invalid C++
enum E { A, B, C } f() {} // valid C, invalid C++
  • Обоснование: При сравнении типов в разных единицах компиляции C ++ полагается на эквивалентность имен, когда C полагается на структурную эквивалентность. Что касается типов параметров: поскольку тип, определенный в списке параметров, будет находиться в области действия функции, единственные допустимые вызовы в C ++ будут происходить из самой функции.
  • Влияние на исходную функцию: Удаление семантически четко определенной функции.
  • Сложность конвертации: Семантическое преобразование. Определения типов необходимо переместить в область видимости файла или в файлы заголовков.
  • Насколько широко используется: Редко. Этот стиль определений типов рассматривается как плохой стиль кодирования.

Структурная эквивалентность в C достигается за счет концепции «совместимости типов». Это позволяет C рассматривать многие типы, как если бы они были идентичными, даже если они теоретически различны - потому что они объявлены в двух разных единицах трансляции. В C ++ такой концепции не существует, поскольку типы связаны и сопоставляются с одним и тем же объектом (т. e, чтобы позволить функциям-членам связываться друг с другом).

Обратите внимание, что процитированное выше объяснение основано на C89, который не учитывал имя тега структуры при определении совместимости типов. В черновике C89 соответствующий текст читается следующим образом:

Кроме того, два типа структуры, объединения или перечисления, объявленные в отдельных единицах перевода, совместимы, если они имеют одинаковое количество членов, одинаковые имена членов и совместимые типы членов; для двух структур члены должны быть в одинаковом порядке;

В C99 проверка типов более строгая: если одна структура имеет имя тега, объявление другой структуры должно иметь такое же имя тега. Итак, в случае вашего безымянного типа объединения, чтобы объявить функцию в другом TU, имеющем совместимый тип, вам снова понадобится безымянное объединение, если вы хотите иметь действительный код C99 (без неопределенного поведения) - вы не можете «обмануть» и использовать именованное объединение в одном TU и безымянное объединение в другом TU. Мне кажется, что этот "трюк" применим и для C89. C99 TC3 6.2.7 / 1 :

Более того, два типа структуры, объединения или перечисления, объявленные в отдельных единицах перевода, совместимы, если их теги и члены удовлетворяют следующим требованиям: Если один объявлен с тег, другой должен быть объявлен с таким же тегом. Если оба являются полными типами, применяются следующие дополнительные требования: между их членами должно быть взаимно однозначное соответствие, так что каждая пара соответствующих членов объявляется с совместимыми типами, и так, что если один член соответствующей пары объявляется с именем, другой член объявляется с тем же именем. Для двух структур члены-корреспонденты должны быть объявлены в том же порядке.


То, как вы хотите это сделать, не работает. Вызов функции преобразует аргументы в тип параметров, как если бы при обычном назначении.

Итак, чтобы это работало, у вас должен быть аргумент, совместимый с типом параметра. Для двух объединений, объявленных в одной и той же единице трансляции, это означает, что их типы должны быть одинаковыми - это единственный способ найти совместимый тип в одной и той же единице трансляции. Но это не может сработать, потому что объявление безымянного объединения создает уникальный новый тип - нет возможности «вернуться» к нему с помощью другого объявления.

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

union base_type {
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
};

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
                  union base_type base,int self_running);

union base_type base_b16(uint16_t t) 
{ union base_type b; b.b16 = t; return b; }
union base_type base_b32(uint32_t t) 
{ union base_type b; b.b32 = t; return b; }
union base_type base_b64(uint64_t t) 
{ union base_type b; b.b64 = t; return b; }

Теперь это может выглядеть следующим образом

pcg_new_state(...., base_b32(4211), ....);
7
ответ дан 14 December 2019 в 04:44
поделиться

Как насчет этого:

typedef struct _base
{
    union
    {
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
    };
}Base;

int func(Base b)
{
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{

    Base b;
    b.b16 = 0xFFFF;
    func(b);

    return 0;

}
0
ответ дан 14 December 2019 в 04:44
поделиться

Вы можете заставить это скомпилировать в GCC, указав объединение с именем в функции, но вы не не могу использовать его из-за объема определения, как предупреждает GCC:

test_union.c:14: warning: ‘union X’ declared inside parameter list
test_union.c:14: warning: its scope is only this definition or declaration, which is probably not what you want

Код:

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
                         union X{
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
}base ,int self_running);
0
ответ дан 14 December 2019 в 04:44
поделиться

Размер союза в любом случае будет размером самого большого члена - так что вы ничего не получите. С таким же успехом вы можете просто сделать параметр uint64_t, поскольку преобразования из больших в меньшие беззнаковые типы четко определены и обычно дешевы в реализации. (Например, присвоение uint64_t uint16_t может быть выполнено просто путем взятия младших 16 битов более широкого типа.)

EDIT : Например

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, uint64_t param_base ,int self_running)
{
    /* Implementation for 16 bit arch */
    uint16_t base = param_base;

    /* ... more code ... */
}
0
ответ дан 14 December 2019 в 04:44
поделиться

Я быстро просмотрел стандарт (Edit: C ++) и не увидел ничего запрещающего ни в 8.3.5 - Функции [dcl.fct], ни в 9.5 - Союзы [class. союз]. Как полуобразованное предположение, я бы сказал, что законно принимать профсоюз, но не объявлять один подобный встроенный. GCC дает:

ошибка: типы не могут быть определены в типах параметров.

Так что вам придется определить тип заранее.

Даже если это законно, однако, это не значит, что это хорошо идея. Просто взглянув на него, я бы предположил, что, возможно, перегрузка может предоставить лучшее решение. Но, конечно, вы знаете свой код лучше всех ... просто подумав, что вам может понадобиться поискать более простое и идиоматическое решение.

1
ответ дан 14 December 2019 в 04:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: