c語言大小端轉化
補:x86機是小端(修改分區表時要注意),單片機一般爲大端
今天碰一個關於字節順序的問題,雖然看起來很簡單,但一直都沒怎麼完全明白這個東西,索性就找了下資料,把它弄清楚. 因爲現行的計算機都是以八位一個字節爲存儲單位,那麼一個16位的整數,也就是C語言中的short,在內存中可能有兩種存儲順序big-endian和litte-endian.考慮一個short整數0x3132(0x32是低位,0x31是高位),把它賦值給一個short變量,那麼它在內存中的存儲可能有如下兩種情況:
大端字節(Big-endian):
----------------->>>>>>>>內存地址增大方向
short變量地址
0x1000 0x1001
_____________________________
| | | 0x31 | 0x32 |_______________|________________ 高位字節在低位字節的前面,也就是高位在內存地址低的一端.可以這樣記住(大端->高位->在前->正常的邏輯順序)
小端字節(little-endian):
----------------->>>>>>>>內存地址增大方向
short變量地址
0x1000 0x1001
_____________________________
| | | 0x32 | 0x31 |________________|________________ 低位字節在高位字節的前面,也就是低位在內存地址低的一端.可以這樣記住(小端->低位->在前->與正常邏輯順序相反) 可以做個實驗
在windows上下如下程序
#include<stdio.h>
#include<assert.h>
void main( void )
{
shorttest; FILE*fp; test =0x3132; //(31ASIIC碼的’1’,32ASIIC碼的’2’) if ((fp =fopen ("c:\\test.txt", "wb")) == NULL)
assert(0);
fwrite(&test,sizeof(short), 1, fp); fclose(fp); }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 在LINUX下程序 #include <stdio.h> int main() { int a = 0x12345678; char *b = (char *)&a; printf("a = %x b = %x\n ",a,b[0]); return 0; } 如果輸出的結果是a = 0x12345678 b = 0x78 則說明你的機器是小端存放 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 然後在C盤下打開test.txt文件,可以看見內容是21,而test等於0x3132,可以明顯的看出來x86的字節順序是低位在前.如果我們把這段同樣的代碼放到(big-endian)的機器上執行,那麼打出來的文件就是12.這在本機中使用是沒有問題的.但當你把這個文件從一個big-endian機器複製到一個little-endian機器上時就出現問題了.
如上述例子,我們在big-endian的機器上創建了這個test文件,把其複製到little-endian的機器上再用fread讀到一個short裏面,我們得到的就不再是0x3132而是0x3231了,這樣讀到的數據就是錯誤的,所以在兩個字節順序不一樣的機器上傳輸數據時需要特別小心字節順序,理解了字節順序在可以幫助我們寫出移植行更高的代碼.
正因爲有字節順序的差別,所以在網絡傳輸的時候定義了所有字節順序相關的數據都使用big-endian,BSD的代碼中定義了四個宏來處理:
#definentohs(n) //網絡字節順序到主機字節順序n代表net, h代表host, s代表short
#definehtons(n) //主機字節順序到網絡字節順序n代表net, h代表host, s代表short
#definentohl(n) //網絡字節順序到主機字節順序n代表net, h代表host, l代表 long
#definehtonl(n) //主機字節順序到網絡字節順序n代表net, h代表host, l代表 long
舉例說明下這其中一個宏的實現:
#define sw16(x) \ ((short)(\ (((short)(x)&(short)0x00ffU)<< 8) |\ (((short)(x)&(short)0xff00U)>> 8))) 這裏實現的是一個交換兩個字節順序.其他幾個宏類似.
我們改寫一下上面的程序 #include<stdio.h>
#include<assert.h>
#define sw16(x) \ ((short)(\
(((short)(x)& (short)0x00ffU)<<8) | \ (((short)(x)& (short)0xff00U)>>8) )) // 因爲x86下面是低位在前,需要交換一下變成網絡字節順序 #define htons(x) sw16(x)
void main( void )
{
shorttest; FILE*fp; test=htons(0x3132);//(31ASIIC碼的’1’,32ASIIC碼的’2’) if((fp = fopen ("c:\\test.txt", "wb"))==NULL) assert(0);
fwrite(&test,sizeof(short), 1, fp); fclose(fp); }
如果在高字節在前的機器上,由於與網絡字節順序一致,所以我們什麼都不幹就可以了,只需要把#definehtons(x)sw16(x)宏替換爲 #define htons(x) (x).
一開始我在理解這個問題時,總在想爲什麼其他數據不用交換字節順序?比如說我們write一塊buffer到文件,最後終於想明白了,因爲都是unsignedchar類型一個字節一個字節的寫進去,這個順序是固定的,不存在字節順序的問題,夠笨啊..
|
http://hi.baidu.com/liyangzhao/blog/item/277e2ce7e105cf2db838200f.html
文章三:
big-endian和little-endian這兩個術語來自JonathanSwift在十八世紀的嘲諷作品Gulliver’sTravels。Blefuscu帝國的國民被根據吃雞蛋的方式劃分爲兩個部分:一部分在吃雞蛋的時候從雞蛋的大端(bigend)開始,而另一部分則從雞蛋的小端(little end)開始。
x86的CPU使用的是LE(Windows中稱爲“主機字節序”),而SocksAddr中使用的則是BE(就是“網絡字節序”),所以在使用網絡編程時需要使用htns,htnl,nths,nthl來倒字節序。
其實對彙編熟了就清楚了,慘,我的彙編很慘的
LE little-endian
最符合人的思維的字節序
地址低位存儲值的低位
地址高位存儲值的高位
怎麼講是最符合人的思維的字節序,是因爲從人的第一觀感來說
低位值小,就應該放在內存地址小的地方,也即內存地址低位
反之,高位值就應該放在內存地址大的地方,也即內存地址高位
BE big-endian
最直觀的字節序
地址低位存儲值的高位
地址高位存儲值的低位
爲什麼說直觀,不要考慮對應關係
只需要把內存地址從左到右按照由低到高的順序寫出
把值按照通常的高位到低位的順序寫出
兩者對照,一個字節一個字節的填充進去
例子:在內存中雙字0x01020304(DWORD)的存儲方式
內存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04
MSDN中關於LE和BE的解釋
Byte Ordering Byte ordering Meaning
big-endian The most significant byte is on the left end ofaword.
little-endian The most significant byte is on the right end ofaword.
這裏這個最重要的字節可以解釋成值的最高位,如果換成是錢的話就是最值錢的那一位
比如我有1234元人民幣,最值錢的是1000元,最不值錢的是4元,那麼這個1就是最重要的字節
Big endian machine: It thinksthefirst byte it reads is the biggest.
Little endian machine: It thinks the first byte it reads isthelittlest.
舉個例子,從內存地址0x0000開始有以下數據
0x0000 0x12
0x0001 0x34
0x0002 0xab
0x0003 0xcd
如果我們去讀取一個地址爲0x0000的四個字節變量,若字節序爲big-endian,則讀出
結果爲0x1234abcd;若字節序位little-endian,則讀出結果爲0xcdab3412.
如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
x86系列CPU都是little-endian的字節序.