如何确定平台上的最大指针大小?

ein*_*ica 5 c++ pointers sizeof

在(C和)C ++中,指向不同类型的指针不一定具有相同的大小。我本来希望void *一定是最大的,但实际上甚至没有保证。

我的问题:如何确定我的(编译目标)平台上最大的指针大小?

注意:我的意思是任何指针,包括指向类成员函数的指针。您可以从&运营商那里得到的东西。我的意思不是“口头地”称为指针的实体,即不是unique_ptr'或shared_ptr'之类的。

Mic*_*zel 5

C++语言中有四类完全不相关的指针类型:对象指针、函数指针、非静态数据成员指针和非静态成员函数指针。术语“指针”通常仅适用于对象和函数指针类型[basic.compound]/3

\n\n
\n

[...] 除了指向静态成员的指针之外,引用 \xe2\x80\x9cpointers\xe2\x80\x9d 的文本不适用于指向成员的指针。[…]

\n
\n\n

指针和指向非静态成员的指针实际上被视为两种完全不同的复合类型[basic.compound]/1(这是有道理的,因为非静态成员指针更像是相对偏移量,而不是实际地址)。

\n\n

除了对象和函数指针之间有条件支持的转换(其语义(如果支持)将由实现定义[expr.reinterpret.cast]/8 )之外,无法在这四类指针之间进行转换类型。

\n\n

但是,该标准确实指定了对象指针之间的互用性[expr.reinterpret.cast]/7、函数指针之间的互用性[expr.reinterpret.cast]/6、数据成员指针之间的互用性[expr.reinterpret.cast]/10.2和成员函数指针之间的互换性[expr.reinterpret.cast]/10.1

\n\n

因此,虽然不存在与所有其他指针类型通常相关的公共指针类型,但将任何对象指针转换为某个任意对象指针类型并返回是定义明确的行为。将任何函数指针转换为某个任意函数指针类型并返回是定义明确的行为。将任何数据成员指针转换为某种任意数据成员指针类型并返回是定义明确的行为。将任何成员函数指针转换为某个任意成员函数指针类型并返回是定义明确的行为。所有这些不同类别的指针类型的共同点是它们都是对象类型[basic.types]/8

\n\n

虽然这并不严格保证,例如,所有成员函数指针类型具有相同的大小,但它确实隐式地建立了某些成员函数指针类型的任何对象都可以有效地用于存储任何成员函数指针值。可能仍然存在比其他成员函数指针类型更大的成员函数指针类型,但它们不可能比其他成员函数指针类型保存更多信息,因为标准要求与任何其他成员函数指针类型之间的转换不得丢失信息(始终可以恢复原始值) )。对于所有其他类型的指针类型,相同的参数也类似地起作用。

\n\n

基于这一切,我认为在标准 C++ 中找到“最大的指针类型”在技术上是不可能的。然而,虽然技术上不可能找到最大的指针类型本身,但根据上面的论点,绝对有可能找到可靠地存储任何指针类型值所需的存储量的上限。虽然这两者在技术上是不同的,但实际上,第二个很可能几乎与第一个一样好(没有合理的编译器会随机添加大量填充位到某些指针类型的值表示中,只是因为这样做在技术上是合法的)。至少我很难想象除了存储指针值之外您可能还想对您所要求的信息进行什么处理。

\n\n

使用,例如

\n\n
using generic_obj_ptr = void*;\nusing generic_fun_ptr = void (*)();\n\nclass dummy_t;\nusing generic_dat_mem_ptr = dummy_t dummy_t::*;\nusing generic_mem_fun_ptr = void (dummy_t::*)();\n
Run Code Online (Sandbox Code Playgroud)\n\n

你可以计算

\n\n
auto obj_ptr_size = sizeof(generic_obj_ptr_t);\nauto fun_ptr_size = sizeof(generic_fun_ptr_t);\nauto dat_mem_ptr_size = sizeof(generic_dat_mem_ptr_t);\nauto mem_fun_size = sizeof(generic_mem_fun_ptr_t);\n\nauto max_ptr_size = std::max({ sizeof(generic_obj_ptr_t), sizeof(generic_fun_ptr_t), sizeof(generic_dat_mem_ptr_t), sizeof(generic_mem_fun_ptr_t) });\nauto max_ptr_align = std::max({ alignof(generic_obj_ptr_t), alignof(generic_fun_ptr_t), alignof(generic_dat_mem_ptr_t), alignof(generic_mem_fun_ptr_t) });\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者只是使用

\n\n
using ptr_storage_t = std::aligned_union<0U, generic_obj_ptr_t, generic_fun_ptr_t, generic_dat_mem_ptr_t, generic_mem_fun_ptr_t>;\n
Run Code Online (Sandbox Code Playgroud)\n\n

甚至

\n\n
using any_ptr_t = std::variant<generic_obj_ptr_t, generic_fun_ptr_t, generic_dat_mem_ptr_t, generic_mem_fun_ptr_t>;\n
Run Code Online (Sandbox Code Playgroud)\n\n

或其纯粹形式:

\n\n
using any_ptr_t = std::variant<void*, void (*)(), dummy_t dummy_t::*, void (dummy_t::*)()>;\n
Run Code Online (Sandbox Code Playgroud)\n\n

作为存储,在转换为和从 时可以存储任何对象指针值void*,在转换为和从 时可以存储任何函数指针值void (*)(),在转换为和从 时可以存储任何数据成员指针dummy_t dummy_t::*,并且可以是任何成员函数指针投射到 和来自 时存储void (dummy_t::*)()

\n\n

在这里玩它

\n\n

将其包装在一个类中的任务,该类负责存储任何指针类型的任意值的所有转换(不要忘记处理可能的 cv 限定),应留给读者作为练习,主要是因为我今晚真的很想睡个好觉……

\n