【问题标题】:C/C++ What does casting do in the low level?C/C++ 低级强制转换有什么作用?
【发布时间】:2020-10-28 13:33:35
【问题描述】:

有人告诉我,类型转换 C 转换只会改变系统解释信息的方式(例如,将 char 'A' 转换为 int 在使用 cout 打印时确实返回 65自从在 内存它保持为01000001)。

但是,我注意到,当将浮点数转换为相同宽度的整数时,该值是守恒的,不会改变,就像只改变解释一样。 例如,设X 为双精度浮点数:

double X = 3.14159;

就我现在而言,在检查&X 时,我们会发现(由decimal to binary converter 转换):

01000000 00001001 00100001 11111001 11110000 00011011 10000110 01101110

但是,正如你们中的一些人已经知道的那样,在这样做时:

long long Y = (long long)X;

Y 将是3,即X 的截断版本,而不是4614256650576692846,如果查找long long,则在查看&X 处的二进制值时将获得的值。

所以,我认为很明显他们错了,但是,低级别的演员如何工作?是否检测到该值是否会更改?您将如何编码以获取 Y = 4614256650576692846 而不是 Y = 3

【问题讨论】:

  • 所有演员表都不同。 这个演员改变了位。
  • memcpy(&Y, &X, sizeof(Y));
  • 每当您觉得需要在您的 C++ 程序中进行 C 风格的类型转换时,您应该将其视为您做错了什么的标志。
  • charint (几乎总是)不同的大小,因此这不是编译器只是重新解释位的最佳示例。在常见情况下,编译器必须在逻辑上将三个字节的数据添加到char,然后才能将其解释为int。如果char 是负数,这些字节甚至可能不都是0。将这种转换描述为创建一个与char 具有相同数值的int 要简单得多。
  • 演员阵容无关紧要。 long long Y = X; 会做同样的事情。

标签: c++ c casting type-conversion low-level


【解决方案1】:

铸造将尝试尽可能精确地保留值。 您可以使用memcpy() 来复制位模式。

#include <iostream>
#include <cstring>

int main() {
    double X = 3.14159;
    long long Y;
    memcpy(&Y, &X, sizeof(Y));
    std::cout << Y << '\n';
    return 0;
}

【讨论】:

  • 只是一些吹毛求疵:不能保证sizeof(long long) == sizeof(double)。现在恰好是这样。此外,对于处理位,更喜欢使用unsigned 整数。
【解决方案2】:

强制转换让编译器决定如何更改数据,以使其尽可能有用,同时尊重所请求的数据类型。

intchar 的转换只是将解释从 65 更改为 'A'

然而,当我们有一个我们想要保存的值时,编译器将使用特殊指令进行转换。

例如,当从double 转换为long long 时,处理器将使用CVTTSD2SI 指令,该指令将FP 寄存器的值加载并截断为通用寄存器:

double a = 3.14159;
long long b = (long long)a;

将进行反汇编(为了便于理解,我去掉了堆栈指针):

movsd   xmm0, QWORD PTR [a]
cvttsd2si       rax, xmm0
mov     QWORD PTR [b], rax

因此,使用原始值的方法将如所选答案中所述:取消引用指向 double 的指针并将其放入 long long 变量中,或者如其他所述,使用 memcpy()。

【讨论】:

    【解决方案3】:

    如果要获取Y = 4614256650576692846,可以使用:

    double X = 3.14159;
    long long Y = *( (long long*)(&X) );
    

    这会将双指针转换为 long long 指针,然后编译器会认为 (long long*)(&amp;X) 是 long long 存储的某个地方。

    但我不建议你这样做,因为结果是基于double在你的机器上的存储方式,结果不能保证是4614256650576692846。

    【讨论】:

    • 不仅如此,这种演员阵容是未定义的行为。您不能将doublelong long 命名。 (在大多数情况下,只允许基于 char 的类型使用别名。)bit_cast 会解决这个问题,同时看起来更好。
    • 您需要 memcpy 才能安全便携地键入双关语。或 C++20 std::bit_cast。此答案中的代码仅适用于某些实现,例如g++ -fno-strict-aliasing,或始终适用于 MSVC。但是这个问题被标记为 g++ 和 clang,因此特定于 MSVC 的类型双关语不是一个好的答案,尤其是在没有警告它是 ISO C++ 中的 UB 以及在大多数编译器中并不完全安全的非标准扩展时。 (它可能碰巧在简单的情况下工作,但 memcpy 总是工作,并在现代编译器中有效地优化。)
    • 这是错误代码。这个答案违反了严格的别名。参见What is the strict aliasing rule? 它也可能违反6.3.2.3 Pointers, paragraph 7 of the C11 standard(其他版本和C++ 有类似的限制):“指向对象类型的指针可能会转换为指向不同对象类型的指针。如果生成的指针未正确对齐引用类型,行为未定义。”
    猜你喜欢
    • 2012-04-13
    • 2017-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 2012-02-10
    • 2013-06-02
    相关资源
    最近更新 更多