繁体   English   中英

big endian和little endian值是否可移植?

[英]Are big endian and little endian values portable?

你好,我有一个小端dout和大端的小dout我知道这个问题已经问过n次但我无法弄清楚以下几点

让我们把int i = 10它以二进制形式存储为00000000 00000000 00000000 00001010在堆栈部分如下: -

00000000 |00000000 |00000000 |00001010   // In case of little endian
MSB-------------------------------------------LSB

大端

00001010 |00000000 |00000000 |00000000   // In case of in big endian
MSB-------------------------------------------LSB

在这个小端和大端都将给出相同的输出10?

那么这些小端和大端的用途是什么?


我被要求实现代码,这些代码可以在我的访谈中对所有大小系统都是可移植的。 我回答说:

编译器会自己做,如果int i = 10 in little endian,那么在big endian中它也是10作为输出

这个答案是否正确?

00000000 | 00000000 | 00000000 | 00001010 // big    endian

00001010 | 00000000 | 00000000 | 00000000 // little endian

无论数据是以big endian还是little endian模式存储,大多数情况下,只有当你试图通过指针访问内存中较小部分的变量时才会很重要,就像试图通过a访问32位整数的最低有效字符一样指向字符的指针或带有字符数组的联合。 另一个问题的例子是,如果您将文件中的数据直接读取到32位整数数组中,或者从32位整数数组中读取数据。 文件中的数据通常也以小端或大端模式存储。

据我所知,没有通用的编译时方法来确定cpu是以big endian模式还是little endian模式运行(特定的编译器可能已为此定义)。 您可以使用32位整数和大小为4的字符数组的并集编写测试代码。然后将union中的整数设置为10,并检查union字符数组[0]是否包含10表示小端模式,或者如果联合字符数组[3]包含10,这意味着大端模式。 可以使用其他方法来确定CPU是处于小端还是大端模式。

一旦确定cpu是处于小端还是大端模式,就可以包含条件代码来处理这两种情况,例如来自/来自32位整数数组的文件I / O. 如果您希望文件数据处于大端模式,但您的cpu处于小端模式,则必须在写入之前或从文件读取之后反转每个整数的字节。

无论cpu模式如何,您还可以编写代码序列以大端模式存储数据。 如果已经处于大端模式,它将浪费时间,但它适用于大端和小端模式:

char     buffer[256];
char *   ptr2char;
uint32_t uint32bit;
/* ... */
    ptr2char = buffer;    /* store uint32bit in big endian mode */
    *ptr2char++ = (uint32bit >> 24)&0xff;
    *ptr2char++ = (uint32bit >> 16)&0xff;
    *ptr2char++ = (uint32bit >>  8)&0xff;
    *ptr2char++ = (uint32bit      )&0xff;

只是为了修正你的整数图: int i = 10;

// Big endian
&i <- address of i
00000000 |00000000 |00000000 |00001010 // In case of big endian

MSB---------------------------LSB


// Lower memory -----------------> higher memory


// Little endian

00001010 |00000000 |00000000 |00000000 // In case of in little endian
&i <- address of i
LSB---------------------------MSB

小端最低有效字节 (LSB)存储在最低存储器地址中。

big endian中最高有效字节 (MSB)存储在最低内存地址中。

首先:你实际上混淆了big -little-endian字节顺序,正如@ rcgldr@ Galik的回答所指出的那样。 正如您在样本中显示的那样,字节顺序完全相反:

00000000 | 00000000 | 00000000 | 00001010 // big endian

00001010 | 00000000 | 00000000 | 00000000 // little endian

至于你的假设和问题:

“在这个小端和大端都将给出相同的输出10?”

这取决于你所指的输出类型

  1. 无论主机的endianess如何,以下代码都是可移植的,在任何情况下输出都是格式化文本( "10" ):

int i = 10;

std::cout << i << std::endl;

  1. 以下代码将无法移植。 由于值是以二进制形式写入的,因此字节顺序将保持逐字:

int i = 10;

std::ofstream binfile("binaryfile.bin");
binfile.write((const char*)&i,sizeof(int));

如果应在具有不同字节顺序的主机上读取文件,则后一个示例将不起作用。

为了解决这些问题,有htonl()ntohl()函数族。 通常,人们同意使用网络字节顺序(big-endian)格式来存储二进制数据或通过网络发送它。

这是一个简短的示例,如何使用上面提到的字节顺序转换函数:


int i = 10;
int sendValue = htonl(i); // convert the value of i to network byte order

std::ofstream binfile("binaryfile.bin");
binfile.write((const char*)&sendValue,sizeof(int)); // write the adapted value

std::ifstream binfile("binaryfile.bin");
int recvValue = 0;
binfile.read((char*)&recvValue,sizeof(int)); // read the value in network byte order
int i = ntohl(recvValue); // convert the value of recvValue to host byte order

“那么这些小端和大端的用途是什么?”

不同格式的原因(使用)是,有不同的CPU架构,使用不同的方式在内存中表示整数值,这取决于访问它们的特定硬件设计的最有效方式。
这些架构差异并没有更糟/更好,这就是为什么它被称为endianess 这个造币的起源来自Johnatan Swift的小说“格列佛的旅行”,并且是Daniel Cohen的文章“关于和平的战争和和平的和平”中提到的第一个(?)。


“编译器会自行完成,如果int i = 10 in little endian,那么在big endian中它也是10作为输出”

好吧,正如你从上面的例子中看到的那样,这个答案是错误的。

字节顺序在以下情况下很重要:

  1. 您正在直接检查/操作多字节类型的字节
  2. 您正在序列化二进制数据,或在不同体系结构之间传输二进制数据

直接检查/操作多字节类型的字节

例如,假设您要拆分并显示32位IEEE浮点的二进制表示。 下面显示了float和big-little-endian体系结构中float的布局和相应字节的地址:

A        A+1      A+2      A+3        Big endian
-------- -------- -------- --------   s = sign bit
seeeeeee efffffff ffffffff ffffffff   e = exponent bit
-------- -------- -------- --------   f = fraction bit
A+3      A+2      A+1      A          Little Endian
-------- -------- -------- --------
A+1      A        A+3      A+2        "Middle" Endian (VAX)

符号位在float的最高有效字节(MSB)中。 在大端系统上,MSB在字节A ; 在小端系统上,它在字节A + 3中。 在旧的VAX F浮点数等奇数球上,它在中间位于A+1字节处。

因此,如果要屏蔽符号位,可以执行以下操作:

float val = some_value();
unsigned char *p = (unsigned char *) &val; // treat val as an array of unsigned char

// Assume big-endian to begin with
int idx = 0;

if ( little_endian() )
  idx = 3;

int sign = (p[idx] & 0x80) >> 7

序列化或传输二进制数据

再举一个例子,您希望保存二进制(非文本)数据,使其可以被大端或小端系统读取,或者您将二进制数据从一个系统传输到另一个系统。 Internet传输的约定是big-endian(MSB优先),所以在通过'net发送消息之前,你会使用像htonl (host-to-network long)和htons (host-to-network short)这样的调用在发送数据之前执行任何必要的字节交换:

uint32_t host_value = some_value();
uint32_t network_value = htonl( host_value ); 
send( sock, &network_value, sizeof network_value, 0 ); 

在像x86这样的小端系统上, htonl会将host_value的字节从0,1,2,3重新排序到3,2,1,0并将结果保存到network_value 在大端系统上, htonl基本上是一个无操作系统。 逆操作是ntohlntohs

如果您没有做上述任何事情,那么您通常不必担心字节序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM