我想创建一个如下所示的联合
union {
long i;
float f;
void* ptr;
};
其中成员i和f始终为ptr的大小(浮点型和long long型为32位/ double块long&long型为64位)。
以最少的宏使用量实现此目标的最佳方法是什么?
答案 0 :(得分:2)
请注意,在ISO C ++中,联合类型合并(写一个成员,然后读另一个成员)是未定义的行为。它 在ISO C99和GNU C ++中都有很好的定义。 (和其他一些C ++编译器,我想包括MSVC。)也要注意将非平凡可复制的类型(带有构造函数/析构函数)作为联合成员。
当然,除了类型绑定(例如,手卷多态性)之外,联合还有其他用途,
uintptr_t
出于这个原因而存在。(如果出于某种原因,您需要使用签名类型,请选择intptr_t
或ptrdiff_t
。
但是对于float
和double
,您需要预处理器。与UINTPTR_MAX
sizeof(void*)
为您提供了一种使用预处理器检查指针宽度的方法
请注意,uintptr_t
通常与指针的宽度相同,但是类型名称定义为可以存储指针值的名称。 floatptr_t
在32位平台上不是这种情况。 (有趣的事实:它将在x86-64上用于“规范”的48位地址 1 )。如果这使您感到困扰,或者您担心会扭曲您对uintptr_t
的看法,请选择其他名称; floatptr_t
简短的长相看起来是正确的,即使它是“错误的”。
#include <stdint.h>
// assumption: pointers are 32 or 64 bit, and float/double are IEEE binary32/binary64
#if UINTPTR_MAX > (1ULL<<32)
typedef double floatptr_t;
#else
typedef float floatptr_t;
#endif
static_assert(sizeof(floatptr_t) == sizeof(void*), "pointer width doesn't match float or double, or our UINTPTR_MAX logic is wrong");
union ptrwidth {
uintptr_t u;
intptr_t i;
floatptr_t f;
void *ptr;
};
为了测试这一点,我使用x86 32位gcc -m32
和gcc
(x86-64),MSVC 32和64位以及ARM 32位编译了on the Godbolt compiler explorer
int size = sizeof(ptrwidth);
int size_i = sizeof(ptrwidth::i);
int size_f = sizeof(ptrwidth::f);
int size_ptr = sizeof(ptrwidth::ptr);
# gcc -m32 output
size_ptr: .long 4
size_f: .long 4
size_i: .long 4
size: .long 4
# gcc -m64 output
size_ptr: .long 8
size_f: .long 8
size_i: .long 8
size: .long 8
因此确认工会本身和每个成员的预期大小。
MSVC也可以工作,编译为int size_f DD 08H
或04H
,依此类推。
脚注1 :在x8-64上,规范的虚拟地址以48位符号扩展为64,因此您 实际上可以通过{{ 1}}-> intptr_t
转换并返回,没有舍入错误。但对于没有至少2字节对齐的上半地址,则不是double
-> uintptr_t
。 (如果没有AVX512F,double
<-> uint64_t
转换速度很慢。)
在当前硬件上,非规范虚拟地址出现故障。