用非零值初始化void指针的正确(或最安全)方法



以下作品:

void* p = reinterpret_cast<void*>(0x1000);

但看起来"不正确/不安全",例如0x1000是int,甚至不是uintptr_t,我可以解决这个问题,但有更好/更安全的铸造方法吗?

0x1000是int,甚至不是uintptr_t,我可以解决这个问题,但有更好/更安全的方法来铸造吗

0x1000int,在reinterpret_cast<void*>(0x1000)中,编译器发出一个符号扩展指令(此处符号为0)或一个带有立即数操作数的普通寄存器加载指令,以使值与void*一样宽。

由于许多原因,编译器不可能知道0x1000是否代表有效地址,因此它必须遵守并假设它是有效地址。

reinterpret_cast将表示地址的整数转换为指针是目前的做法。

您可以使用reinterpret_cast从整数创建指针。

然而,不指向现有对象的指针(或数组中最后一个元素后1的指针,该对象被视为虚数组中唯一的元素,或为空指针)具有无效指针值。取消引用是UB,其他具有无效指针值的操作是特定于实现的,因此您需要确保编译器允许使用这些指针执行操作。

void* p = reinterpret_cast<void*>(0x1000); // invalid pointer,
// operations on it are implementation defined

§6.7.2化合物类型[基本化合物]

  1. […]指针类型的每个值都是以下值之一:

    3.1——指向对象或函数的指针(据说指针指向对象或功能),或

    3.2——指针经过对象的末尾(8.5.6),或

    3.3——该类型或的空指针值(7.11)

    3.4——指针值无效

§8.5.1.10重新解释cast[expr.relinterpret.cast]

  1. 指针可以显式转换为任何足以容纳它的整数类型。映射函数由实现定义。[…]
  2. 积分类型或枚举类型的值可以显式转换为指针。转换为整数的指针足够的大小(如果实现中存在任何此类大小)并返回相同的指针类型将具有其原始值;之间的映射指针和整数以其他方式由实现定义

允许从整数转换为指针,但如果结果指针值没有指向现有对象(或经过对象的对象),则结果指针的值无效。

现在关于如何处理无效指针:

§6.6.4存储持续时间[basic.stc]

  1. […]未定义通过无效指针值的指示和将无效指针值传递给释放函数行为任何其他对无效指针值的使用都具有实现定义的行为35

35)有些实现可能会定义复制无效指针值导致系统生成的运行时故障。


这篇文章被大量编辑,因为它在第一次迭代中是错误的。热烈感谢社区对我的纠正和挑战。

tl;医生:你可能就是不应该这么做

以下。。。看起来"不正确/不安全"。。。有更好/更安全的铸造方法吗?

正如另一个答案所指出的;不正确的";达到实现定义行为的程度。此外,你在使用一个神奇的数字。

但问题不在于选角,我相信。我真的有点怀疑你是否需要初始化一个带有文字地址的void*变量。为什么?

  • 如果该地址有某种类型的值,那么不要使用void *,而是使用类型指针。即使您以后想将指针传递给memset()memcpy(),甚至是一些使用void *的回调函数,也要延迟类型擦除。

  • 你从哪里得到那个号码的?你肯定知道幻数是坏的,对吧?好吧,至少使用之类的东西

    constexpr const uintptr_t sound_card_memory_mapped_buffer_address { 0x1000 };
    

    这解决了您的一个问题(而不是uintptr_t),而且读起来也更清楚,即使您使用void *:

    void* p = reinterpret_cast<void*>(sound_card_memory_mapped_buffer_address);
    
  • p是一个糟糕的变量名称选择。根据用途将其重命名。

  • 您真的需要初始化p吗?:

    • 如果你现在不使用它,为什么还要初始化它?尽量不要声明它,直到它即将被使用(如果有的话)。

    • 如果你打算使用它,为什么还要申报呢?尝试:

      do_something_with(sound_card_memory_mapped_buffer_address);
      

      并且看不到CCD_ 21。

很明显,我要问的问题比答案多,因为你只给我们提供了一句话。

最新更新