英特尔处理器(也许还有其他处理器)使用小端格式存储。

我总是想知道为什么有人要以相反的顺序存储字节。这种格式比大端格式有什么优势吗?

评论

6502是早期(第一个?)流水线处理器。我似乎记得有人声称由于管道问题,它在某些与性能相关的问题上是低端的,但现在我不知道该问题可能是什么。有什么建议吗?

@ Steve314:我的答案解释了一点字节序对流水线CPU的性能有何帮助:programmers.stackexchange.com/q/95854/27874

小端,大端-您必须选择一个。就像在道路的左侧或右侧行驶。

我建议您用ASM编写一些代码,最好用于“老式”架构,例如6502或Z80。您将立即明白为什么它们使用小尾数法。使用大字节序的体系结构对其指令集具有某些特征,从而使其更可取。这不是一个任意决定!

每个字节顺序系统都有其优点。 Little-endian机器使您可以先读取最低字节,而无需读取其他字节。您可以很容易地检查一个数字是奇数还是偶数(最后一位为0),如果您喜欢这种方法,这很酷。大端系统将数据存储在内存中的方式与人类对数据的处理方式(从左到右)相同,这使底层调试更加容易。

#1 楼

无论哪种方式都有论点,但有一点是,在低端字节序系统中,内存中给定值的地址(即32位,16位或8位宽度)是相同的。

换句话说,如果内存中有两个字节的值:

0x00f0   16
0x00f1    0


将那个“ 16”作为一个16位值(大多数情况下为c“ short”) 32位系统)或作为8位值(通常为c'char')仅更改您使用的获取指令-而不更改您从中获取的地址。

在大端系统中,上面的布局为:

0x00f0    0
0x00f1   16


您需要增加指针,然后对新值执行更窄的获取操作。

因此,简而言之,“在小端序系统上,强制转换是不可操作的。”

评论


当然,假设您可以合理地忽略未读的高位字节(例如,您知道它们始终为零)。

–Steve314
11年7月24日在21:06

@ Steve314:如果我在C中将2的补码系统(绝大多数系统)从32位向下转换为16位(例如),则字节不必为零即可被忽略。不管它们的价值如何,我都可以忽略它们,并保持其与C标准和程序员的期望相符。

–user23679
11年7月24日在22:08

@Stritzinger-我们谈论的是由编译器生成的汇编/机器代码,它不能移植。可以编译的高级语言代码是可移植的-只是编译为在不同体系结构上的不同操作(就像所有操作一样)。

– Jimwise
2011年7月25日14:51



我不赞成这种观点,因为在大端体系结构中,指针可能指向所指对象的末尾而不是起点,因此您将拥有完全相同的优势。

– dan_waterworth
2011年7月26日在14:54

@dan_waterworth不太准确-例如,请记住C中的指针算术规则,以及递增或递减同一指针的强制转换会发生什么。您可以移动复杂性,但不能消除它。

– Jimwise
2011年7月26日在20:34

#2 楼


我总是想知道为什么有人会想以相反的顺序存储字节。


大端和小端仅是“正常顺序”和“反向顺序”从人类的角度出发,只有在所有这些都正确的情况下才可以...


您正在屏幕或纸上读取这些值。地址在左侧,较高的地址在右侧。
您正在用十六进制书写,左侧是高阶半字节,或者二进制,左侧是最高有效位。
您从左到右阅读。

这些都是人类惯例,对CPU根本不重要。如果您保留#1和#2并翻转#3,则小字尾对于阅读阿拉伯语或希伯来语(从右到左书写)的人来说似乎是“完全自然的”。

还有其他使大尾数看起来不自然的人类惯例,例如...


“较高”(最高有效)字节应位于“较高”内存地址。

回到我主要对68K和PowerPC进行编程时,我认为big-endian是“正确的”而little-endian是“错误的”。但是,由于我一直在做更多的ARM和Intel工作,所以我已经习惯了低位优先。真的没关系。

评论


实际上,数字是用阿拉伯语和希伯来语从[最高有效数字]到左[最低有效数字]来写的。

–Random832
11年7月24日在23:32

那么,为什么字节中的位以“大端”格式存储?为什么不一致?

– tskuzzy
2011年7月25日在12:42



它们不是-按照惯例,位0最低,而位7最高。而且,由于位不能单独寻址,因此通常不能对字节中的位进行排序。当然,它们在给定的通信协议或存储介质中可能具有物理顺序,但是除非您使用的是低级协议或硬件级别,否则您无需担心此顺序。

– Stewart
2011年7月25日在13:22



@Stewart:是的-按照惯例,位0是最右边的位,左边是7位。同样,所有C程序员都应关注此顺序。如果没有,那么编写跨平台不可移植的代码非常容易(sed的原始版本(我们必须为“ Unix中的Advanced C”类修复此问题)在所有非32位Little-endian)

– BlueRaja-Danny Pflughoeft
2011年7月25日在16:08



BlueRaja:仅根据书面约定。这与CPU体系结构没有共同之处。您可以将字节写为0-7 LSB-MSB而不是7-0 MSB-LSB,从算法的角度来看,没有任何变化。

– SF。
2011年7月26日在7:14

#3 楼

好的,这就是我向我解释的原因:加法和减法

加或减多字节数字时,必须从最低有效字节开始。例如,如果要添加两个16位数字,则可能从最低有效字节到最高有效字节有一个进位,因此必须从最低有效字节开始才能查看是否有进位。这是您进行长期加法时从最右边的数字开始的相同原因。您不能从左侧开始。

请考虑一个8位系统,该系统从内存中顺序提取字节。如果它首先获取最低有效字节,则可以在从内存中获取最高有效字节的同时开始进行加法运算。这种并行性就是为什么在诸如此类的系统上,在低端字节序中性能更好的原因。如果必须等到从内存中取出两个字节或以相反的顺序取出它们,这将花费更长的时间。在现代CPU上,我怀疑字节顺序是否会有所不同,并且仅出于历史原因而使用小尾数。

评论


嗯-这与我对大整数使用小尾数块排序的原因大致相同。我应该已经解决了。人们现在确实需要着手进行控制论-我的大脑已经迫切需要一些零件和一些根本的升级,我等不及了!

–Steve314
2011年7月25日在20:51

一个想法-6502在硬件上没有做太多的16位数学运算-毕竟是8位处理器。但是它确实使用相对于16位基址的8位有符号偏移来进行相对寻址。

–Steve314
2011年7月25日在20:55

注意,这个想法对于多精度整数算术仍然很重要(如Steve314所说),但是在字级上。现在,大多数操作都不受处理器字节序的直接影响:仍然可以像GMP一样,在大字节序系统上首先存储最低有效字。小尾数处理器对于少数操作(例如,某些字符串转换?)仍然具有优势,因为一次读取一个字节可能更容易完成,因为只有在小尾数系统中,此类数字的字节顺序才是正确的。

–vinc17
14年8月19日在9:29

如果内存带宽受到限制,小尾数处理器将具有优势,例如在某些具有16位内存总线的32位ARM处理器或具有8位数据总线的8088中:处理器可以只加载低半字节并执行在等待上半部分的同时添加/订阅/多...

–phuclv
18年3月23日在15:00

好吧,您可以只读取从最高地址到最低地址的值。

–西蒙·里希特(Simon Richter)
20-04-28在12:27

#4 楼

有了8位处理器,它肯定会更有效率,您可以执行8位或16位操作,而无需使用不同的代码,也不需要缓冲额外的值。每次一次字节。

评论


大尾数确实对人类来说更容易,因为它不需要重新排列字节。例如,在PC上,0x12345678存储为78 56 34 12,而在BE系统上,其存储为12 34 56 78(字节0在左侧,字节3在右侧)。请注意,数字越大(就位而言),它需要的交换就越多;一个单词需要交换一次;一个DWORD,两次通过(总共三次交换);一个QWORD三遍(共7遍),依此类推。也就是说,(位/ 8)-1交换。另一种选择是向前和向后读取它们(向前读取每个字节,但向后扫描整个#)。

– Synetech
2011年7月24日在20:12

一百一十三是中间字节序,或者是大字节序,其中“十三”本质上是一个非十进制数字。当我们拼出数字时,与用于数字的常量基数约定有一些细微的差异,但是一旦除去这些特殊情况,其余的就是大端数字-数以千计的前几千个,数以千计的前几百个等等。

–Steve314
2011年7月24日在20:58

@ Steve314,数字的拼写无关紧要,这是我们编程时使用的数字读数。马丁,没有计算机不必关心人类如何阅读数字,但是如果人类易于阅读,那么编程(或其他相关工作)将变得更加容易,并且可以减少或避免某些缺陷和错误。

– Synetech
11年7月24日在23:39

@ steve314在丹麦语中,“ 95”发音为“ fem halvfems”(5个,加上20分之四十)

–疫苗
2012年11月12日下午16:53

@Synetech:0x12345678存储为87 65 43 21,具体取决于选择写入的方式。一个人如何写一些相当随意的东西(读作:取决于上下文)。

–托马斯·埃丁
2014年1月26日10:37



#5 楼

没有人回答为什么这样做可能会产生很多后果。

考虑一个8位处理器,它可以在给定的时钟周期内从内存中加载单个字节。

现在,如果要将16位值载入(例如)您拥有的唯一16位寄存器(即程序计数器),那么一种简单的方法是:


从获取位置加载一个字节
将该字节向左移8个位置
将内存获取位置增加1
加载下一个字节(进入寄存器的低位部分)

结果:您只需增加获取位置,就只需加载到较宽寄存器的低位部分,并且只需要向左移动即可。 (当然,向右移动对其他操作很有帮助,因此这只是一个侧面显示。)

这样的结果是16位(双字节)的内容按顺序存储了..最小。即,较小的地址具有最高有效字节-大字节序。将下一个字节加载到暂存区中,对其进行移位,然后将其弹出到更宽的寄存器的顶部。或使用更复杂的门控安排以能够选择性地加载到顶部或底部字节中。

尝试使用小端字节序的结果是您需要更多的硅(开关和门),或者更多操作。

换句话说,就过去而言,要物有所值,就可以在大多数性能和最小的硅片面积上获得更多的收益。几乎无关紧要,但是管道填充之类的事情可能仍然有点大。 >
(而且大字节序处理器在字节顺序方面倾向于大字节序,而在字节数方面则很少。但是有些处理器很奇怪,会使用大字节序以及字节序。这使生活变得非常美好。对于硬件设计人员来说,添加内存映射的外围设备很有趣,但对程序员没有其他影响。)

#6 楼

日语日期约定为“ big endian”-yyyy / mm / dd。这对于排序算法非常方便,它可以使用简单的字符串比较与通常的“首个字符是最重要的”规则进行比较。

类似的情况适用于存储在重要领域优先记录。字段中字节的重要性顺序与记录中字段的重要性相匹配,因此您可以使用memcmp来比较记录,而不必关心比较两个长字,四个字还是八个单独的字节。 />
翻转字段的重要性顺序,您将获得相同的优势,但是对于小端数字而不是大端数字。

当然,这没有什么实际意义。 。无论您的平台是大端还是小端,如果您确实需要,都可以订购一个记录字段来利用此技巧。如果您需要编写可移植的代码,这真是一种痛苦。

我还可以提供经典呼吁的链接...

http://tools.ietf。 org / rfcmarkup?url = ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt

编辑

一个额外的想法。我曾经写了一个大的整数库(看是否可以),为此,不管平台如何排序这些位中的32位宽的块,都以低位字节序存储。原因是...


很多算法自然会在最低端开始工作,并希望这些端匹配。例如,此外,进位符号会增加到越来越多的有效数字,因此从最低有效位开始是有意义的。
增加或缩小值仅意味着在末尾添加/删除块-无需移位大块上/下。由于内存的重新分配,可能仍需要复制,但并不经常。

当然,这与处理器没有明显的关联-在CPU具有硬件大整数支持的情况下,这纯属库。

#7 楼

吉姆韦斯(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 :)

评论


这只是证明以CPU固有的任何格式工作更快/更轻松。它没有说什么更好。大尾数法也一样:for(i = 0; i <4; i ++){num + = data [i] <<(24-i * 8); }对应于在大端CPU上的move.l数据num。

– Martin Vilcans
2011年7月25日在22:34

@martin:我的书中减去减法会更好

– Cem Kalyoncu
2011年7月25日在23:40

其实并不重要,因为编译器还是会展开循环。无论如何,许多CPU都有字节交换指令来处理此问题。

– Martin Vilcans
2011年7月26日在9:04



我不同意大尾数法,我会做{num << = 8; num | = data [i];至少这不必使用mul计算左移计数

–HayriUğurKoltuk
2011年7月26日在13:20

@ali:您的代码将执行我编写的确切操作,并且在big endian上不起作用。

–Cem Kalyoncu
11年7月26日在19:14

#8 楼


我总是想知道为什么有人会想以相反的顺序存储字节。它也是用英语书写的方式。您从最高有效位开始,然后从最高有效位到最低有效位。例如

1234


是一千零二百三十四。

这是大尾数法有时也被称为自然顺序的方法。

在小尾数法中,此数将是一,二十,三十四万四千。
但是,当您执行加法或减法之类的算术时,您需要从结尾开始。

  1234
+ 0567
  ====


您从4和7开始,写出最低位,然后记住进位。然后加3和6等。对于加,减或比较,如果数字已经取反,如果您已经具有按顺序读取内存的逻辑,则实现起来更简单。

要以这种方式支持big endian,您需要逻辑来反向读取存储器,或者您具有仅在寄存器上运行的RISC进程。 ;)

许多Intel x86 / Amd x64设计都是历史悠久的。

#9 楼

大尾数法对于某些操作很有用(想到的八位字节长相等的“ bignums”比较)。小尾数表示其他形式(可能添加两个“ bignums”)。最后,这取决于设置的CPU硬件,通常是一个或另一个(某些MIPS芯片是IIRC,可以在启动时切换为LE或BE)。

#10 楼

如果只涉及具有可变长度的存储和传输,但不涉及具有多个值的算术运算,则LE通常更易于编写,而BE则更易于读取。 (和后面)作为一个具体示例。所有这些都可以在具有简单结束条件的简单循环中完成。通常,您需要另一个除数,该除数对特定数字(此处为100)具有10的最大幂。当然,您首先需要找到它。

将字符串转换为int时,如果将其作为反向写入操作完成,则更容易在BE中完成。写存储最后一个最高有效位,因此应该首先读取它。

同样,您还需要一个附加因数,即从1开始并为每个数字乘以10。

因此,我通常更喜欢使用BE进行存储,因为一个值只被写入一次,但读取时为至少一次甚至多次。为了简化结构,我通常还会选择将路径转换为LE,然后反转结果,即使它第二次写入该值也是如此。以及更多。

#11 楼

1)主要原因是强制转换操作本身对硬件不可见;重铸指针不需要更改指针地址的算术运算,并且不依赖编译器知道先前的类型就知道可以将指针偏移多少。为了克服这个问题,指针总是可以指向内存中数据的结尾,但这比使用little endian解决问题更不直观

2)假设有人存储了一个双字00 00 00 01,格式为大尾数格式,为0x100。您需要知道该地址处的数据格式,才能读取所需的值。如果使用little endian,您只能从0x100读取一个字节,如果使用little endian,则需要为1,您需要知道一个dword存储在此处并从0x103读取。这比起endian少,抽象了内存布局的细节,因为程序员需要了解数据的表示形式。

3)在硬件级别,它可以简化存储到负载转发,因为如果您将一个双字写入0x100,然后读取一个字节,则它将在小字节序而不是0x103上读取相同的地址。这意味着存储到存储转发到存储缓冲区中的一个字节需要0个检查。当您将字节加载到0x103时,您必须检查存储缓冲区中的其他地址及其数据长度。提取最高字节是错误的,因为在big endian上,它可能只先提取最高字节。