Введите punning между целым числом и массивом, используя `union` - PullRequest
3 голосов
/ 05 марта 2019

Разрешено ли делать пробивание между целым числом и массивом целых чисел?

Специальный код:

#include <nmmintrin.h>
#include <stdint.h>

union   Uint128 {
    __uint128_t uu128;
    uint64_t    uu64[2];
};

static inline   uint_fast8_t    popcnt_u128 (__uint128_t n)
{
    const union Uint128 n_u     = {.uu128 = n};
    const uint_fast8_t  cnt_a   = _mm_popcnt_u64(n_u.uu64[0]);
    const uint_fast8_t  cnt_b   = _mm_popcnt_u64(n_u.uu64[1]);
    const uint_fast8_t  cnt     = cnt_a + cnt_b;

    return  cnt;
}

Ответы [ 3 ]

5 голосов
/ 05 марта 2019

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

3 голосов
/ 06 марта 2019

Да, профсоюзное наказание является законным в ISO C99 и более поздних версиях. Объединения и тип-штампование , а также Является ли тип-штампование через объединение, не указанное в C99, и стало ли оно заданным в C11? (В C89 это было определение реализации , не undefined ).

Как расширение GNU, оно четко определено в gnu89 и GNU C ++.https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2Dpunning

Это также допустимо в MSVC ++, который, например, использует объединения для определения __m128i для доступа к векторным элементам.(А также разрешает приведение указателей для вычеркивания типов, в отличие от других компиляторов, которые обеспечивают строгое псевдонимы.)


Остерегайтесь того, что не разрешено в ISO C ++ дляпрочитать член объединения, отличный от того, который был написан последним (неопределенное поведение).Это общее расширение, которое, я думаю, поддерживают все компиляторы x86 (и вы используете встроенные функции Intel), но не все компиляторы повсюду следует предполагать.

Вы всегда можете использовать memcpy для строго-портативный C ++ для копирования между объектными представлениями двух разных типов.


В вашем случае любой приличный оптимизирующий компилятор должен скомпилировать это так же, как (uint64_t)nи n>>64, если вы не отключите оптимизацию.

0 голосов
/ 06 марта 2019

Правила доступа типа в параграфе C раздела 6.5 проекта N1170 C11 не предусматривают, чтобы объект типа struct или union имел доступ к своему хранилищу с помощью чего-либо, кроме lvalue этого типа union,lvalue другого типа, который содержит такой union, или lvalue символьного типа.

Качественный компилятор, который может видеть, что указатель или lvalue одного типа используется для получения указателя или lvalueдругой, к которому затем осуществляется доступ, должен быть в состоянии распознать, что доступ к последнему, сделанный в контексте, где вывод является видимым, является доступом к первому.Я думаю, что авторы Стандарта думали, что достаточно очевидно, что это само собой разумеется, тем более что даже что-то вроде someUnion.intMember = 3; будет вызывать UB в противном случае.Левый операнд присваивания является lvalue типа int, и нет положения, позволяющего использовать lvalue типа int для доступа к объекту типа объединения.Диапазон ситуаций, когда компилятор распознает, что доступ через производный указатель или lvalue является доступом к родительскому объекту, является проблемой качества реализации;Стандарт не дает указаний относительно того, что следует ожидать от «хорошей» реализации.

Что касается того, что позволяют clang и gcc, они, похоже, признают, что доступ к someUnion.someArray[i] является доступом к объединению,но они не распознают *(someUnion.someArray+i) аналогично, хотя Стандарт определяет две конструкции как эквивалентные.Поскольку Стандарт не требует , чтобы реализации распознавали (или даже очевидное someUnion.intMember), несоответствие не делает Clang и GCC несоответствующими.Тем не менее, следует отметить, что они удивительно слепы, когда дело доходит до признания lvalues ​​на основе союзов.

...