梗概
本文介紹了字節(jié)序和比特序中的大端和小端,不同的cpu架構(gòu)有不同的內(nèi)存數(shù)據(jù)讀寫(xiě)方式蛙粘,但程序的數(shù)值計(jì)算發(fā)生在寄存器上垫卤,cpu通過(guò)在寄存器和內(nèi)存的數(shù)據(jù)傳輸轉(zhuǎn)換中對(duì)用戶(hù)隱藏了大端小端;數(shù)據(jù)在網(wǎng)絡(luò)中發(fā)送的時(shí)候會(huì)統(tǒng)一轉(zhuǎn)成大端出牧,在主機(jī)接收的時(shí)候根據(jù)主機(jī)的CPU架構(gòu)自行轉(zhuǎn)換穴肘,這維護(hù)了不同CPU架構(gòu)下的數(shù)據(jù)通信;但仍有缺陷的是舔痕,如果在跨平臺(tái)的程序中使用了位域评抚,由于涉及到比特序塊數(shù)據(jù)反轉(zhuǎn)問(wèn)題豹缀,需要手動(dòng)處理。
字節(jié)序: 大端數(shù)和小端數(shù)
大端數(shù)(big-endian)和小端數(shù)(little-endian)是兩種不同的數(shù)據(jù)存儲(chǔ)方式慨代,這兩個(gè)中文會(huì)讓人繞暈邢笙,從英文上是比較好理解的。
big-endian:大的部分(數(shù)的高位)在存儲(chǔ)單元的尾部
little-endian:小的部分(數(shù)的低位)在存儲(chǔ)單元的尾部
來(lái)看一張內(nèi)存圖鱼响, 從內(nèi)存單元上來(lái)看排列順序是這樣的鸣剪,也就是從下往上增長(zhǎng),從右往左增長(zhǎng)丈积,當(dāng)一個(gè)指針指向一個(gè)int型(四內(nèi)存單元)的變量時(shí)筐骇,指針的地址是地址最低的內(nèi)存單元,CPU從內(nèi)存中取數(shù)的時(shí)候是永遠(yuǎn)都是從最低位(右邊)開(kāi)始一個(gè)字節(jié)一個(gè)字節(jié)的取江滨,將它放到寄存器中(寄存器永遠(yuǎn)是符合人類(lèi)直覺(jué)的LE little-endian存儲(chǔ)方式, 內(nèi)存上的大小端存儲(chǔ)經(jīng)過(guò)CPU的加載到寄存器時(shí)被轉(zhuǎn)換成LE little-endian铛纬, 從而隱藏了內(nèi)存上的大小端),最左邊是就是取一個(gè)數(shù)的尾部.
所以就很好理解了唬滑,對(duì)于一個(gè)數(shù)int a=0x01020304
內(nèi)存地址 | 0x00000004 | 0x00000003 | 0x00000002 | 0x00000001 |
---|---|---|---|---|
big-endian | 04 | 03 | 02 | 01 |
little-endian | 01 | 02 | 03 | 04 |
而字節(jié)序在CPU與程序中的本質(zhì)是告唆,字節(jié)序是在數(shù)據(jù)的存儲(chǔ)字節(jié)大于1的時(shí)候,從內(nèi)存加載一個(gè)字節(jié)到寄存器上的不同加載方式晶密, 但使用數(shù)據(jù)的時(shí)候擒悬,無(wú)論是讀short ,int 還是long long最終真正使用的是寄存器上的le little endian 的值,所以大小端對(duì)人是隱藏的稻艰,但當(dāng)你一個(gè)一個(gè)去看內(nèi)存的值的時(shí)候懂牧,就是直接加載到最低位,這時(shí)候就不同了尊勿。
驗(yàn)證機(jī)器是big-endian還是little-endian
union num{
int temp;
char c[4];
};
int main(){
union num number;
number.temp = 0x01020304;
cout<<int(number.c[3])<<endl; //如果是1則是小端僧凤,4是大端
return 0;
}
網(wǎng)絡(luò)傳輸中的字節(jié)序
網(wǎng)絡(luò)中傳輸?shù)淖止?jié)序統(tǒng)一為big endian,主機(jī)上的數(shù)據(jù)在send的時(shí)候會(huì)通過(guò)hton進(jìn)行轉(zhuǎn)換元扔,到接收端recv的時(shí)候ntoh恢復(fù)回來(lái)躯保,如果hton在big endian架構(gòu)的系統(tǒng)上是一個(gè)空宏
比特序
字節(jié)序是在數(shù)據(jù)的存儲(chǔ)字節(jié)大于1的時(shí)候,從內(nèi)存加載一個(gè)字節(jié)到寄存器上的不同加載方式澎语。而比特序途事,則是加載一個(gè)8位的bit的時(shí)候到寄存器上的不同方式,一般而言比特序和主機(jī)的字節(jié)序保持一致是大端或者小端
網(wǎng)絡(luò)傳輸中的比特序和跨平臺(tái)程序
網(wǎng)絡(luò)傳輸過(guò)程中咏连,傳輸協(xié)議做了hton的字節(jié)序轉(zhuǎn)換盯孙,而網(wǎng)卡則會(huì)替我們將比特序統(tǒng)一轉(zhuǎn)換到大端,接收端網(wǎng)卡接收的時(shí)候根據(jù)自己的cpu架構(gòu)進(jìn)行轉(zhuǎn)換祟滴,對(duì)于cpu振惰,內(nèi)存而言也是不可見(jiàn)的;但是在使用c的位域的時(shí)候垄懂,會(huì)出現(xiàn)問(wèn)題骑晶,對(duì)于如下的結(jié)構(gòu)體痛垛,
struct A{
uint16_t first:4;
uint16_t second:4;
}A;
從小端序傳到大端序的主機(jī)上,比特序發(fā)生的倒序桶蛔,cpu在讀取的進(jìn)寄存器時(shí)候可以解決匙头,但是 first 和 second對(duì)應(yīng)數(shù)值的比特塊卻完全顛倒了,這就需要手動(dòng)矯正:
所以在涉及到位域的跨平臺(tái)設(shè)計(jì)中常會(huì)看到如下內(nèi)容,如此一來(lái)仔雷,在不同平臺(tái)下蹂析,就會(huì)發(fā)生反轉(zhuǎn)
struct A{
#if defined(__LITTLE_ENDIAN_BITFIELD)
uint16_t first:4;
uint16_t second:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
uint16_t second:4;
uint16_t first:4;
}A;
具體參考:
https://www.linuxjournal.com/article/6788
https://blog.csdn.net/liuxingen/article/details/45420455/