彻底弄懂计算机中的大端小端
大端與小端這個問題在做和其他設備交換原始字節數據的時候是非常重要的概念,也是必須要掌握的內容,但是很多人就是僅僅是稍微有些了解,但每次真正去做東西的時候,還是要花半天去想,博主就是這樣的人,出現這樣問題的真正原因是還沒有完全弄清楚大端小端。今天就讓我們一起徹底的弄懂這兩個東西吧!
先講講關于這兩個東西的傳說吧(也是抄來的^_^)
“大端”和“小端”可以追溯到1726年的Jonathan Swift的《格列佛游記》,其中一篇講到有兩個國家因為吃雞蛋究竟是先打破較大的一端還是先打破較小的一端而爭執不休,甚至爆發了戰爭。1981年10月,Danny Cohen的文章《論圣戰以及對和平的祈禱》(On holy wars and a plea for peace)將這一對詞語引入了計算機界。這么看來,所謂大端和小端,也就是big-endian和little-endian,其實是從描述雞蛋的部位而引申到計算機地址的描述,也可以說,是從一個俚語衍化來的計算機術語。稍有些英語常識的人都會知道,如果單靠字面意思來理解俚語,那是很難猜到它的正確含義的。在計算機里,對于地址的描述,很少用“大”和“小”來形容;對應地,用的更多的是“高”和“低”;很不幸地,這對術語直接按字面翻譯過來就成了“大端”和“小端”,讓人產生迷惑也不是很奇怪的事了。
我希望這篇文章的讀者起碼要知道大端小端是干什么的,不然看了效果也不會很好
一個句話解釋什么是大端,什么是小端
在用英文屬于解釋就是,most significant (最高有效位)在低地址位就是大端,(least significat) 在高地址位就是小端,看完這句話,你是不是清楚了什么是大端,什么是小端了呢? 正常人的反應會是,哎呀,我操,這都是些啥啊?不但沒有更清楚,反而更暈了,我開始看到這樣的解釋也是這樣的反應。
再來一個問題,徹底搞暈你(放心,我會把你搞清醒的^__^)
示例用C來描述
例1 :short num1 = 0x12FF; 例2 :char * str1 = "abcde"; 例3 :數字32的 short 十六進制: 00 20請問上面的代碼中 例1 是大端還是小端 , 例2 是大端還是小端,例3 是大端還是小端 ,我希望到這里我已經成功的將你?搞暈?了。
根本原則,大端小端是針對于存儲而言的,和字面的表達方式沒關系
? ? 所以之前我提的問題全是些偽命題,而計算機中大端小端真正的含義要在存儲中講才能講明白。
????我們先來看例1 ,我們都知道在C中short 占用兩個字節,我們這里給這兩個字節賦值為0x12 和0xFF,在計算機中內存是一個個的字節單元。
????好了問題又來了,這兩個字節在內存中是如果放的呢,假如 num1的 起始地址是 0x0056A100, 那么存放num1內容的應該是 0x0056A100和0x0056A101,但是哪個是0x12 ,哪個是0xFF呢? 我們寫一段代碼來看一下
int main(int argc, _TCHAR* argv[]){unsigned short num1 = 0x12FF;char * address = (char *)&num1;printf("low bytes is %x , high bytes is %x",*address & 0xFF, *(address + 1) & 0xFF ); }程序很簡單,就是分別打印兩個字節的內容,低字節是address,高字節是address+1 , 我們來看一下 運行結果
可以看到低位地址是0xFF高位是0x12,num1 這個數的高位12 存在了內存的低地址處,低位FF存在高地址處, 所以我們的機器 是小端的機器,其實只要確定內存的低地址存放的是高位還是低位就行了,這里低地址是0xFF(低位),所以是小端。代表我們的機器是小端機器,嚴格的說是CPU處理數據的方式采用的是小端法。
字符串中又是怎樣的情況呢?
char * a = “abcd”;
其實在程序中字符串并沒有大端小端并之說,只有在涉及到數據的傳輸時,字符串的大端小端才值的注意,我們后面會探討這一問題,但很多教材或者是博客都直接把整型和字符串的大小端放在一起講,就容易更讓人搞不清楚。這里的d肯定是存放在高地址,a肯定是存放在低地址。
大端小端和字符串沒有任何關系
網絡中的大端與小端
可能上面的內容你已經搞清楚了,但是當你看一些關于網絡或者的資料時發現又有什么網絡字節序神馬的,然后又糊涂了,讓我們一起來破除關于網絡字節序列這些神馬的浮云。
-了解網絡是怎樣發送數據的?
我們使用的網絡協議很多都是基于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);接收和發送的函數方法參數是一樣的
recv(Socket soc, char * buf, len , 0);
好,這個數目發完了,B接收的時候用下面的代碼
好了,數據發送和接受都完成了,到這里為止,和大小端半毛錢關系都沒有,接下來就有了。這B想看看到底欠了A多少錢了,用下面的代碼接收。
int total = ((ownMoney[0] << 8) | (ownMoney[1] & 0xFF ) &0xFF );一看嚇了一跳 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 ) );一看,額,1500,心里松了一口氣,這就對了。我們來看看出現剛剛這個問題的原因。
A是 Intel CPU (小端機器), 0x05DC這個數,低地址存放的是DC,高地址是05, B接受了放在 字符數組中, 因為B也是小端機器,B還原數據的時候是在低地址放在了高位,也就是0xDC,這是 不對的。
那總不能每次發送數據都問一下別人是什么CPU吧?
于是人們就約定將數據的低地址處放數據的高位(也叫大端法),于是A就遵循了這個約定, 發送之前將數據位置改了一下,因為之前低地址放的是低位。之前A的數據是這樣放的
| DC | 05 |
因為約定采用大端法發送數據,所以要改成下面這樣
| 05 | DC |
如果遵守約定,B就不用問A你的機器是什么類型了,但是還有一個問題,因為B的機器類型可能是大端也可能是小端,那接受數據的時候還要去判斷機器類型,這也太麻煩了,對,沒錯,底層的網絡處理就是這么麻煩。
讓世界更美好
這個世界變得越來越美好,是因為有前人無私的付出,計算機的世界也一樣,在大小端數據轉換中也有實現好了的函數,只等著你去調用。
htons(unsigned short n):將short轉為網絡字節序 htonl(unsigned long n) : 將long轉為網絡字節序htons 意思就是host to network ,后面一個代表數據類型還有從網絡字節序轉成本機字節序的方法
ntohl ntohs?
總結
以上是生活随笔為你收集整理的彻底弄懂计算机中的大端小端的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: paros web中间件攻击、扫描、监控
- 下一篇: golang的panic用法