C++ 如何编写endian不可知C/C++;密码?
我在谷歌上搜索了一下,没有找到关于这个问题的好文章。在实现一个我想成为endian不可知论者的应用程序时,我应该注意什么?这可能是一篇很好的文章供您阅读: 计算机的字节顺序一点也不重要,除了编译器编写者之类的人,他们对映射到寄存器段的内存字节的分配非常挑剔。你可能不是一个编译器编写者,所以计算机的字节顺序对你来说一点都不重要 注意短语“计算机的字节顺序”。重要的是外围设备或编码数据流的字节顺序,但——这是关键点——进行处理的计算机的字节顺序与数据本身的处理无关。如果数据流用字节顺序B编码值,那么在计算机上用字节顺序C解码值的算法应该是关于B,而不是关于B和C之间的关系C++ 如何编写endian不可知C/C++;密码?,c++,c,endianness,C++,C,Endianness,我在谷歌上搜索了一下,没有找到关于这个问题的好文章。在实现一个我想成为endian不可知论者的应用程序时,我应该注意什么?这可能是一篇很好的文章供您阅读: 计算机的字节顺序一点也不重要,除了编译器编写者之类的人,他们对映射到寄存器段的内存字节的分配非常挑剔。你可能不是一个编译器编写者,所以计算机的字节顺序对你来说一点都不重要 注意短语“计算机的字节顺序”。重要的是外围设备或编码数据流的字节顺序,但——这是关键点——进行处理的计算机的字节顺序与数据本身的处理无关。如果数据流用字节顺序B编码值,那么
唯一需要关心endianness的是在可能没有相同endianness的系统之间传输endian敏感的二进制数据(即,不是文本)时。通常的解决方案是使用“”(也称为big-endian)传输数据,然后在另一端根据需要切换字节
要将主机字节顺序转换为网络字节顺序,请使用
htons(3)
和htonl(3)
。要转换回,请使用ntohl(3)
和ntohs(3)
。查看您需要了解的所有信息。对于64位数据,这将很有帮助。在您的代码中,您几乎可以忽略它-所有内容都会被抵消
当您向磁盘或网络读/写数据时,如果需要在2、4或8字节整数类型和字节索引数组之间重新解释(反之亦然),请使用 这在加密算法实现、序列化应用程序(如网络协议、文件系统或数据库后端)以及操作系统内核和驱动程序中经常出现 它通常由诸如ENDIAN之类的宏检测到。。。什么 例如:
uint32 x = ...;
uint8* p = (uint8*) &x;
p在BE机器上指向高位字节,在LE机器上指向低位字节
使用可以编写的宏:
uint32 x = ...;
#ifdef LITTLE_ENDIAN
uint8* p = (uint8*) &x + 3;
#else // BIG_ENDIAN
uint8* p = (uint8*) &x;
#endif
例如,始终获取高字节
这里有定义宏的方法:如果你的工具链没有提供它们。这显然是一个颇具争议的话题 一般的方法是设计应用程序,使您只关心一小部分的字节顺序:代码的输入和输出部分 在其他地方,您应该使用本机字节顺序 请注意,尽管大多数机器都以相同的方式执行此操作,但不能保证浮点和整数数据以相同的方式存储,因此要完全确保工作正常,您不仅需要知道大小,还需要知道它是整数还是浮点 另一种选择是只使用和生成文本格式的数据。这几乎是同样容易实现的,并且除非您在很少处理的情况下有很高的数据进出应用程序的速率,否则它的性能差别可能很小。(对某些人)的好处是,当代码中出现错误时,您可以在文本编辑器中读取输入和输出数据,而不是试图解码输出中字节51213498-51213501的实际值 在实现一个我想成为endian不可知论者的应用程序时,我应该注意什么 您首先必须认识到endian何时成为问题。当您必须从外部某处读取或写入数据时,无论是从文件读取数据还是在计算机之间进行网络通信,这通常都会成为一个问题 在这种情况下,endianess对于大于一个字节的整数很重要,因为不同的平台在内存中对整数的表示不同。这意味着每次您需要读取或写入外部数据时,您需要做的不仅仅是转储程序内存,或者直接将数据读取到您自己的变量中 e、 g.如果您有这段代码:
unsigned int var = ...;
write(fd, &var, sizeof var);
您直接写出的是var
的内存内容,这意味着数据会显示在数据所到的任何地方,就像它在您自己的计算机内存中所表示的一样
如果将此数据写入文件,则无论在大端机还是小端机上运行程序,文件内容都将不同。因此,代码不是endian不可知论的,您应该避免这样做
相反,关注数据格式。在读取/写入数据时,始终先确定数据格式,然后编写代码来处理数据。如果您需要阅读一些现有的定义良好的文件格式或实现现有的网络协议,那么这可能已经为您做出了决定
了解数据格式后,您的代码将执行以下操作,而不是直接转储int变量:
uint32_t i = ...;
uint8_t buf[4];
buf[0] = (i&0xff000000) >> 24;
buf[1] = (i&0x00ff0000) >> 16;
buf[2] = (i&0x0000ff00) >> 8;
buf[3] = (i&0x000000ff);
write(fd, buf, sizeof buf);
我们现在已经选取了最高有效字节并将其作为缓冲区中的第一个字节,最低有效字节放在缓冲区的末尾。该整数在buf
中以大端号格式表示,与主机的端号无关-因此此代码是端号不可知的
此数据的使用者必须知道数据是以大端格式表示的。无论程序运行在哪台主机上,此代码都可以很好地读取数据:
uint32_t i;
uint8_t buf[4];
read(fd, buf, sizeof buf);
i = (uint32_t)buf[0] << 24;
i |= (uint32_t)buf[1] << 16;
i |= (uint32_t)buf[2] << 8;
i |= (uint32_t)buf[3];
这最多也会导致性能下降(在某些情况下是无关紧要的,在其他情况下是许多数量级),在无法对整数进行未对齐访问的平台上更会导致崩溃。有几个答案涉及到文件IO,这当然是最常见的endian问题。我将谈到一个尚未提及的问题:工会 以下union是SIMD/SSE编程中的常用工具,不支持endian:
union uint128_t {
_m128i dq;
uint64_t dd[2];
uint32_t dw[4];
uint16_t dh[8];
uint8_t db[16];
};
任何代码
uint32_t i;
uint8_t buf[4];
read(fd, buf, sizeof buf);
i = ntohl(*(uint32_t)buf));
union uint128_t {
_m128i dq;
uint64_t dd[2];
uint32_t dw[4];
uint16_t dh[8];
uint8_t db[16];
};
union u64_parts {
uint64_t dd;
uint32_t dw[2];
};
union u64_parts {
uint64_t dd;
struct {
#ifdef BIG_ENDIAN
uint32_t dw2, dw1;
#else
uint32_t dw1, dw2;
#endif
}
};