今天我们谈谈计算机中的大端小端以及网络中的大端小端。参考了网上好多资料以后,从众多资料中我也按照自己的理解方式,总结一下。
学习一个新东西:我们依然是先了解一下大端小端的历史:
讲故事 这个就需要求救一下度娘,以下故事来自度娘,真假我也不知道,权当听故事:
“大端”和“小端”可以追溯到1726年的Jonathan Swift的《格列佛游记》,其中一篇讲到有两个国家因为吃鸡蛋究竟是先打破较大的一端还是先打破较小的一端而争执不休,甚至爆发了战争。1981年10月,Danny Cohen的文章《论圣战以及对和平的祈祷》(On holy wars and a plea for peace)将这一对词语引入了计算机界。这么看来,所谓大端和小端,也就是big-endian和little-endian,其实是从描述鸡蛋的部位而引申到计算机地址的描述,也可以说,是从一个俚语衍化来的计算机术语。稍有些英语常识的人都会知道,如果单靠字面意思来理解俚语,那是很难猜到它的正确含义的。在计算机里,对于地址的描述,很少用“大”和“小”来形容;对应地,用的更多的是“高”和“低”;很不幸地,这对术语直接按字面翻译过来就成了“大端”和“小端”,让人产生迷惑也不是很奇怪的事了。
听完了故事,这下让我们来看什么是大端小端:依然沿袭过去用图讲解,来理解结构:
现实生活中我们写的数据时左边位权高,右边的位权低,比如123,1表示的就是100,对吧,因为计算机只认识0和1,这里,我把它换算成2进制形式:上图是1的二进制形式。这下就要引出今天我们的主角大端小端,most significant (最高有效位)在低地址位就是大端,(least significat) 在高地址位就是小端,现在我们大多数的计算机都是采用八位一个字节,所以当如果把1按照第一个存储的话就是大端,第二个就是小端。上边用红色标记起来的就是理解的重点。
还有就是关于字符串是怎么区分的,比如说:char *ch ="12345",对于字符串,是没有什么大端小端的,5一定是在高地址存放的。
网络中的大端与小端
可能上面的内容你已经搞清楚了,但是当你看一些关于网络或者的资料时发现又有什么网络字节序神马的,然后又糊涂了,让我们一起来破除关于网络字节序列这些神马的浮云。
了解网络是怎样发送数据的?
我们使用的网络协议很多都是基于socket的,所以我们基于socket来讲,在socket规范中发送数据是 这个方法
send(Socket soc, char * buf, len , 0);
第一个参数是对方的socket,也就是地址
第二个参数是 一个字符指针,也就是要发送的内容存放的地址
第三个是发送数据的长度
第四个是和选择协议相关的,一般设为0
所以这里我们看到在底层发送数据的方法里面根本没有和什么大端小端有关系的东西,发送的函数只关心你要给老子发送的东西在哪儿,发多少,其他一概不管,好,现在用一个场景说明一下。程序员A把一个金额发给程序员B,这个金额是B欠A钱的金额。
//A发给B,这是B欠我A的数目 ,是1500元,下面是十六进制写法short a = 0x05DC;//于是A就send(sock, (char *)&a,2,0);12341234
接收和发送的函数方法参数是一样的
recv(Socket soc, char * buf, len , 0);
好,这个数目发完了,B接收的时候用下面的代码
char ownMoney [2];recv(Socket soc,ownMoney, 2 , 0);1212
好了,数据发送和接受都完成了,到这里为止,和大小端半毛钱关系都没有,接下来就有了。这B想看看到底欠了A多少钱了,用下面的代码接收。
int total = ((ownMoney[0] << 8) | (ownMoney[1] & 0xFF ) &0xFF );11
一看吓了一跳 56325,这就出大问题了,B心想,A是我铁哥们儿,肯定不会骗我,肯定是那个地方数据出错了,于是有了下面的对话
B:兄弟你电脑CPU是什么的?
A:是intel的啊
B:发送数据的时候有做什么处理吗?
A:没有做任何处理
B:额,我明白了
刚好B的机器也是Intel的CPU,B找到原因之后背了一遍我编写的口诀,低地址是低位是小端,低低地址是高位为大端。
B分析了一下数据的发送流程,A发送两个字节的short型数据,因为A是小端所以,先发送过来的是低位数据,后发送的是高位,我先接收的也是低位,后接收的是高位。
B修改程序后
int total = (ownMoney[0] &0xFF ) | ( (ownMoney[1] << 8) & 0xFF ) );11
一看,额,1500,心里松了一口气,这就对了。我们来看看出现刚刚这个问题的原因。
A是 Intel CPU (小端机器), 0x05DC这个数,低地址存放的是DC,高地址是05, B接受了放在 字符数组中, 因为B也是小端机器,B还原数据的时候是在低地址放在了高位,也就是0xDC,这是 不对的。
那总不能每次发送数据都问一下别人是什么CPU吧?
于是人们就约定将数据的低地址处放数据的高位(也叫大端法),于是A就遵循了这个约定, 发送之前将数据位置改了一下,因为之前低地址放的是低位。之前A的数据是这样放的
低地址 | 高地址 |
---|---|
DC | 05 |
因为约定采用大端法发送数据,所以要改成下面这样
低地址 | 高地址 |
---|---|
05 | DC |
//这样改变位置的代码short a = 0x05DC;//用一个字符指针指向a char* pointerOfA = (char *)&a;//把数据的低位值保存在一个变量里,也就是 DCchar temp = *(pointerOfA );//把高位的值放在低位 *pointerOfA = *(pointerOfA +1);*(pointerOfA +1) = temp;//再发送send(sock, pointerOfA ,2,0);1234567891011121312345678910111213
如果遵守约定,B就不用问A你的机器是什么类型了,但是还有一个问题,因为B的机器类型可能是大端也可能是小端,那接受数据的时候还要去判断机器类型,这也太麻烦了,对,没错,底层的网络处理就是这么麻烦。
以上总结是本人参考了网上的资料后自己整理发布的,如果原博主有问题,可以联系本人。