C語(yǔ)言大小端字節(jié)序存儲(chǔ)模式深入解讀
前言
本文以C語(yǔ)言實(shí)現(xiàn),主要通過(guò)例題+說(shuō)明的模式講解存儲(chǔ)模式:大小端字節(jié)序。
對(duì)于正整數(shù)而言,它的補(bǔ)碼 = 原碼 = 反碼;
對(duì)于負(fù)整數(shù)而言,它的補(bǔ)碼 = 原碼按位取反(就是反碼) + 1;
例如,當(dāng)語(yǔ)句int a = 500被執(zhí)行時(shí),內(nèi)存中會(huì)開(kāi)辟 4字節(jié)(即32bit)的空間存儲(chǔ)整數(shù)20的二進(jìn)制補(bǔ)碼(00000000 00000000 0000000111110100)。同時(shí),我們也知道,內(nèi)存中的每一個(gè)存儲(chǔ)單元的大小為 1字節(jié)(8bit)。
那么此時(shí)問(wèn)題來(lái)了:int a = 500所占的總空間是4字節(jié),然而每一個(gè)內(nèi)存單元只存的下1字節(jié)的數(shù)據(jù),換句話說(shuō),每一個(gè)整型數(shù)據(jù)需要4個(gè)存儲(chǔ)單元才能存的下——那么,這4個(gè)存儲(chǔ)單元在內(nèi)存中到底是如何分布的呢?
是像數(shù)組一樣,用來(lái)存儲(chǔ)數(shù)據(jù)的4個(gè)內(nèi)存單元彼此之間連續(xù)開(kāi)辟,還是物理空間上其實(shí)并不連續(xù)、只是解析數(shù)據(jù)時(shí)把4個(gè)單元中的數(shù)據(jù)“拼”到一起,還原出連續(xù)的整型數(shù)呢?
每一個(gè)字節(jié)的數(shù)據(jù),如何“被安排”存儲(chǔ)空間?
這個(gè)問(wèn)題,本質(zhì)上是數(shù)據(jù)在內(nèi)存中的存儲(chǔ)模式問(wèn)題。這就關(guān)系到我們今天要介紹的重點(diǎn):大端數(shù)據(jù)存儲(chǔ)模式或小端數(shù)據(jù)存儲(chǔ)模式(即大端字節(jié)序與小端字節(jié)序)。
我們打開(kāi)編譯器vs2019的內(nèi)存監(jiān)視,可以從監(jiān)視窗口看到數(shù)值在內(nèi)存中的存儲(chǔ)情況(顯示為16進(jìn)制)。
注意:32位下,
20的16進(jìn)制補(bǔ)碼為:00 00 00 14
-10的十六進(jìn)制補(bǔ)碼為:ff ff ff f6
兩個(gè)16進(jìn)制位恰好是一個(gè)字節(jié)8bit。我們將視圖調(diào)整為每行顯示4個(gè)字節(jié)(即一個(gè)int,這樣看得更清晰),于是可以發(fā)現(xiàn),在內(nèi)存中數(shù)據(jù)似乎是“倒著存”的。00 00 00 14在內(nèi)存中的存儲(chǔ)顯示為了14 00 00 00.
事實(shí)上,這并不叫做“倒著存”,這正是我們上面提到過(guò)的:小端字節(jié)序的存儲(chǔ)模式。在閱讀完本文后,我們就能明白。
一、大小端介紹
當(dāng)一個(gè)數(shù)值的大小超過(guò) 1字節(jié) ,那么它要存到內(nèi)存中,字節(jié)與字節(jié)之間就有存儲(chǔ)順序的問(wèn)題。這個(gè)順序稱(chēng)之為字節(jié)序。
以int類(lèi)型為例。
理論上來(lái)說(shuō),即使存儲(chǔ)一個(gè)int型數(shù)據(jù)的4個(gè)存儲(chǔ)單元不連續(xù),甚至天南海北,只要最終解析時(shí)能還原成一個(gè)正常的數(shù)即可。但是這么做屬實(shí)沒(méi)有必要:計(jì)算機(jī)中存儲(chǔ)肯定是采用最便捷的存儲(chǔ)方式,那就是連續(xù)存儲(chǔ),一個(gè)整型的4個(gè)字節(jié)空間挨在一塊兒。
這時(shí)要考慮的問(wèn)題便是,這四個(gè)挨在一塊的存儲(chǔ)空間,是由低地址向高地址存儲(chǔ),還是由高地址向低地址存儲(chǔ)?
1. 大端字節(jié)序與小端字節(jié)序的概念
小端字節(jié)序:把一個(gè)數(shù)值的低位字節(jié)內(nèi)容存放在低地址處;高位字節(jié)內(nèi)容存放在高地址處。(低位存低地址,高位存高地址)。
大端字節(jié)序:把一個(gè)數(shù)值的低位字節(jié)內(nèi)容存放在高地址處;高位字節(jié)內(nèi)容存放在低地址處。(低位存高地址,高位存低地址)。
例如,要存儲(chǔ)一個(gè)十六進(jìn)制數(shù) 0x11223344。該數(shù)中右端是低數(shù)位,左端是高數(shù)位(類(lèi)比十進(jìn)制數(shù),十進(jìn)制數(shù)10里面0是個(gè)位更低,1是十位更高)。
小端存儲(chǔ)情況如下:
小端存儲(chǔ)模式示意
小端存儲(chǔ)模式下,若取出該int型數(shù)據(jù)的首地址內(nèi)容會(huì)發(fā)現(xiàn),存的其實(shí)是0x44。
而大端存儲(chǔ)情況如下:
大端存儲(chǔ)模式示意
以上就是對(duì)大小端存儲(chǔ)模式的理解。
至于實(shí)際中如何存儲(chǔ)的,還取決于具體編譯器的選擇?,F(xiàn)在大部分的編譯器選擇的是小端存儲(chǔ)模式,也就是我們上面看到的“倒著存”。
這時(shí)再參照引言中的例子,應(yīng)該能對(duì)這兩種存儲(chǔ)模式有一個(gè)較好的理解了。
2. 為什么會(huì)有大小端之分?
這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們以字節(jié)為單位,每個(gè)地址單元都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8 bit 。但是在 C 語(yǔ)言中,除了 8 bit 的 char 之外,還有 16 bit 的 short 型,32 bit 的 long 型(要看具體的編譯器);另外,對(duì)于位數(shù)大于 8 位的處理器,例如 16 位或者 32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問(wèn)題。因此就導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式。
例如,一個(gè) 16bit 的 short 型 x ,在內(nèi)存中的地址為 0x0010 , x 的值為 0x1122 ,那么 0x11 為高字節(jié),0x22 為低字節(jié)。
對(duì)于大端模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。 小端模式,剛好相反。我們常用的 X86 結(jié)構(gòu)是小端模式,而 KEIL C51 則為大端模式。很多ARM , DSP 都為小端模式。有些 ARM 處理器還可以由硬件來(lái)選擇是大端模式還是小端模式。
3.一道和字節(jié)序相關(guān)的例題
題干
在小端機(jī)器中,下面代碼輸出的結(jié)果是:
#include <stdio.h> int main() { int a = 0x11223344; char *pc = (char*)&a; *pc = 0; printf("%x\n", a); return 0; }
思路
本題中的數(shù)值與我們上面討論的數(shù)值一樣,應(yīng)該不難理解:
1. char*類(lèi)型的指針變量pc指向的只能指向char類(lèi)型的空間。如果是非char類(lèi)型的空間,則必須將該空間的地址強(qiáng)轉(zhuǎn)為char*類(lèi)型。
2. char *pc = (char*)&a; pc實(shí)際指向的是整形變量a的空間,即指針pc里放的是0x00405090,指向的值即0x44。
3. *pc=0,關(guān)鍵一步:究竟是將哪個(gè)存儲(chǔ)單元中的值置零了?由首個(gè)單元中存儲(chǔ)的內(nèi)容可知,將0x44位置中內(nèi)容改為了0。修改完成之后,a中內(nèi)容為:0x11223300 (數(shù)值書(shū)寫(xiě)肯定是高數(shù)位在左,低數(shù)位在右)
二、如何設(shè)計(jì)一個(gè)小程序判斷當(dāng)前機(jī)器的字節(jié)序
百度2015年系統(tǒng)工程師筆試題
題干
請(qǐng)簡(jiǎn)述大端字節(jié)序和小端字節(jié)序的概念,設(shè)計(jì)一個(gè)小程序來(lái)判斷當(dāng)前機(jī)器的字節(jié)序。( 10 分)
解題
概念部分同上,在此不再贅述。
代碼部分:
1. 要檢測(cè)某一機(jī)器是大端還是小端其實(shí)并不難,我們可以直接用int類(lèi)型的1來(lái)檢測(cè)。
2. 1在內(nèi)存中的二進(jìn)制補(bǔ)碼為:00000000000000000000000000000001
也就是說(shuō),我們只需要把首個(gè)內(nèi)存單元的值取出來(lái)看看是0還是1,就能判斷它是大端還是小端。
因?yàn)槿绻谴蠖耍瑒t“高數(shù)位存在低地址”,我們?nèi)〕鲎畹偷刂返闹?,?yīng)當(dāng)是最高數(shù)位上的數(shù)00000000;而若是小端,則“低數(shù)位存在低地址”,依然取出最低地址的值,應(yīng)當(dāng)是00000001
依照該思路,有以下代碼:
#include <stdio.h> int check_sys() { int i = 1; return (*(char *)&i); } int main() { int ret = check_sys(); if(ret == 1) { printf("小端\n"); } else { printf("大端\n"); } return 0; }
當(dāng)然,也可以用共用體來(lái)實(shí)現(xiàn)檢查,這是更為便捷的一種方式:
int check_sys() { union { int i; char c; }un; un.i = 1; return un.c; }
到此這篇關(guān)于C語(yǔ)言大小端字節(jié)序存儲(chǔ)模式深入解讀的文章就介紹到這了,更多相關(guān)C語(yǔ)言大小端字節(jié)序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用C++中string實(shí)現(xiàn)任意長(zhǎng)度的正小數(shù)、整數(shù)之間加減法方法實(shí)例
這篇文章主要介紹了利用C++中string函數(shù)實(shí)現(xiàn)任意長(zhǎng)度的正小數(shù)、整數(shù)之間加減法方法實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-06-06C語(yǔ)言算法的時(shí)間復(fù)雜度和空間復(fù)雜度
這篇文章主要介紹了C語(yǔ)言算法的時(shí)間復(fù)雜度和空間復(fù)雜度,算法在編寫(xiě)成可執(zhí)行程序后,運(yùn)行時(shí)需要耗費(fèi)時(shí)間資源和空間(內(nèi)存)資源,更多相關(guān)需要的朋友可以參考一下2022-07-07C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼
以下是對(duì)C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼進(jìn)行了分析介紹,需要的朋友可以參考下2013-07-07C語(yǔ)言柔性數(shù)組的實(shí)現(xiàn)示例
柔性數(shù)組既數(shù)組大小待定的數(shù)組, C語(yǔ)言中結(jié)構(gòu)體的最后一個(gè)元素可以是大小未知的數(shù)組,本文就來(lái)介紹一下柔性數(shù)組的用法,感興趣的可以了解一下2024-03-03C++?STL中五個(gè)常用算法使用教程及實(shí)例講解
本文主要介紹了C++?STL算法中常見(jiàn)的五個(gè)算法的使用教程并附上了案例詳解,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11Qt學(xué)習(xí)教程之對(duì)話框消失動(dòng)畫(huà)效果
這篇文章主要給大家介紹了關(guān)于Qt學(xué)習(xí)教程之對(duì)話框消失動(dòng)畫(huà)效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07從c++標(biāo)準(zhǔn)庫(kù)指針萃取器談一下traits技法(推薦)
本篇文章基于gcc中標(biāo)準(zhǔn)庫(kù)源碼剖析一下標(biāo)準(zhǔn)庫(kù)中的模板類(lèi)pointer_traits,并且以此為例理解一下traits技法,對(duì)c++ traits技法源碼分析感興趣的朋友跟隨小編一起看看吧2021-07-07