在整数和整数数组之间进行类型运算是否合法?
特定代码:
#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;
}
答案 0 :(得分:5)
是的,C标准明确规定了通过联合在所有数据类型之间进行类型修剪。对于数组,没有特殊的规定禁止这样做。
答案 1 :(得分:3)
是的,在ISO C99和更高版本中,联合类型联动是合法的。 Unions and type-punning 以及Is type-punning through a union unspecified in C99, and has it become specified in C11?(在C89中,是定义的实现,而不是未定义的)。
作为GNU扩展,它在gnu89和GNU C ++中定义良好。 https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2Dpunning
在MSVC ++中这也是合法的,例如,它使用联合定义__m128i
来访问矢量元素。 (并且还允许指针广播以进行类型修剪,这与其他实施严格别名的编译器不同。)
请注意,在ISO C ++中 是不合法的,该命令读取的成员不是最后写入的成员(未定义的行为)。我认为这是一个通用扩展,所有x86编译器都支持(并且您使用的是Intel内部函数),但并非所有地方的所有编译器都应该被假定为支持。
对于严格可移植的C ++,您始终可以使用memcpy
来在两种不同类型的对象表示之间进行复制。
对于您而言,除非您停用优化功能,否则任何体面的优化编译器 都应与(uint64_t)n
和n>>64
进行相同的编译。
答案 2 :(得分:0)
C11草案N1570的6.5节C节中的类型访问规则没有规定access the element orange
或struct
类型的对象可以用除{ {1}}类型,包含此类union
的另一种类型的左值或字符类型的左值。
可以看到使用一种类型的指针或左值来派生另一种类型的指针或左值并随后对其进行访问的质量编译器应该能够识别出在访问以下内容的情况下对后者的访问派生是可见的,是对前者的访问。我认为该标准的作者认为它很明显可以不用说,特别是因为即使类似union
之类的东西也可以调用UB。赋值的左操作数是类型union
的左值,没有规定允许使用类型someUnion.intMember = 3;
的左值来访问并集类型的对象。编译器认为通过派生指针或左值进行的访问是对父级的访问的情况范围是实现质量问题;该标准未提供有关“良好”实施的预期结果的指导。
对于clang和gcc所允许的内容,他们似乎认识到对int
的访问是对联合的访问,但是他们 也不承认int
,即使标准将两个构造定义为等效。由于该标准不会要求实现就可以识别两者(甚至不包括明显的someUnion.someArray[i]
),因此,这种差异也不会使clang和gcc不一致。尽管如此,应该指出的是,在基于联合识别左值时,它们是令人惊讶的盲目。