C++ 将uint8_t转换为其二进制表示形式

C++ 将uint8_t转换为其二进制表示形式,c++,c,serialization,casting,uint8t,C++,C,Serialization,Casting,Uint8t,我有一个类型为uint8\u t的变量,我想将其序列化并写入一个文件(至少对于Windows来说,它应该是可移植的,这正是我的目标) 试图以二进制形式将其写入文件时,我遇到了以下代码段: uint8_t m_num = 3; unsigned int s = (unsigned int)(m_num & 0xFF); file.write((wchar_t*)&s, 1); // file = std::wofstream 首先,让我确保我理解了这个代码段的作用-它接受我的va

我有一个类型为
uint8\u t
的变量,我想将其序列化并写入一个文件(至少对于Windows来说,它应该是可移植的,这正是我的目标)

试图以二进制形式将其写入文件时,我遇到了以下代码段:

uint8_t m_num = 3;
unsigned int s = (unsigned int)(m_num & 0xFF);
file.write((wchar_t*)&s, 1); // file = std::wofstream
首先,让我确保我理解了这个代码段的作用-它接受我的var(基本上是一个无符号字符,1字节长),将其转换为
无符号int
(4字节长,不太便于移植),并使用
&0xFF
“提取”最不重要的字节

现在,有两件事我不明白:

  • 为什么首先要将其转换为无符号整数,为什么我不能简单地执行类似于
    file.write((wchar\u t*)&m\u num,1)
    重新解释强制转换(&m_num)
    ?()
  • 如何序列化较长的类型,例如
    uint64\u t
    (8字节长)<代码>无符号整数
    在这里可能足够,也可能不够

  • uint8\u t
    是1字节,与
    char

    wchar\u t
    在Windows中为2字节,在Linux中为4字节。这也取决于持久性。如果担心可移植性,则应避免使用
    wchar\t

    您只需使用
    std::ofstream
    。Windows为
    std::ofstream
    提供了一个附加版本,它接受UTF16文件名。这样,您的代码与Windows UTF16文件名兼容,并且仍然可以使用
    std::fstream
    。比如说

    int i = 123;
    std::ofstream file(L"filename_in_unicode.bin", std::ios::binary);
    file.write((char*)&i, sizeof(i)); //sizeof(int) is 4
    file.close();
    ...
    std::ifstream fin(L"filename_in_unicode.bin", std::ios::binary);
    fin.read((char*)&i, 4); // output: i = 123
    
    这相对简单,因为它只存储整数。这将适用于不同的Windows系统,因为Windows始终是小端,并且
    int
    大小始终为4

    但是有些系统是big-endian的,你必须单独处理


    如果使用标准I/O,例如
    fout,则不可能将
    unit8\t
    值写入
    wofstream
    ,因为
    wofstream
    只写入宽字符,根本不处理二进制值

    如果您想写一个宽字符,表示0到255之间的代码点,那么您的代码是正确的

    若您想将二进制数据写入文件,那个么最接近的等价物是流的
    ,这将允许您写入字节

    回答您的问题:

  • wofstream::write
    写入宽字符,而不是字节。如果将
    m_num
    的地址重新解释为宽字符的地址,则将写入一个16位或32位(取决于平台)宽字符,其中第一个字节(即最低有效或最高有效,取决于平台)是
    m_num
    的值,剩余字节是
    m_num
    之后内存中发生的任何事件。根据宽字符的字符编码,该字符甚至可能不是有效字符。即使有效,也基本上是胡说八道。(如果
    wofstream::write
    需要宽字符对齐而不是字节对齐的输入,或者
    m_num
    后面紧跟着不可读的内存,则可能会出现其他问题)

  • 如果您使用的是
    wofstream
    ,那么这将是一个混乱的局面,我无法解决它。如果切换到面向字节的
    ,则有两种选择1。如果您只在同一系统上读取文件,
    file.write(&myint64value,sizeof(myint64value))
    将起作用。写入64位值的字节的序列将是未定义的,但在读回时将使用相同的序列,因此这无关紧要。不要尝试使用
    wofstream
    做类似的事情,因为这很危险2。分别提取
    myint64value
    的8个字节(右移8位的倍数,然后取底部的8位),然后写入。这是完全可移植的,因为您可以控制写入字节的顺序


  • 这段代码很可怕,不可移植,并且具有未定义的行为。它还针对一系列你可能不是的广泛角色。嗨@AlanStokes,谢谢你的评论。你能详细解释一下为什么这个代码不好而且不可移植吗?什么是更明智的方法呢?嗨@BarmakShemirani,谢谢你的回答!现在它确实很有意义!显然,我想char更便于携带。您能解释一下为什么使用标准i/o更好吗?还有什么其他的可移植性问题?我在回答中补充了更多的解释。文本也有一个问题。如果您希望与其他系统兼容,通常的做法是将UTF16转换为UTF8。我不知道你是否在文件中包含文本,我没有参与。谢谢@nugae!关于第二点,问题是持久性,对吗?使用像
    htons
    htonl
    和friends(基本上将标准设置为big-endian)这样的函数可以解决问题,我错了吗?是的,它是endianness。只要您停留在一个系统内,这并不重要,但如果您想要系统间的兼容性,那么它就可以
    htonl及其亲属可以工作,但(根据文档)他们只会上升到
    uint32\t
    。因此,如果要执行
    uint64\u t
    ,则必须分别执行下半部分(
    &0xffffffffU
    )和上半部分(
    >32
    )。您可以将其打包到自己的
    htonl64
    函数中,或者(更好)打包到自己的
    write64
    read64
    函数中。