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

时间:2019-04-18 12:16:30

标签: c++ pointers sizeof

在(C和)C ++中,指向不同类型don't necessarily have the same size的指针。我本来希望void *一定是最大的,但似乎甚至无法保证。

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

注意:我的意思是 any 指针,包括指向类成员函数的指针; &运算符可以提供的功能。我不是指被“通俗地”称为指针的实体,即不是unique_ptrshared_ptr的指针。

2 个答案:

答案 0 :(得分:4)

有3种不同类型的指针,它们的大小可以不同:

  • 指向对象的指针
  • 功能指针
  • 成员函数指针

根据C ++ 17标准6.9.2.5,保证void *的大小足以容纳每个指向对象的指针:

  

指向cv限定([basic.type.qualifier])或cv不限定的指针   void可用于指向未知类型的对象。这样的指针   应该能够容纳任何对象指针。类型为cv void *的对象   应具有与简历相同的表示和对齐要求   字符*。

class A;

typedef void (A::*a_func_ptr)(void);
typedef void (*func_ptr)(void);

size_t a = sizeof(a_func_ptr), b = sizeof(func_ptr), c = sizeof(void*);

std::cout << std::max(a, std::max(b, c)) << std::endl;

应该做这项工作。

编辑:C ++ 17标准6.9.2.3表示

  

除指向静态成员的指针外,引用“指针”的文字   不适用于指向成员的指针。

因此,可能的最大指针是void *或函数指针:

std::cout << std::max(sizeof(void*), sizeof(void(*)(void))) << std::endl;

答案 1 :(得分:3)

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

  

[…]除指向静态成员的指针外,引用“指针”的文本不适用于指向成员的指针。 […]

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

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

但是,该标准确实指定了对象指针[expr.reinterpret.cast]/7之间的互转换性,函数指针[expr.reinterpret.cast]/6之间的互转换性,数据成员指针[expr.reinterpret.cast]/10.2之间的互转换性以及成员函数指针{{3}之间的互转换性}。

结果是,虽然通常没有其他所有指针类型都与之相关的通用指针类型,但是将任何对象指针转换为任意对象指针类型并返回的定义良好。将任何函数指针强制转换为任意函数指针类型并返回的定义明确。将任何数据成员指针强制转换为任意数据成员指针类型并返回的定义明确。并且,将任何成员函数指针转换为任意成员函数指针类型并返回的定义明确的行为。所有这些不同类型的指针类型的共同点是,它们都是对象类型[expr.reinterpret.cast]/10.1

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

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

例如,使用

using generic_obj_ptr = void*;
using generic_fun_ptr = void (*)();

class dummy_t;
using generic_dat_mem_ptr = dummy_t dummy_t::*;
using generic_mem_fun_ptr = void (dummy_t::*)();

您可以计算

auto obj_ptr_size = sizeof(generic_obj_ptr_t);
auto fun_ptr_size = sizeof(generic_fun_ptr_t);
auto dat_mem_ptr_size = sizeof(generic_dat_mem_ptr_t);
auto mem_fun_size = sizeof(generic_mem_fun_ptr_t);

auto 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) });
auto 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) });

或仅使用

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>;

甚至

using any_ptr_t = std::variant<generic_obj_ptr_t, generic_fun_ptr_t, generic_dat_mem_ptr_t, generic_mem_fun_ptr_t>;

或以其纯格式:

using any_ptr_t = std::variant<void*, void (*)(), dummy_t dummy_t::*, void (dummy_t::*)()>;

作为一种存储,在向void*进行转换时可以存储任何对象指针值,而从void (*)()进行转换时可以存储任何功能指针值,可以存储任何数据成员指针在与dummy_t dummy_t::*之间进行转换时,并且在与void (dummy_t::*)()之间进行转换时可以存储任何成员函数指针。

[basic.types]/8

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