为什么“从'x*'到'y'失去精度”是一个硬错误,什么是适合旧版代码的解决方案

Why is “cast from ‘X*’ to ‘Y’ loses precision” a hard error and what is suitable fix for legacy code

本文关键字:什么 一个 硬错误 解决方案 代码 失去 为什么 精度      更新时间:2023-10-16

1。为什么?

像这样的代码曾经有效,这是显而易见的意思。编译器是否允许允许(按规范)使其成为错误?

我知道它的精确度失去了,我会对警告感到满意。但是它仍然具有明确定义的语义(至少定义了未签名的缩小尺寸的演员),并且用户可能只想这样做。

2。解决方法

我有旧的代码,我不想重构太多,因为它很棘手并且已经进行了调试。它正在做两件事:

  1. 有时将整数存储在指针变量中。如果代码以前存储了整数,则仅将指针投放到整数。因此,尽管演员阵容正在缩小尺寸,但溢出在现实中永远不会发生。该代码经过测试并有效。

    当整数存储时,它总是适合普通的旧unsigned,因此更改类型不是一个好主意,并且指针经过很大的传递,因此更改它的类型将有些侵入性。

    <</p>
  2. 使用地址作为哈希值。一件相当普遍的事情。哈希表不大,可以扩展类型。

    代码使用普通的unsigned来实现哈希值,但是请注意,size_t 的常规类型仍然可能生成错误,因为不能保证sizeof(size_t)> = sizeof(void *)。在具有分段内存和远处指针的平台上,size_t只需要覆盖偏移部分。

那么,最不适合侵入性的解决方法是什么?已知该代码在与不产生此错误的编译器编译时可以使用,因此我真的想执行操作,而不是更改。


注意:

void *x;
int y;
union U { void *p; int i; } u;
  1. *(int*)&xu.p = x, u.i不是不是等效于 (int)x,而不是 (void *)y的对立面。在大型末端架构上,前两个将在较低地址上返回字节,而后者将以低顺序字节进行工作,该字节可能位于更高的地址上。
  2. *(int*)&xu.p = x, u.i都是严格的违规违规行为, (int)x

c ,5.2.10:

4-可以将指针明确转换为任何足够大的积分类型以容纳它。[...]

c,6.3.2.3:

6-任何指针类型都可以转换为整数类型。[...]如果结果无法在整数类型中表示,则行为是不确定的。[...]

,如果int为32位,而void *为64位,则(int) p是非法的。C 编译器是正确的,可以给您错误,而C编译器可能会在翻译上出现错误,或者发出未定义行为的程序。

您应该写,添加一个转换:

(int) (intptr_t) p

或使用C 语法,

static_cast<int>(reinterpret_cast<intptr_t>(p))

如果您要转换为未签名的整数类型,请通过uintptr_t而不是intptr_t转换。

这是一个很难解决的"一般",因为" LOSES PROCISION"表示您的指针大于您试图将其存储的类型大。这很可能是"好的:"在您的脑海中,编译器担心您将恢复INT值重新回到指针中,该指针现在已经失去了32位的上限(假设我们在谈论32位INT和64位指针 - 有其他可能的组合)。

uintptr_t与系统上的指针兼容大小兼容,因此通常,您可以通过:

来克服实际错误
int x = static_cast<int>(reinterpret_cast<uintptr_t>(some_ptr));

这将首先迫使指针的大整数,然后将大整数施放为较小的类型。

c

的答案

将指针转换为整数正在定义。您的问题是您所谈论的代码似乎永远都不正确。可能只在int和指针的古代建筑上工作。

如果存在于平台上(通常是这样),则唯一应该不损失的类型是[u]intptr_t。这种uintptr_t的哪一部分适合用于您的哈希功能,您不应该对此做出任何假设。我会去做

之类的东西
uintptr_t n = (uintptr_t)x;

,然后

((n >> 32) ^ n) & UINT32_MAX

可以在32位拱门上进行优化,并为您提供64位拱门上所有其他位的痕迹。

对于C 基本上应该适用,只有铸件为reinterpret_cast<std:uintptr_t>(x)

相关文章: