前言:Hello,大家好,我是心跳sy??,本節(jié)我們介紹c語言的兩種基本的內(nèi)置數(shù)據(jù)類型:數(shù)值類型和字符類型在內(nèi)存中的儲存方法,并對大小端進行詳細介紹(附兩種大小端判斷方法),文章每個例題和知識點都會有詳細的解釋,友友們放心食用,我們一起來看看吧~??!
1、??數(shù)據(jù)類型介紹??
1.1、??整型家族?
??我們之前已經(jīng)了解過 int、short、long、longlong、float、double、char這幾種基本的c語言內(nèi)置數(shù)據(jù)類型。其中 int、short、long、char類型是整型家族,其中char雖為字符型類型,但是它的ASCII碼在內(nèi)存中保存為整型,所以也可以歸為整型家族(這里我們?yōu)榱朔奖憬榻B整型和浮點型在內(nèi)存中的儲存規(guī)則),也把它納入整型之中。
??整型家族里面的每個數(shù)據(jù)類型也都擁有有符號(signed)和無符號(unsigned)兩種類型情況,其中char類型的有無符號與編譯器有關(guān)。數(shù)據(jù)在內(nèi)存的儲存與有無符號息息相關(guān)。
1.2、??浮點型家族
??浮點型家族包括 float和 double類型,有趣的是這兩個浮點型數(shù)據(jù)類型都沒有無符號的情況,這是為什么呢?我們知道,整型是采用二進制儲存在內(nèi)存中的,而浮點數(shù)卻是按照整數(shù)部分、小數(shù)部分、指數(shù)部分存放的,運算也是分開來運算的,所以unsigned無法作用于float與double這種浮點型。
1.3、??構(gòu)造類型?
??由表中我們可以看到,構(gòu)造類型由數(shù)組類型、結(jié)構(gòu)體類型、共用體類型(聯(lián)合類型) 以及枚舉類型組成,構(gòu)造類型一般可以給予用戶非常大的自定義范圍,可以同時對一個對象定義不同的變量,比如可以做到同時定義一個人的性別、年齡等等。(對四個構(gòu)造類型的詳細介紹我們后面都會單獨寫一篇文章,會統(tǒng)一收錄到c語言專欄中)
1.4、??指針類型?
??指針類型包括整型指針、浮點型指針、字符指針、數(shù)組指針、函數(shù)指針、空指針(void*)等等。?指針類型的作用是存放變量的地址,可以通過指針來改動變量。 指針作為一個變量是有大小的,其大小在32位平臺是4個字節(jié),64位平臺上是8個字節(jié),大小與指針的類型無關(guān)。(我們以后會對指針一章詳細介紹)
1.5、??空類型?
??空類型是指沒有定義類型的數(shù)據(jù)類型,也稱為void類型,在C語言中,空類型可以省略函數(shù)的參數(shù)列表,也可以修飾函數(shù)表示無返回值,通常用作函數(shù)指針,表示指向沒有返回值的函數(shù)的指針。 空類型和其他類型之間的轉(zhuǎn)換通常需要通過類型轉(zhuǎn)換操作符進行。?
記住以下3個規(guī)則:
??如果函數(shù)無參數(shù),應(yīng)聲明其參數(shù)為void類型。如:
int function(void)
{
return 1;
}
??若此時在調(diào)用function(2),在c語言編譯器中不會報錯,但是在c++編譯器中將會不合法報錯,所以無論在哪種編譯器中,一定養(yǎng)成良好習慣,若函數(shù)不接受任何參數(shù),一定要指明參數(shù)為void。
??如果函數(shù)沒有返回值,那么一定要聲明為void類型。?
??如果函數(shù)的參數(shù)可以是任意類型的指針,那么應(yīng)該聲明其參數(shù)為void*,比如內(nèi)存操作函數(shù)memset的函數(shù)原型為:
void*memset(void*buffer,int c,size_t num);
2、??插敘:原碼、反碼、補碼的介紹??
??我們知道,計算機底層只認識0和1,所以所有整型和浮點型數(shù)據(jù)到了底層都會通過轉(zhuǎn)換成二進制形式(0和1的形式)來儲存。計算機整數(shù)的表示方式有3種,即原碼、反碼、補碼。3種表示方式均有符號位和數(shù)值位兩部分,而所有整型數(shù)據(jù)在內(nèi)存中都會以補碼的形式儲存。我們要了解整型的內(nèi)存結(jié)構(gòu)就需要先知道原碼、反碼、補碼的知識。
??先來看看3個碼的基本概念:
??原碼是指一個數(shù)的二進制表示,第一位是符號位,正數(shù)為0,負數(shù)為1;
比如32位下-5的二進制,其原碼為:1000 0000 0000 0000 0000 0000 0000 0101?
??反碼是指將原碼中除符號位以外每一位取反后得到的二進制數(shù);
其反碼為:1111 1111 1111 1111 1111 1111 1111 1010?
??補碼是指將反碼加1后得到的二進制數(shù)。
其補碼為:1111 1111 1111 1111 1111 1111 1111 1011
??在整型中我們可以分成兩大類記憶理解:
正整數(shù):原碼、反碼、補碼相同。
比如:5的原碼、反碼、補碼都為:
0000 0000 0000 0000 0000 0000 0000 0101?
負整數(shù):原碼、反碼、補碼需進行計算得到,計算方式見概念。
??計算方式總結(jié)(針對負整數(shù)):
??從原碼——>補碼:原碼符號位不變,其他位按位取反,再加1;
??從補碼——>原碼:補碼符號位不變,其他位按位取反,再加1;(也可以先減1,再符號位不變,其他位按位取反)
3、??整型數(shù)據(jù)在內(nèi)存中的存儲??
??我們前面提到,整型數(shù)據(jù)都是以補碼的形式儲存在內(nèi)存中的。其原因在于,使用補碼,可以將符號位和數(shù)值域統(tǒng)一處理;同時,加法和減法也可以統(tǒng)一處理,(CPU只有加法器)此外,補碼與原碼相互轉(zhuǎn)換,其運算過程是相同的,不需要額外的硬件電路,比如原碼到補碼需取反加一,補碼到原碼也只需取反加一即可。
??我們來看一個簡單的整型數(shù)據(jù)內(nèi)存例子:
int main()
{
int i = 5;
int j = -10;
return 0;
}
可以看到i,j變量在x86環(huán)境下的內(nèi)存顯示如下:?
?
??從此例子中可以證明整型數(shù)據(jù)在內(nèi)存中都是以補碼的形式儲存的,正整數(shù)5的原、反、補相同,負整數(shù)20以補碼的十六進制形式儲存。
?但是我們又會發(fā)現(xiàn)一個問題,它們儲存的順序有些奇怪,我們知道,變量 i被分配4字節(jié)的內(nèi)存,其中5只占一個字節(jié),其他3個字節(jié)都為0, 而數(shù)據(jù)5被放置在第一個字節(jié)的位置,這放置的順序是按照什么規(guī)則來的嗎?這里就要介紹大小端的知識點了~
3.1、??大小端介紹(附兩種證明方法)
?為什么會有大小端之分呢?
??這是因為在計算機系統(tǒng)中,我們是以字節(jié)為單位的,每個地址單元都對應(yīng)著一個字節(jié),一個字節(jié)為8 bit。但是在C語言中除了8 bit的char之外,還有16 bit的short 型,32 bit的long型(要看具體的編譯器),另外,對于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個字節(jié),那么必然存在著一個如何將多個字節(jié)安排的問題。因此就導(dǎo)致了大端存儲模式和小端儲存模式。
??先看概念:
大端模式:指數(shù)據(jù)的高字節(jié)存儲在內(nèi)存的低地址處,而數(shù)據(jù)的低字節(jié)儲存在內(nèi)存的高地址中;
小端模式:指數(shù)據(jù)的高字節(jié)存儲在內(nèi)存的高地址處,而數(shù)據(jù)的低字節(jié)存儲在內(nèi)存的低地址中。?
?
??如果現(xiàn)在還不是很明白,沒關(guān)系,我們再來針對實例解釋:
??我們就看看5的存儲模式:?
5的補碼為(記住正整數(shù)原反補相同):0000 0000 0000 0000 0000 0000 0000 0101?
化為16進制表示為:0x00 00 00 05(其中05是低字節(jié),往前依次為高字節(jié))
??(一)、大端模式情況:可以看到低字節(jié)05存儲在高地址處,即為大端模式。
??(二)、小端模式情況:可以看到低字節(jié)存儲在低地址處,即為小端模式。
?
?那么怎么才能知道自己的編譯器用的是哪種模式呢,我們這里介紹兩種方法:
??方法一:通過union關(guān)鍵字來實現(xiàn)
??我們先介紹一下union關(guān)鍵字的用法:union的用法與struct非常相似,只不過union維護足夠空間來放置多個數(shù)據(jù)成員中的一種,而不是為每個數(shù)據(jù)成員都分配空間。在union中所有的數(shù)據(jù)成員共用一個空間,同一時間內(nèi)只能儲存其中一個數(shù)據(jù)成員,所有的數(shù)據(jù)成員都具有相同的起始地址。
??一個union只配置一個足夠大的空間來容納最大長度的數(shù)據(jù)成員,union關(guān)鍵字在c++中主要用來壓縮空間,如果一些數(shù)據(jù)不可能在同一時間被用到,則可以使用。
??我們可以利用union類型“所有數(shù)據(jù)成員的起始地址一致”為特點來證明大小端:
int Check_ram()
{
union check
{
int i;
char ch;
}c;
c.i = 1;
return (c.ch == 1);
}
int main()
{
if (Check_ram() == 0)
{
printf("是大端模式\n");
}
else
printf("是小端模式\n");
return 0;
}
代碼分析:
??我們設(shè)置一個函數(shù)來判斷大小端,函數(shù)中定義兩個數(shù)據(jù)成員變量,一個是int類型變量i,一個是char類型變量ch,我們給i賦值為1,這時內(nèi)存分配一個可以容納最大長度數(shù)據(jù)成員的內(nèi)存(此時分配4字節(jié),最大數(shù)據(jù)類型是int),函數(shù)返回char類型變量是否為一的值,若為1,則返回1,若不為1,則返回0。主函數(shù)中接收返回值,如果返回值為0則是大端模式(因為成員起始地址一樣,int型為4字節(jié),char類型變量只有一字節(jié),所以利用1所在的1字節(jié)位置在哪來判斷大小端),若返回值等于1則是小端模式。
??可以看到輸出結(jié)果為小端模式,證明函數(shù)返回值為1,因為i變量的1肯定為低字節(jié)(見上文分析),并且int類型的內(nèi)存分配是由低地址到高地址的,union中數(shù)據(jù)成員起始地址又一樣,所以如果ch中的值也為1,那么一定是小端模式。
?
??方法二:普通方法強制類型轉(zhuǎn)換實現(xiàn)
int Check_ram()
{
int i = 1;
return (*(char*)&i);
}
int main()
{
if (Check_ram()== 1)
{
printf("是小端模式\n");
}
else
{
printf("是大端模式\n");
}
return 0;
}
??輸出結(jié)果與上面一樣,都為小端模式,我們同樣設(shè)計函數(shù)實現(xiàn),只需定義一個int型變量,只不過函數(shù)的返回值是將i的地址強制類型轉(zhuǎn)換為char*后解引用的值,返回的類型就改為char了,與上面方法是同種道理,不明白的友友可以參照上述解釋。
3.2、??signed與unsigned關(guān)鍵字對整型取值的影響
??我們知道,計算機底層只認識0和1,任何數(shù)據(jù)到了底層都必須轉(zhuǎn)換為0和1,那么負數(shù)怎么儲存呢?我們前面了解了計算機整數(shù)的3種表示方法原碼、反碼、補碼,均有符號位和數(shù)值位兩部分組成,數(shù)據(jù)類型的最高位是用來存符號的,約定若這個數(shù)為整數(shù),則最高位為0,否則為1,其值為除最高位以外剩余位的值。
??這樣的話,一個32位的 signed int類型整數(shù),其值表示的范圍為:-2^31~ (2^31-1);8位的 char類型數(shù),其值表示的范圍為:-2^7~(2^7-1)。一個 32 位的 unsigned int類型整數(shù),其值表示的范圍為:0~(2^32-1);8位的 unsigned char 類型數(shù),其值表示的范圍為:0~(2^8-1)。需要說明的是,signed 關(guān)鍵字也很寬宏大量,你也可以完全當它不存在,缺省情況下(默認條件下),編譯器默認數(shù)據(jù)為signed類型 (char類型數(shù)據(jù)除外)。
??看了上面的概念,相信你已經(jīng)有了自己的理解,我們通過下面一道看似簡單的題目康康你真的懂了嗎??
int main()
{
signed char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
輸出結(jié)果如下:?
?
這個結(jié)果和你想的是否一樣呢?如果不一樣沒關(guān)系,我們一起來看看分析過程:
?
??由上面的分析過程,我們知道a[0]-a[254]里面對應(yīng)的值都不是0,而到a[255]時值為0,strlen函數(shù)時用來計算字符串長度的,遇到‘\0’,則認為字符串結(jié)束讀取,所以到現(xiàn)在我們就能明白了為什么計算字符串長度是255了吧,這個問題的關(guān)鍵就是要明白signed char的取值范圍為[-128~127],而超出這個范圍就會產(chǎn)生溢出必須截斷,之后就會產(chǎn)生循環(huán)。
??重點:我們總結(jié)一個有符號char的范圍規(guī)律,可以畫圖看到其范圍可以形成一個圓,從0開始不斷+1,直到新的循環(huán)。
?
?文章來源地址http://www.zghlxwxcb.cn/news/detail-685002.html
???看了一個signed char類型的例題,我們再來看一個unsigned char類型的例題,猜一下結(jié)果會是多少呢?
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
??重點:輸出結(jié)果是死循環(huán)輸出hello world,我們用一張圖來解釋:
??我們看到,unsigned char類型范圍為[0~255],他因為是無符號的,所以8位中的最高位符號位變?yōu)閿?shù)值位,參與數(shù)的大小計算,從畫圖可以得到,當?shù)?55時為11111111,當?shù)?56時,發(fā)生內(nèi)存溢出,最高位被截去,又會變成0,循環(huán)又一次開始,所以上述例子中,當i=256時,i變成0,繼續(xù)循環(huán),死循環(huán)hello world。
4、??浮點型數(shù)據(jù)在內(nèi)存中的儲存??
??整數(shù)類型并不適用于所有應(yīng)用,有些時候需要變量能存儲帶小數(shù)點的數(shù),或者能存儲極大數(shù)或極小數(shù)。而這類數(shù)可以用浮點格式(小數(shù)點是浮動的)進行儲存,C語言提供了3種浮點格式。
浮點數(shù)家族包括:float(單精度浮點數(shù))、double(雙精度浮點數(shù))、long double(擴展精度浮點數(shù))類型
常見的浮點數(shù):3.14159? ? ? ? ?1E10(1*10^10)
??我們來看看浮點數(shù)在內(nèi)存中的存儲方法(大多數(shù)現(xiàn)代計算機遵循IEEE 754標準(即IEC 60559)規(guī)范)
??IEEE 754標準提供了兩種主要的浮點數(shù)格式:單精度(32位)和雙精度(64位)。數(shù)值以科學計數(shù)法的形式存儲,每一個數(shù)都由3部分組成:符號、指數(shù)和小數(shù)。指數(shù)部分的位數(shù)說明了數(shù)值的可能大小程度,而小數(shù)部分的位數(shù)說明了精度。
??單精度格式中,指數(shù)長度為8位(E 指數(shù)位),而小數(shù)部分占了23位(有效數(shù)字M),因此單精度數(shù)可以表示的最大值約是3.40*10^38,其中精度是6個十進制數(shù)字。?
??見下圖:
??雙精度格式中,指數(shù)長度為11位(E指數(shù)位),而小數(shù)部分占了52位(有效數(shù)字M)
??見下圖:
??任意一個二進制浮點數(shù) V?可以表示為下面形式:?
比如:十進制的5.0,寫成二進制是 101.0 ,相當于 1.01×2^2 。
??那么,按照上面V的格式,可以得出S=0,M=1.01,E=2。
十進制的-5.0,寫成二進制是 -101.0 ,相當于 -1.01×2^2 。?
??那么,S=1,M=1.01,E=2。?
????對于M(有效數(shù)字)和E(指數(shù)位),IEEE有一些特別規(guī)定:
??對于M:
前面提到, 1≤M<2 ,也就是說,M可以寫成 1.xxxxxx 的形式,其中xxxxxx表示小數(shù)部分。
IEEE 754規(guī)定,在計算機內(nèi)部保存M時,默認這個數(shù)的第一位總是1,因此可以被舍去,只保存后面的 xxxxxx部分。
比如保存1.01的時候,只保存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位浮點數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字?。
??對于E:(情況比較復(fù)雜)
? ? ?·首先,E為一個無符號整數(shù)。
由此,浮點數(shù)存儲規(guī)則結(jié)束。
? 感謝大家花費寶貴的時間閱讀本文章,制作不易,希望大家多多支持呀??????,如有任何問題歡迎各位大佬在評論區(qū)批評指正?。?!
?文章來源:http://www.zghlxwxcb.cn/news/detail-685002.html
?
到了這里,關(guān)于數(shù)據(jù)在內(nèi)存中的儲存·大小端(文字+畫圖詳解)(c語言·超詳細入門必看)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!