日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

字节序、位序

發(fā)布時(shí)間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字节序、位序 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

字節(jié)序

??? 字節(jié)序,又稱端序、尾序,英文單詞為Endian,該單詞來(lái)源于于喬納森·斯威夫特的小說(shuō)《格列佛游記》,小說(shuō)中的小人國(guó)因?yàn)槌噪u蛋的問(wèn)題而內(nèi)戰(zhàn),戰(zhàn)爭(zhēng)開始是由于以下的原因:我們大家都認(rèn)為,吃雞蛋前,原始的方法是打破雞蛋較大的一端??墒钱?dāng)今皇帝的祖父小時(shí)候吃雞蛋,一次按古法打雞蛋時(shí)碰巧將一個(gè)手指弄破了,因此他的父親,當(dāng)時(shí)的皇帝,就下了一道敕令,命令全體臣民吃雞蛋時(shí)打破雞蛋較小的一端,違令者重罰。老百姓們對(duì)這項(xiàng)命令極為反感。歷史告訴我們,由此曾發(fā)生過(guò)六次叛亂,其中一個(gè)皇帝送了命,另一個(gè)丟了王位…關(guān)于這一爭(zhēng)端,曾出版過(guò)幾百本大部著作,不過(guò)大端派的書一直是受禁的,法律也規(guī)定該派的任何人不得做官。?1980年,Danny Cohen在其著名的論文"On Holy Wars and a Plea for Peace"中,為平息一場(chǎng)關(guān)于字節(jié)該以什么樣的順序傳送的爭(zhēng)論,而引用了該詞。

?????????在計(jì)算機(jī)科學(xué)領(lǐng)域中,字節(jié)序是指存放多字節(jié)數(shù)據(jù)的字節(jié)(byte)的順序,典型的情況是整數(shù)在內(nèi)存中的存放方式和網(wǎng)絡(luò)傳輸?shù)膫鬏旐樞?。有時(shí)候也可以用指位序(bit)。為了更好地理解,先看下面這段小程序,這個(gè)程序是把一個(gè)包含4位數(shù)字的字符串轉(zhuǎn)換為16進(jìn)制整數(shù)來(lái)存儲(chǔ),16進(jìn)制整數(shù)的每一個(gè)字節(jié)存儲(chǔ)一位數(shù)字字符。比如:”1234”,轉(zhuǎn)換成16進(jìn)制整數(shù)0x01020304。

程序1清單:

#include?<stdio.h>

#include?<conio.h>

?

int?main( )

{

?????char?input[4] = {0};

?????int?integer???= 0;

?????int?i;

?????printf("/r/n請(qǐng)輸入一個(gè)位數(shù),每一位的范圍是從到0到9/r/n");

?????for(i = 0; i < 4; i++)

?????{

?????????input[i] = getch();

?????????if(input[i] >?'9'?|| input[i] <?'0')

?????????{

??????????????printf("input error!/r/n");

??????????????return?1;

?????????}

?????????putch(input[i]);

?????}

????getch();

?????putch('/n');

?

?????for(i = 0; i < 4; i++)

?????{

?????????input[i] = input[i] -?'0';

?????}

?

?????memcpy((void*)&integer, (void*)input, 4);

?

?????printf("轉(zhuǎn)換后的進(jìn)制數(shù)是:0x%08x/r/n", integer);

?

?????getch();

?

?????return?0;

}

?

現(xiàn)在來(lái)分析一下這段代碼

首先,定義了一個(gè)字符數(shù)組input,用來(lái)接收用戶輸入的4個(gè)數(shù)字字符;

第二,把4個(gè)字符數(shù)字轉(zhuǎn)換成對(duì)應(yīng)的數(shù)字;

第三,把轉(zhuǎn)換的數(shù)字復(fù)制到整型變量integer中;

最后在屏幕上打印。

如果在PPC或者ARM的機(jī)器上編譯運(yùn)行這個(gè)程序,那么會(huì)在屏幕上打印出結(jié)果:0x01020304,這與我們的預(yù)期一致;但是在X86的機(jī)器上則打印出的結(jié)果是:0x04030201。這個(gè)令人驚訝的結(jié)果正是字節(jié)序問(wèn)題引起。下面來(lái)詳細(xì)談?wù)勥@個(gè)問(wèn)題。

?

????從計(jì)算機(jī)誕生之后,就有幾種不同的字節(jié)序,典型的是大端序(big endian)和小端序(little endian),當(dāng)然還有不常見的混合序(middle endian)。這些用來(lái)都是描述多字節(jié)數(shù)據(jù)在內(nèi)存中的存放方式的。以上面的16進(jìn)制數(shù)0x01020304為例,在計(jì)算機(jī)中需要用4個(gè)字節(jié)來(lái)保存它,’01’, ’02’,’03’,’04’各占一個(gè)字節(jié)。按照人類的計(jì)數(shù)習(xí)慣,最左邊的’01’,稱之為最高有效位(MSB,Most Significant Byte,它具有最高權(quán)重);最右邊的’04’稱之為最低有效位(LSB, Least Significant Byte,他具有最低權(quán)重);在計(jì)算機(jī)中需要用4個(gè)字節(jié)來(lái)保存它,其中’01’, ’02’,’03’,’04’各占一個(gè)字節(jié)。大端序的計(jì)算機(jī)保存這個(gè)數(shù)值時(shí),按照從低地址到高地址的順序分別保存MSB到LSB四個(gè)字節(jié),0x01020304的存儲(chǔ)情況如下圖所示:

?

而小端序的計(jì)算機(jī)則以相反的順序來(lái)保存它,如下圖所示:

?

?

可以看到,在小端序的計(jì)算機(jī)中,0x01020304的保存順序恰好與上面的程序1中相反,這就是最后輸出結(jié)果為0x04030201的原因;大端序的計(jì)算機(jī)中兩者保存順序一致,所以打印正確。

?

??? 我們可以在VC集成環(huán)境中來(lái)驗(yàn)證上面的分析。在VS2005中調(diào)試下面的小程序,在return語(yǔ)句處設(shè)置斷點(diǎn),斷住后打開內(nèi)存窗口查看&i處的內(nèi)容??梢灾庇^的看到在x86(小端序)的機(jī)器上整數(shù)的存放方式。

程序2清單:

#include?<stdio.h>

int?main()

{

?????int?i = 0x01020304;

?????printf("i = %#x/r/n",i);

?????return?0;

}

?

?

?

?

?

再看看如何才能讓程序1在大端序和小端序的機(jī)器上都能正確執(zhí)行呢?一個(gè)辦法就是利用預(yù)編譯宏,針對(duì)不同的機(jī)器定義定義不同的數(shù)據(jù)結(jié)構(gòu)。下面是一個(gè)例子:

程序3清單:

#include?<stdio.h>

#include?<conio.h>

?

typedef?union

{

?????struct{

#ifdef?BIG_ENDIAN

?????char msb;

?????char midb1;

?????char midb2;

?????char lsb;

#else

?????char?lsb;

?????char?midb2;

?????char?midb1;

?????char?msb;

#endif

?????} bytes;

?????int??var;

} INTEGER;

?

int?main()

{

?????int??i??????????= 0;

?????char?input[5]???= {0};

?????INTEGER integer = {0};

?

?????printf("/r/n請(qǐng)輸入一個(gè)位數(shù),每一位的范圍是從到到/r/n");

?????for(i = 0; i < 4; i++)

?????{

?????????input[i] = getch();

?????????if(input[i] >?'9'?|| input[i] <?'0')

?????????{

??????????????printf("input error!/r/n");

??????????????return?1;

?????????}

?????????putch(input[i]);

?????}

????getch();

?????putch('/n');

?

?????integer.bytes.msb???= input[0] -?'0';

?????integer.bytes.midb1 = input[1] -?'0';

?????integer.bytes.midb2 = input[2] -?'0';

?????integer.bytes.lsb???= input[3] -?'0';

?

?????printf("轉(zhuǎn)換后的進(jìn)制數(shù)是:0x%08x/r/n", integer.var);

?

?????getch();

?

?????return?0;

}

可以看到,這段代碼定義了兩套數(shù)據(jù)結(jié)構(gòu),通過(guò)BIG_ENDIAN這個(gè)宏定義來(lái)決定使用哪一套數(shù)據(jù)結(jié)構(gòu)。這是個(gè)笨拙卻有效的方法。下面這個(gè)例子則漂亮一些:

程序4清單

#include?<stdio.h>

#include?<conio.h>

#include?<memory.h>

#include?<winsock2.h>

?

int?main( )

{

?????char?input[4] = {0};

?????int?integer???= 0;

?????int?i;

?????printf("/r/n請(qǐng)輸入一個(gè)位數(shù),每一位的范圍是從到到/r/n");

?????for(i = 0; i < 4; i++)

?????{

?????????input[i] = getch();

?????????if(input[i] >?'9'?|| input[i] <?'0')

?????????{

??????????????printf("input error!/r/n");

??????????????return?1;

?????????}

?????????putch(input[i]);

?????}

????getch();

?????putch('/n');

?

?????for(i = 0; i < 4; i++)

?????{

?????????input[i] = input[i] -?'0';

?????}

?

?????memcpy((void*)&integer, (void*)input, 4);

?

?????integer = ntohl(integer);

?

?????printf("轉(zhuǎn)換后的進(jìn)制數(shù)是:0x%08x/r/n", integer);

?

?????getch();

?

?????return?0;

}

這個(gè)程序利用了大端序與人類書寫習(xí)慣一致的特點(diǎn),通過(guò)ntohl函數(shù)將整數(shù)進(jìn)行轉(zhuǎn)換。這個(gè)函數(shù)的功能是將網(wǎng)絡(luò)序轉(zhuǎn)換成主機(jī)序,在大端機(jī)器上它什么也不做,在小端機(jī)器上它會(huì)將輸入?yún)?shù)的值轉(zhuǎn)換成小端序的值。在windows環(huán)境下鏈接時(shí)別忘了將Ws2_32.lib庫(kù)添加進(jìn)來(lái)。

?

主機(jī)序和網(wǎng)絡(luò)序

主機(jī)序就是指主機(jī)的端序。

網(wǎng)絡(luò)字節(jié)序(網(wǎng)絡(luò)序)指多字節(jié)數(shù)據(jù)在網(wǎng)絡(luò)傳輸中的順序,TCP協(xié)議規(guī)定網(wǎng)絡(luò)序是大端序,即高字節(jié)先發(fā)送。因此大端序的機(jī)器接受到的數(shù)據(jù)可直接使用,小端序機(jī)器則需要轉(zhuǎn)換后使用。BSD socket API中定義了一組轉(zhuǎn)換函數(shù),用于16和32bit整數(shù)在網(wǎng)絡(luò)序和本機(jī)字節(jié)序之間的轉(zhuǎn)換。htonl,htons用于本機(jī)序轉(zhuǎn)換到網(wǎng)絡(luò)序;ntohl,ntohs用于網(wǎng)絡(luò)序轉(zhuǎn)換到本機(jī)序。一般來(lái)說(shuō),為了保證程序的可移植性,編寫代碼時(shí),發(fā)送的數(shù)據(jù)需要使用htonl、htons轉(zhuǎn)換,接收到的數(shù)據(jù)要使用ntohl、ntohs轉(zhuǎn)換。

注意:不存在對(duì)單字節(jié)整數(shù)進(jìn)行轉(zhuǎn)換的函數(shù)”ntohc”和”htonc”!

位序

?????????位序,一般用于描述串行設(shè)備的傳輸順序。一般說(shuō)來(lái)大部分硬件都是采用小端序(先傳低位),因此,對(duì)于一個(gè)字節(jié)數(shù)據(jù),大部分機(jī)器上收發(fā)的順序都一樣,不會(huì)有問(wèn)題,這就是為什么沒(méi)有針對(duì)單字節(jié)數(shù)據(jù)的API接口”ntohc”和”htonc”。當(dāng)然,也有例外,比如-I2C協(xié)議就是采用了大端序。這些細(xì)節(jié)只有在網(wǎng)絡(luò)協(xié)議的數(shù)據(jù)鏈路層底端才會(huì)碰到,對(duì)一般的程序員來(lái)說(shuō)很少涉及。

?????????但是在C語(yǔ)言中存在一種特殊的數(shù)據(jù)結(jié)構(gòu):位域。它的存在,使得C程序員能方便地進(jìn)行位操作(比如在網(wǎng)絡(luò)協(xié)議中經(jīng)常出現(xiàn)1bit或者多bit的標(biāo)示位,它們不是一個(gè)完整的字節(jié))。但同時(shí)也引起一些難以察覺(jué)的問(wèn)題,這些問(wèn)題的根源仍然是前面提到的端序。

?

?????????與字節(jié)序一樣,一個(gè)字節(jié)中的8個(gè)bit順序在不同端序的機(jī)器上并不相同。大端機(jī)器上從低地址到高地址順尋分別是msb->lsb,如下圖:

?

?

小端序的機(jī)器上則正好相反

?

現(xiàn)代計(jì)算機(jī)的最小存儲(chǔ)單位是BYTE,無(wú)法對(duì)bit尋址,因此我們無(wú)法直接觀察每個(gè)字節(jié)內(nèi)部bit的順序。但是我們?nèi)匀豢梢酝ㄟ^(guò)位域來(lái)間接觀察字節(jié)內(nèi)部bit順序,以印證上面的說(shuō)法。

在C語(yǔ)言中,位域與結(jié)構(gòu)體類似,其語(yǔ)法規(guī)定:先聲明的成員位于低地址,后聲明的成員位于高地址。那么下面的位域中:

typedef?struct?OneByte

{

?????bt0 : 1;

?????bt1 : 1;

?????bt2 : 1;

?????bt3 : 1;

?????bt4 : 1;

?????bt5 : 1;

?????bt6 : 1;

?????bt7 : 1;

}

成員bt0就位于一個(gè)字節(jié)中最低地址bit0處,成員bt7就位于一個(gè)字節(jié)的最地址bit7處。

?

我們看看下面的程序。

#include?<stdio.h>

?

typedef?struct?OneByte

{

?????char?bt0 : 1;

?????char?bt1 : 1;

?????char?bt2 : 1;

?????char?bt3 : 1;

?????char?bt4 : 1;

?????char?bt5 : 1;

?????char?bt6 : 1;

?????char?bt7 : 1;

} ONE_BYTE;

?

int?main()

{

?????ONE_BYTE onebyte = {0};

????

?????onebyte.bt7 = 1;

?

?????printf("onebyte = %#x/r/n", *((unsigned?char?*)&onebyte));

?

?????return?0;

}

?

當(dāng)bt7賦值為1后,onebyte在內(nèi)存中是這個(gè)樣子的:

?

而在VC2005中編譯運(yùn)行的結(jié)果如下:

?

0x80轉(zhuǎn)換成二進(jìn)制是1000 0000。由于在X86(小端序)中,高地址bit7是msb,因此onebyte的值是0x80了;這就證實(shí)了前面的說(shuō)法。?相應(yīng)的如果是在大端序計(jì)算機(jī)中,bit7是lsb,則onebyte的值是0x01。

?

?

?

未完待續(xù)

總結(jié)

以上是生活随笔為你收集整理的字节序、位序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。