【问题标题】:Copying Byte Pattern For Floats Does Not Work复制浮点数的字节模式不起作用
【发布时间】:2021-12-09 20:15:55
【问题描述】:

我目前正在自学 C++,并尽我所能学习有关内存的知识。我发现您可以使用 char 指针来复制 int 的位模式,并通过强制转换将其存储在内存中:

#include <iostream>

using namespace std;

int main()
{

int x = 20;

char* cp = new char[sizeof(int)];

cp[0] = *((char*)&x);
cp[1] = *((char*)&x+1);
cp[2] = *((char*)&x+2);
cp[3] = *((char*)&x+3);

std::cout << (int)*cp; // returns 20; 


return 0;
}

上面的代码有效,当我将 cp 转换为 int 以便编译器一次读取 4 个字节时,我得到了正确的数字,即 20。

但是将其更改为浮点数:

#include <iostream>

using namespace std;

int main()
{

float x = 20;

char* cp = new char[sizeof(float)];

cp[0] = *((char*)&x);
cp[1] = *((char*)&x+1);
cp[2] = *((char*)&x+2);
cp[3] = *((char*)&x+3);

std::cout << (float)*cp; // returns 0.


return 0;
}

返回0。现在我在这里有点困惑。如果我复制每个字节,为什么它仍然给我一个 0?如果有人能帮助我理解这一点,那就太棒了。

【问题讨论】:

  • (float)*cp 更改为*(float*)cp。我告诫不要使用这些做法,因为它们绕过了 C++ 的强类型系统,并且只应在极少数情况下使用并应谨慎使用。
  • @Elijay 这似乎已经解决了,我以为我在逻辑上做错了什么。你能解释一下为什么我必须做 (float)cp 而不是 (float)*cp。我想我明白了为什么,但它还没有完全点击。是的,非常感谢你,我绝对不会在其他项目中这样做。我目前只是想了解记忆,我发现我对它有如此多的控制权。
  • *cp 首先发生,然后(float) 将 ASCII 字符的值从整数转换为浮点数。其他字节不被访问。
  • @Eljay 它可能会起作用,但它是未定义的行为。千万不要这样做,说真的,如果你遇到了违反严格别名规则导致的错误,甚至猜测发生了什么都会非常痛苦。这不值得,额外的std::memcpy 不会杀死任何人,编译器可以看穿它并尽可能避免实际复制。

标签: c++ pointers memory


【解决方案1】:

(int)*cp; 首先解除对指针的引用,返回一个char 值,该值现在被静态转换为整数。这仅适用于 char 可以存储的范围 - 0 255-128 127,并且需要 little-endian 系统。

看起来修复它的方法是*reinterpret_cast&lt;float*&gt;(cp);*((float*)cp)。两者都是错误的并导致未定义的行为,因为它们违反了严格的别名规则。

严格的别名规则规定,只有当指针指向的内存位置存在T 类型的对象时,才能取消引用指向T 的指针。 charstd::byteunsigned char 除外。这意味着通过强制转换为char 来检查任何类型都是正确的,但不能简单地将一堆字节解释为随机的T

序列化和反序列化对象的正确方法是:

#include <iostream>

using namespace std;

int main() {
    float x = 20.0f;

    // This is safe.
    char* cp1 = reinterpret_cast<char*>(&x);
    // Also safe because there is a float object at cp1.
    std::cout << *reinterpret_cast<float*>(cp1);

    // No need for dynamic allocation.
    char cp2[sizeof(float)];
    // Copy the individual bytes into a buffer.
    //  = Serialize the object.
    std::memcpy(cp2, &x, sizeof(x));

    // NOT SAFE, UNDEFINED BEHAVIOUR
    // There is no float object at cp2.
    std::cout << *reinterpret_cast<float*>(cp2);

    // Deserialization through copy
    float y;
    std::memcpy(&y, cp2, sizeof(y));
    // Safe
    std::cout << y;

    return 0;
}

【讨论】:

  • 我明白了,我必须告诉你我还没有重新解释演员工具...我在 youtube 上观看了一些使用 char* 和 void* 的视频。感谢您向我展示了如何安全地使用它,尽管我主要不想使用 memcopy 或其他工具,因为我想看看如何自己实际实现它。做 (float)cp 在我的情况下实际上是有效的(感谢上面的评论者)。对于那些对内存如何与编程语言和一些陷阱一起工作感兴趣的初学者,你有什么好的资源吗?我只是对字节和内存着迷。非常感谢!
  • @MP3D 好的,手动复制字节也可以,但std::memcpy 经常被编译器识别,特别是在这些重新解释内存的情况下。像std::uint32_t x; float y; std::memcpy(&amp;x,&amp;y,4); 这样的代码是将整数字节解释为浮点数的唯一安全方法(用于一些魔术)。并且编译器认识到这一点并避免复制,memcpy 只是作为对别名问题的提示。
  • @MP3D 是的,很可能代码可以工作,直到突然由于一些优化而无法工作。不幸的是我没有任何资源,也许看看curated list of C++ books。我肯定会避免使用任何 C++11 之前的资源,或者至少不要从它们开始。
猜你喜欢
  • 2014-06-07
  • 2013-07-02
  • 2018-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-24
相关资源
最近更新 更多