哈喽,你们好,我是asong
。今天想与你们聊一聊计算机硬件中的两种储存数据的方式:大端字节序(big endian)、小端字节序(little endian)。老实说,我第一次知道这个概念仍是在学习单片机的时候,不过当时学完就忘了,真正长记性是在面试的时候,面试官问我:你能用C
语言写段代码判断机器的字节序吗?你必定好奇为何要用C
语言写,傻瓜,这是我大学的时候面试嵌入式岗位呀。扯远啦,其实当时的我是懵逼的,早就忘了什么大端、小端了,因此遗憾的错过嵌入式行业,进入了互联网行业(手动狗头)。本文的全部代码已经上传
github
:https://github.com/asong2020/...;欢迎star
git因为微信公众号改版,文章推送会乱序,为了第一时间收到
asong
的消息,请读者朋友动动小手,在公众号主页右上角设置里加个星标。感谢你们~。github
我一直都不理解,为何要有大小端区分,尤为是小端,老是会忘记,由于他不符合人类的思惟习惯,但存在即为合理,存在就有他存在的价值。这里有一个比较合理的解释:计算机中电路优先处理低位字节,效率比较高,由于计算机都是从低位开始的,因此计算机内部处理都是小端字节序。可是咱们日常读写数值的方法,习惯用大端字节序,因此除了计算机的内部,其余场景大都是大端字节序,好比:网络传输和文件储存时都是用的大端字节序。面试
因此大小端问题极可能与硬件或者软件的创造者们有关,实际在计算机工业应用上,不一样的操做系统和不一样的芯片类型都有所不一样。不一样的系统设计不一样,因此咱们也不必深究为何要有这个区分,只须要知道他们的原理就行了。算法
大端模式:高位字节排放在内存的低地址端,低位字节排放在内存的高地址端;编程
小端模式:低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;设计模式
这么说也有点模糊,仍是配个图来看更清晰:缓存
咱们来看一看数值0x1A2B3C4D
在大端与小端的表现形式,这里咱们假设地址是从0x4000
开始:微信
上图所示:大端和小端的字节序最小单位是1字节(8bit),大端字节序就和咱们平时的写法顺序同样,从低地址到高地址写入0x1A2B3C4D
,而小端字节序就是咱们平时的写法反过来,由于字节序最小单位为1
字节,因此从低地址到高地址写入0x4D3C2B1A
。网络
由于大端、小端很容易混记,因此分享一个我本身记忆的小技巧:架构
大端:高低高低,也就是高位字节排放在内存低地址端,高地址端存在低位字节;
小端:高高低低;也就是高位字节排放在内存的高地址端,低位字节排放在内存的低地址端;
Go
区分大小端计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节。它只知道按顺序读取字节,先读取第一个字节,再读取第二个字节,因此说我就能够根据这个特性来读判断大小端。
在使用Go
语言实现以前,仍是想再用C
语言实现一遍,由于这是我一辈子的痛,毕竟在面试的时候没写出来。
能够利用C
语言中union
各字段共享内存的特性,union型数据所占的空间等于其最大的成员所占的空间,对 union 型的成员的存取都是相对于该联合体基地址的偏移量为 0 处开始,也就是联合体的访问不论对哪一个变量的存取都是从 union 的首地址位置开始,联合是一个在同一个存储空间里存储不一样类型数据的数据类型。这些存储区的地址都是同样的,联合里不一样存储区的内存是重叠的,修改了任何一个其余的会受影响。因此咱们可写出代码以下:
#include "stdio.h" // big_endian: 1 // little_endian: 2 int IsLittleEndian() { union { short value; char array[2]; } u; u.value = 0x0102; if (u.array[0] == 1 && u.array[1] == 2){ return 1; }else if (u.array[0] == 2 && u.array[1] == 1){ return 2; } return -1; } int main() { int res; res = IsLittleEndian(); printf("result is %d\n",res); if (res == 1) { printf("it is big endian"); } if (res == 2){ printf("it is little endian"); } return 0; } // 运行结果(不一样系统运行结果会有不一样) result is 2 it is little endian%
如今咱们来思考一下,怎么用Go
语言验证大小端,Go
中是没有union
这个关键字,那就要另辟蹊径,换一个方法来实现啦,咱们能够经过将int32
类型(4字节)强制转换成byte
类型(单字节),判断起始存储位置内容来实现,由于Go
不支持强制类型转换,咱们能够借助unsafe
包达到咱们的要求,写出代码以下:
package main import ( "fmt" "unsafe" ) func IsLittleEndian() bool{ var value int32 = 1 // 占4byte 转换成16进制 0x00 00 00 01 // 大端(16进制):00 00 00 01 // 小端(16进制):01 00 00 00 pointer := unsafe.Pointer(&value) pb := (*byte)(pointer) if *pb != 1{ return false } return true } func main() { fmt.Println(IsLittleEndian()) } // 运行结果:ture
这里你们可能会有疑惑,为何要有大小端转化,这是由于在涉及到网络传输、文件存储时,由于不一样系统的大小端字节序不一样,这是就须要大小端转化,才能保证读取到的数据是正确的。我在大学时作arm
和dsp
通讯的时候,就遇到个大小端转换的问题,由于arm
是小端,dsp
是大端,因此在不了解这个知识点的时候,通讯的数据就是乱的,致使我调试了很久。
大小端的转换其实还算比较简单,经过位操做就能够实现,这里咱们用uint32
类型做为例子:
func SwapEndianUin32(val uint32) uint32{ return (val & 0xff000000) >> 24 | (val & 0x00ff0000) >> 8 | (val & 0x0000ff00) << 8 | (val & 0x000000ff) <<24 }
是的,你没看错,就是这么简单,这里也很简单,就不细讲了。
其实go
官方库encoding/binary
中已经提供了大小端使用的库,咱们要想进行大小端转换,彻底可使用官方库,不必本身造轮子。咱们看一下这个库怎么使用:
// use encoding/binary // bigEndian littleEndian func BigEndianAndLittleEndianByLibrary() { var value uint32 = 10 by := make([]byte,4) binary.BigEndian.PutUint32(by,value) fmt.Println("转换成大端后 ",by) fmt.Println("使用大端字节序输出结果:",binary.BigEndian.Uint32(by)) little := binary.LittleEndian.Uint32(by) fmt.Println("大端字节序使用小端输出结果:",little) } // 结果: 转换成大端后 [0 0 0 10] 使用大端字节序输出结果: 10 大端字节序使用小端输出结果: 167772160
grpc
中对大端的应用你们对gRPC
必定很熟悉,最近在看gRPC
源码时,看到gRPC
封装message
时,在封装header
时,特地指定了使用大端字节序,源码以下:
// msgHeader returns a 5-byte header for the message being transmitted and the // payload, which is compData if non-nil or data otherwise. func msgHeader(data, compData []byte) (hdr []byte, payload []byte) { hdr = make([]byte, headerLen) if compData != nil { hdr[0] = byte(compressionMade) data = compData } else { hdr[0] = byte(compressionNone) } // Write length of payload into buf binary.BigEndian.PutUint32(hdr[payloadLen:], uint32(len(data))) return hdr, data }
在本文的最后咱们再来作一下总结:
encoding/binary
;本文的全部代码已经上传github
:https://github.com/asong2020/...;欢迎star
好啦,这篇文章就到这里啦,素质三连(分享、点赞、在看)都是笔者持续创做更多优质内容的动力!
建立了一个Golang学习交流群,欢迎各位大佬们踊跃入群,咱们一块儿学习交流。入群方式:加我vx拉你入群,或者公众号获取入群二维码
结尾给你们发一个小福利吧,最近我在看[微服务架构设计模式]这一本书,讲的很好,本身也收集了一本PDF,有须要的小伙能够到自行下载。获取方式:关注公众号:[Golang梦工厂],后台回复:[微服务],便可获取。
我翻译了一份GIN中文文档,会按期进行维护,有须要的小伙伴后台回复[gin]便可下载。
翻译了一份Machinery中文文档,会按期进行维护,有须要的小伙伴们后台回复[machinery]便可获取。
我是asong,一名普普统统的程序猿,让咱们一块儿慢慢变强吧。欢迎各位的关注,咱们下期见~~~
推荐往期文章: