为64位和32位版本创建指针大小的并集

时间:2018-10-14 15:08:05

标签: c++ pointers x86-64 32bit-64bit unions

我想创建一个如下所示的联合

union {
    long i;
    float f;
    void* ptr;
};

其中成员i和f始终为ptr的大小(浮点型和long long型为32位/ double块long&long型为64位)。

以最少的宏使用量实现此目标的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

请注意,在ISO C ++中,联合类型合并(写一个成员,然后读另一个成员)是未定义的行为。它 在ISO C99和GNU C ++中都有很好的定义。 (和其他一些C ++编译器,我想包括MSVC。)也要注意将非平凡可复制的类型(带有构造函数/析构函数)作为联合成员。

当然,除了类型绑定(例如,手卷多态性)之外,联合还有其他用途,


uintptr_t出于这个原因而存在。(如果出于某种原因,您需要使用签名类型,请选择intptr_tptrdiff_t

但是对于floatdouble,您需要预处理器。与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 -m32gcc(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 08H04H,依此类推。


脚注1 :在x8-64上,规范的虚拟地址以48位符号扩展为64,因此您 实际上可以通过{{ 1}}-> intptr_t转换并返回,没有舍入错误。但对于没有至少2字节对齐的上半地址,则不是double-> uintptr_t。 (如果没有AVX512F,double <-> uint64_t转换速度很慢。)

在当前硬件上,非规范虚拟地址出现故障。