C++ 为64位和32位生成创建指针大小的联合

C++ 为64位和32位生成创建指针大小的联合,c++,pointers,x86-64,32bit-64bit,unions,C++,Pointers,X86 64,32bit 64bit,Unions,我想创建一个如下所示的联合 union { long i; float f; void* ptr; }; 其中成员i和f始终是ptr的大小(float&long表示32位/double&long表示64位) 什么是用最小的宏使用来实现这一点? 注意,在UNISC++中,联合类型双关(写入一个成员,然后读取另一个)是未定义的行为。它在ISO C99中定义良好,并且在GNU C++中作为扩展。(还有一些C++编译器,我想包括MSVC),也要小心非平凡的可复制类型(具有构造函

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

union {
    long i;
    float f;
    void* ptr;
};
其中成员i和f始终是ptr的大小(float&long表示32位/double&long表示64位)


什么是用最小的宏使用来实现这一点?

注意,在UNISC++中,联合类型双关(写入一个成员,然后读取另一个)是未定义的行为。它在ISO C99中定义良好,并且在GNU C++中作为扩展。(还有一些C++编译器,我想包括MSVC),也要小心非平凡的可复制类型(具有构造函数/析构函数)作为联合成员。< /P> 当然,除了类型双关(例如,手滚多态性)之外,还有一些其他的联合用法,类似这样的用法是有意义的


就是因为这个原因而存在的。(或者
intptr\u t
或者
ptrdiff\u t
,如果出于某种原因您需要签名类型)

但是对于
float
double
相比,您需要预处理器
uintpttr_MAX
提供了一种使用预处理器检查指针宽度的方法,这与
sizeof(void*)

请注意,
uintptr\u t
通常与指针的宽度相同,但类型名称定义为可以存储指针值的名称。对于32位平台上的
floatptr\t
,情况并非如此。(有趣的事实:对于“规范”48位地址1,它将出现在x86-64上)。如果这让你感到困扰,或者你担心它会扭曲你对
uintpttr\t
的看法,请选择一个不同的名称
floatptr_t
是短的,看起来是对的,即使它是“错的”

从而确认联盟本身和每个成员都具有预期的规模

MSVC也可以工作,编译为
int size\u f DD 08H
04H
等等


脚注1:在x86-64上,规范虚拟地址是扩展到64的48位符号,因此您实际上可以通过
intptr\t
->
double
转换往返指针值,而不会出现舍入错误。但对于不至少2字节对齐的高半地址,不能使用
uintpttr\t
->
double
。(如果没有AVX512F,uint64的转换速度很慢。)

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


在32位模式下,线性地址限制为32位。PAE允许多个32位进程各自使用不同4GB的物理内存,但seg:off->32位线性在页表查找之前发生。使用48位
seg:off
地址并不能获得更大的地址空间,因此编译器不会这样做。32位指针是
seg:off
地址的
off
部分,段基固定为零,因此它们与线性虚拟地址相同。与64位模式相同,64位偏移。

注意,在UNISC++中,联合类型双关(写入一个成员,然后读取另一个)是未定义的行为。它在ISO C99中定义良好,并且在GNU C++中作为扩展。(还有一些C++编译器,我想包括MSVC),也要小心非平凡的可复制类型(具有构造函数/析构函数)作为联合成员。< /P> 当然,除了类型双关(例如,手滚多态性)之外,还有一些其他的联合用法,类似这样的用法是有意义的


就是因为这个原因而存在的。(或者
intptr\u t
或者
ptrdiff\u t
,如果出于某种原因您需要签名类型)

但是对于
float
double
相比,您需要预处理器
uintpttr_MAX
提供了一种使用预处理器检查指针宽度的方法,这与
sizeof(void*)

请注意,
uintptr\u t
通常与指针的宽度相同,但类型名称定义为可以存储指针值的名称。对于32位平台上的
floatptr\t
,情况并非如此。(有趣的事实:对于“规范”48位地址1,它将出现在x86-64上)。如果这让你感到困扰,或者你担心它会扭曲你对
uintpttr\t
的看法,请选择一个不同的名称
floatptr_t
是短的,看起来是对的,即使它是“错的”

从而确认联盟本身和每个成员都具有预期的规模

MSVC也可以工作,编译为
int size\u f DD 08H
04H
等等


脚注1:在x86-64上,规范虚拟地址是扩展到64的48位符号,因此您实际上可以通过
intptr\t
->
double
转换往返指针值,而不会出现舍入错误。但对于不至少2字节对齐的高半地址,不能使用
uintpttr\t
->
double
。(如果没有AVX512F,uint64的转换速度很慢。)

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


在32位模式下,线性地址限制为32位。PAE允许多个32位进程各自使用不同4GB的物理内存,但seg:off->32位线性在页表查找之前发生。使用48位
seg:off
地址并不能获得更大的地址空间,因此编译器不会这样做。32位指针是
seg:off
地址的
off
部分,段基固定为零,因此它们与线性虚拟地址相同。与64位模式下的64位偏移量相同。

听起来有点像。听起来有点像。
uintpttr\u t
表示可以存储指针值的uint。。。很难说
floatptr\t
是否也能满足这一保证
1UL@M.M:Good catch。我用
1all
修复了它,它保证至少是64位,因此足够大,可以表示
2^32
。移动两次可避免UB,但可能导致
0
。我只是在用x86-64模式运行的编译器进行测试
#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;
};
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