英特尔处理器(也许还有其他处理器)使用小端格式存储。
我总是想知道为什么有人要以相反的顺序存储字节。这种格式比大端格式有什么优势吗?
英特尔处理器(也许还有其他处理器)使用小端格式存储。
我总是想知道为什么有人要以相反的顺序存储字节。这种格式比大端格式有什么优势吗?
Answers:
无论哪种方式都有论点,但有一点是,在低端字节序系统中,内存中给定值的地址(即32位,16位或8位宽度)是相同的。
换句话说,如果您的内存中有两个字节的值:
0x00f0 16
0x00f1 0
将“ 16”作为16位值(在大多数32位系统上为c“ short”)或作为8位值(通常为c“ char”)只会更改您使用的获取指令-不会更改您获取的地址从。
在大端系统上,上述内容布置为:
0x00f0 0
0x00f1 16
您将需要增加指针,然后对新值执行更窄的获取操作。
因此,简而言之,“在小端序系统上,强制转换是不可操作的”。
我总是想知道为什么有人要以相反的顺序存储字节。
从人的角度来看,大端和小端仅是“正常顺序”和“反向顺序”,只有在所有这些都是真实的情况下才可以...
这些都是与CPU无关的人类惯例。如果要保留#1和#2并翻转#3,则小字尾对于阅读阿拉伯语或希伯来语(从右到左书写)的人来说似乎“完全自然”。
还有其他使大尾数看起来不自然的人类惯例,例如...
回到我主要对68K和PowerPC进行编程时,我认为big-endian是“正确的”,little-endian是“错误的”。但是,由于我一直在做更多的ARM和Intel工作,所以我已经习惯了低位优先。真的没关系。
好的,这就是我向我解释的原因:加法和减法
加或减多字节数字时,必须从最低有效字节开始。例如,如果要添加两个16位数字,则可能从最低有效字节到最高有效字节有一个进位,因此必须从最低有效字节开始才能查看是否有进位。这与您进行长加法时从最右边的数字开始的原因相同。您不能从左边开始。
考虑一个8位系统,该系统从内存中顺序提取字节。如果取最低显著字节第一,它可以开始做加法,而最显著字节从存储器中取出。这种并行性是为什么在诸如此类的系统上使用低端字节顺序时性能会更好的原因。如果必须等到从内存中取出两个字节,或者以相反的顺序取出它们,则将需要更长的时间。
这是在旧的8位系统上。在现代CPU上,我怀疑字节顺序是否会有所不同,并且仅出于历史原因才使用Little Endian。
使用8位处理器,无疑可以提高效率,您可以执行8位或16位操作,而无需使用不同的代码,也不需要缓冲额外的值。
如果您一次处理一个字节,则对于某些加法运算仍然更好。
但是没有理由大尾数会更自然-在英语中,您使用十三(小尾数)和二十三个(大尾数)
0x12345678
上存储为,78 56 34 12
而在BE系统上存储为12 34 56 78
(字节0在左侧,字节3在右侧)。请注意,数字越大(按位计),它需要的交换就越多。一个单词需要交换一次;一个DWORD,两次通过(总共三次交换);一个QWORD三遍(共7遍),依此类推。即(bits/8)-1
交换。另一种选择是向前和向后读取它们(向前读取每个字节,但向后扫描整个#)。
日语日期约定为“ big endian”-yyyy / mm / dd。这对于排序算法非常方便,该算法可以使用简单的字符串比较与通常的“最重要的字符”规则。
类似的情况适用于存储在最重要的字段优先记录中的大端数字。字段中字节的重要性顺序与记录中字段的重要性相匹配,因此您可以使用a memcmp
比较记录,而不必关心比较两个长字,四个字还是八个单独的字节。
翻转字段的重要性顺序,您将获得相同的优势,但是对于小端数字而不是大端数字。
当然,这没有什么实际意义。无论您的平台是大端还是小端,如果您确实需要,都可以订购记录字段来利用此技巧。如果您需要编写可移植的代码,这只是一种痛苦。
我还可能会提供经典呼吁的链接...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
编辑
一个额外的想法。我曾经写了一个大的整数库(看是否可以),为此,不管平台如何排序这些位中的32位宽的块,都以低位字节序存储。原因是...
许多算法自然会在最低端开始工作,并希望匹配这些端。例如,此外,进位符号会增加到越来越多的有效数字,因此从最低有效位开始是有意义的。
增大或减小值仅表示在末尾添加/删除块-无需向上/向下移动块。由于内存重新分配,可能仍需要复制,但并非经常如此。
当然,这与处理器没有明显的相关性-直到CPU由硬件大整数支持制成为止,这纯粹是库的事情。
没有人回答为什么这样做可能会产生后果。
考虑一个8位处理器,它可以在给定的时钟周期内从内存中加载单个字节。
现在,如果您想将一个16位值加载到(例如)您拥有的一个唯一的16位寄存器(即程序计数器)中,那么一种简单的方法是:
结果:您只需要增加获取位置,就可以加载到更宽的寄存器的低位部分,并且只需要向左移动即可。(当然,向右移动对其他操作很有帮助,所以这有点像是侧面表演。)
这样的结果是16位(双字节)的内容按Most..Least的顺序存储。即,较小的地址具有最高有效字节-大字节序。
如果您改为尝试使用Little Endian进行加载,则需要将一个字节加载到宽寄存器的下部,然后将下一个字节加载到暂存区,将其移位,然后将其弹出到较宽寄存器的顶部。或者使用门的更复杂的安排,以便能够有选择地加载到顶部或底部字节中。
尝试使用低字节序的结果是您需要更多的硅(开关和门),或者需要更多的操作。
换句话说,就过去获得巨大回报而言,您可以在最大的性能和最小的硅片面积上获得更多的回报。
这些天来,这些考虑和几乎无关紧要,但是管道填充之类的事情可能仍然有些大问题。
说到写软件,使用小字节序寻址通常会使生活更轻松。
(并且大字节序处理器在字节顺序方面倾向于大字节序,而在字节字节方面则很少。但是有些处理器很奇怪,并且将使用大字节序位排序以及字节序。这使生活变得非常美好。对于硬件设计人员来说,添加内存映射的外围设备很有趣,但对程序员没有其他影响。)
吉姆韦斯(Jimwise)提出了一个好观点。还有另一个问题,在小尾数法中,您可以执行以下操作:
byte data[4];
int num=0;
for(i=0;i<4;i++)
num += data[i]<<i*8;
OR
num = *(int*)&data; //is interpreted as
mov dword data, num ;or something similar it has been some time
对于不受内存交换位置的明显缺点影响的程序员而言,更为直接。我个人发现big endian与自然现象相反:)。12应该存储并写为21 :)
for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }
对应move.l data, num
于大字节序CPU。
我总是想知道为什么有人会想以相反的顺序存储字节
十进制数写为大端。这也是用英语书写的方式。您从最高有效位开始,然后从最高有效位到最低有效位。例如
1234
是一千零二十四。
大尾数法有时被称为自然顺序。
用小尾数法,该数字将是一,二十,三十四和四千。
但是,当您执行加法或减法之类的算术运算时,您将从结尾开始。
1234
+ 0567
====
您从4和7开始,写下最低的数字并记住进位。然后加3和6等。对于加,减或比较,如果数字已经取反,如果您已经具有按顺序读取存储器的逻辑,则实现起来更简单。
为了以这种方式支持大字节序,您需要逻辑来反向读取存储器,或者您具有仅在寄存器上运行的RISC进程。;)
许多Intel x86 / Amd x64设计都是历史悠久的。
如果只涉及可变长度的存储和传输,而没有涉及多个值的算术运算,则LE通常更易于编写,而BE则更易于阅读。
让我们以int到字符串的转换为例。
int val_int = 841;
char val_str[] = "841";
将int转换为字符串时,最低有效位比最高有效位更容易提取。所有这些都可以在具有简单结束条件的简单循环中完成。
val_int = 841;
// Make sure that val_str is large enough.
i = 0;
do // Write at least one digit to care for val_int == 0
{
// Constants, can be optimized by compiler.
val_str[i] = '0' + val_int % 10;
val_int /= 10;
i++;
}
while (val_int != 0);
val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it
现在按BE顺序尝试相同的操作。通常,您需要另一个除数,该除数对特定数字(此处为100)具有10的最大幂。当然,您首先需要找到它。还有更多要做的事情。
将字符串转换为int作为反向写入操作时,在BE中更容易实现。写操作最后存储最高有效位,因此应先读取。
val_int = 0;
length = strlen(val_str);
for (i = 0; i < length; i++)
{
// Again a simple constant that can be optimized.
val_int = 10*val_int + (val_str[i] - '0');
}
现在按LE顺序执行相同的操作。同样,您需要一个附加因子,以1开头,然后将每个数字乘以10。
因此,我通常更喜欢使用BE进行存储,因为一个值只被写入一次,但至少被读取一次,也许被读取多次。对于其更简单的结构,即使它第二次写入该值,我通常也将其转换为LE,然后反转结果。
BE存储的另一个示例是UTF-8编码,等等。