【问题标题】:reinterpret_cast<> and portabiltyreinterpret_cast<> 和可移植性
【发布时间】:2014-05-01 03:13:27
【问题描述】:

我读到reinterpret_cast&lt;&gt; 如果使用不当会很危险。所以我相信我使用得当;)。如果我有模板类并且需要类型转换,我发现它很好用。但最近我读到reinterpret_cast&lt;&gt; 也是不可移植的。我为这一点感到难过。什么原因?取以下代码,

void Disp(int* val)
{
    for (int i=0; i < SZ; ++i)
    {
        cout << *(val+i) << " ";
    }
    cout << endl;
}

int main()
{
    int arr[SZ];
    Disp(arr);

    unsigned char* ptr = reinterpret_cast<unsigned char*>(arr);
    for (unsigned char* i = ptr; i < (ptr + (SZ * sizeof(int))); i++)
    {
        *i = 0;
    }
    Disp(arr);
    return 0;
}

现在输出:

1174214872 32767 4196789 0 568392584 58 4196720 0 0 0 
0 0 0 0 0 0 0 0 0 0 

Machine type: Linux 2.6.32-358.11.1.el6.x86_64 #1 x86_64 x86_64 x86_64 GNU/Linux

975580 -16506540 -13369152 0 -4202936 67876 3 -4202836 4 -4202828 
0 0 0 0 0 0 0 0 0 0 

Machine type: SunOS DELPHI 5.10 Generic_142900-01 sun4u sparc SUNW,Netra-240

我在 Linux 和 Solaris 中都复制了同一个程序的输出。我是可移植性问题的新手。所以谁能告诉我,如果我在我的代码中使用这样的东西,会导致任何可移植性问题吗?即使不使用此代码,当代码变得复杂(使用动态分配等)并长时间运行时,是否有可能出现意外(未定义的行为)。感谢您的帮助。

【问题讨论】:

  • 看到这个答案:stackoverflow.com/a/4748348/1491895 指针类型之间的转换是可以的。
  • 我打赌sizeof(int) 不是4*sizeof(unsigned char) 在其中一台机器上。
  • 您应该使用sizeof(int),而不是硬编码4
  • 我已经改变了。谢谢。
  • 不计算数组的大小,而是使用i &lt; ptr + sizeof arr。或者直接使用i &lt; 1[&amp;arr]

标签: c++ casting reinterpret-cast


【解决方案1】:

reinterpret_cast&lt;&gt; 的可移植性问题在于不同的 CPU 在内存中存储数字的方式不同。有些将它们从最低有效字节存储到最高有效字节(小端),另一些则恰恰相反(大端)。有些人甚至使用了一些奇怪的字节顺序,比如1 0 3 2,别问我为什么。

无论如何,这样做的结果是,reinterpret_cast&lt;&gt; 是可移植的只要您不以任何方式依赖字节顺序

您的示例代码不依赖于字节顺序,它将所有字节都视为相同(将它们设置为零),因此代码是可移植的。如果您使用reinterpret_cast&lt;&gt; 复制一些数据对象在同一台机器上 而不解释字节,代码也将是可移植的(memcpy() 这样做)。

不可移植的是诸如查看第一个字节以确定数字的符号之类的东西(仅适用于大端机器)。如果您尝试通过发送reinterpret_cast&lt;char*&gt; 的结果将数据从一台机器传输到另一台机器,那么您也会遇到麻烦:目标机器可能使用与源机器不同的字节顺序,从而完全误解了您的数据。

我想说reinterpret_cast&lt;&gt; 是不可移植的是错误的,它只是将机器细节暴露给特定于机器的 C++ 代码。任何依赖于该机器细节的代码都是不可移植的。

【讨论】:

    【解决方案2】:

    这段代码有一些问题。

    • 您正在显示未初始化数组的内容。真的没有任何意义。
    • 假设sizeof(int) == 4,您正在硬编码循环的大小。如果您使用的是不同大小的机器int,这将是不可移植的。
    • 如果您使用非零值填充内存,则需要担心处理器的字节顺序。

    编辑:我看到你已经删除了硬编码的魔法常数4,所以第 2 点不再适用。

    【讨论】:

    • 除了用其他值初始化之外,我还需要担心什么吗?我的意思是,你能指出其他一些 reinterpret_cast 变得不可移植的情况吗?
    • @Sarath 转换为char * 是一种通用的方式,因此您可以触发的麻烦有限。更成问题的是对象之间的转换,这些对象可能具有不同的布局,具体取决于您的编译器或设置。或者换一种方式,即将char * 转换为int *,您可能会在某些处理器上遇到对齐问题。
    【解决方案3】:

    我认为这没问题。我看到有人争辩说它是未定义的,因为[basic.lval]#10 中允许的别名列表不包括使用int 来对通过char 类型写入的别名值进行别名。 (但也包括相反的情况)。

    但是,我将“对象的存储值”解释为仍然引用 int 值,即使在通过 char 别名写入之后也是如此。这似乎在 C++ 标准中没有明确表达;但是在 C99 中(这些规则显然源自这些规则),它有不同的措辞涉及 有效类型,它指定内存的有效类型仍然是 int,尽管它是通过 char 编写的别名。

    澄清;在这两种语言中,还有其他规则涉及是否创建陷阱表示的问题。但我看到它认为 C++ 中的严格别名规则胜过这种情况下的那些规则。

    【讨论】:

      【解决方案4】:

      reinterpret_cast&lt;&gt; 的可移植性/实用性问题将取决于它的使用方式。获得有关玩具示例的答案对于您可能真正发现自己想要使用它的任何情况都无济于事。

      一般来说,如果有办法在不使用reinterpret_cast&lt;&gt; 的情况下做某事,那么您应该这样做。使用您问题中的示例,您可以通过使用int* (even though I think that your example's used ofreinterpret_castis in fact well-defined and portable - using a pointer that has been reinterpret-casted to achar*` 是可移植的少数区域之一,将数组可移植地清零而不使用 reinterpret_cast&lt;&gt;

      在大多数情况下,您可能会发现需要 reinterpret_cast&lt;&gt;,但您正在处理的东西确实不可移植,您应该了解为什么在特定情况下需要 reinterpret_cast&lt;&gt;,并了解将会出现的可移植性问题特定于该特定情况。

      【讨论】:

        猜你喜欢
        • 2021-07-23
        • 1970-01-01
        • 1970-01-01
        • 2015-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-16
        • 1970-01-01
        相关资源
        最近更新 更多