C語言詳細分析不同類型數據在內存中的存儲

數據類型的介紹

在我們之前的學習當中我們已經介紹瞭基本的內置類型

char 字符數據類型

short 短整型

int 整形

long 長整型

long long 更長的整形

float 單精度浮點數

double 雙精度浮點數

這些類型的意義是:

1.使用這個類型開辟內存空間的大小,大小決定瞭使用范圍

2.如何看待內存空間的視角。

類型的基本歸類

整形

整形中分為有符號整形和無符號整形,因為我們生後中有些數值需要有正數和負數之分,例如溫度我們就可以使用有符號整形,但是有些不需要負數的數值例如身高,我們就可以使用無符號整形。

char

unsigned char

signed char

short

unsigned short [int]

signed short [int]

int

unsigned int

signed int

long

unsigned long [int]

signed long [int]

浮點型

float

double

構造類型

數組類型

結構體類型 struct

枚舉類型 enum

聯合類型 union

指針類型

int pi;

char pc;

float pf;

void pv;

空類型

void表示空類型(無類型)

通常應用於函數的返回類型,函數的參數、指針類型

整形在內存中的存儲

我們都知道,創建一個變量需要在內存中開辟空間,那變量究竟是怎麼在內存當中存儲的呢。我們又要提到原碼、反碼、補碼的概念瞭。

在計算機中整數有三種表示方式即原碼、反碼、補碼,他們都有符號位和數值位,符號位用0表示正數,用1表示負數。

正整數的原碼、反碼、補碼相同,都是直接將屬豬按照正負數的形式翻譯成二進制形式就可以得到原碼。

負整數的原碼、反碼、補碼

原碼

直接將數值按照正負數的形式翻譯成二進制形式就可以得到原碼

反碼

將原碼的符號位不變,其他位一次按位取反就可以得到反碼

補碼

反碼+1就是補碼

對於一個整形,數據在內存當中存放的其實是他的補碼。那麼為什麼是補碼而是不是原碼呢,原因是使用補碼,可以將符號位和數值域統一處理,同時加法和減法也可以統一處理,我們的cpu隻有加法器,此外,補碼與原碼相互轉換,其運算過程是相同的,不需要額外的硬件電路。

例如:

1 – 1
因為cpu中隻有加法器,所以我們需要將減法轉化為加法,1+ (-1)如果內存中存儲的是原碼,那麼他們相加的結果為
00000000 00000000 00000000 00000001 1的原碼
10000000 00000000 00000000 00000001 -1的原碼
10000000 00000000 00000000 00000010 相加為-2
我們運算的答案是錯誤的,但是當我們存儲的是補碼時:
00000000 00000000 00000000 00000001 1的補碼
11111111 11111111 11111111 11111111 -1的補碼
00000000 00000000 00000000 00000000 相加0
使用補碼運算時,得到正確的答案

int main()
{
	int a = 20;
	int b = -10;
	return 0;
}

如上圖編譯器為瞭方便顯示的是16進制數,其實對於一個整形,數據在內存當中存放的還是他的補碼,我們可以是嘗試還原一下。

例如**-10**
原碼:10000000 00000000 00000000 00001010
反碼:11111111 11111111 11111111 11110101
補碼:11111111 11111111 11111111 11110110
將他的補碼轉換成16進制就變成瞭ff ff ff f6,跟我們通過調試看到的內容一樣

但是我們通過對比發現雖然存儲的是補碼,但是存儲的順序好像不一樣,這是怎麼回事呢?

大小端介紹

當我們的數據大於一個字節時,數據的存儲就有瞭順序問題。例如:一個十六進制數0x11223344,我們說11為數據的高位,44為數據的低位。

大端存儲模式:是指數據的低位保存在高地址中,而數據的高位,保存在內存的低地址處。

小端存儲模式:是指數據的低位保存在低地址中,而數據的高位,保存在內存的高地址處。

int main()
{
	int a = 0x11223344;
	return 0;
}

我們通過調試得出,在vs2019中為小端存儲模式,因為數據的低位44存儲在地址處,高位11存儲在高地址處。

一道筆試題

我們通過一道筆試題來鞏固一下我們剛才所總結的知識。題目:請簡述大端字節序和小端字節序的概念,設計一個小程序來判斷當前機器的字節序。

小端字節序存儲

把數據的低位存儲在內存的低地址處,高位字節的內容,存儲在內存的高地址處 大端字節序存儲 把數據的低字節的內容,存放到內存的高地址處,高字節內容,存放在低地址處

int check_sys()
{
	int a = 1;
	return *(char*)&a;
}
int main()
{
	if (check_sys() == 1)
	{
		printf("小端存儲\n");
	}
	else
	{
		printf("大端存儲\n");
	}
	return 0;
}

浮點數在內存中的存儲

首先我們通過一段代碼來觀察浮點數在內存中的存儲

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值為:%d\n", n);
	printf("*pFloat的值為:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("num的值為:%d\n", n);
	printf("*pFloat的值為:%f\n", *pFloat);
	return 0;
}

以我們的認識,使用%d打印應該打印9使用%f打印應該打印9.0,但事實是這樣的嘛

我們將代碼跑起來發現,並非我們所認識的,那這說明瞭什麼,說明瞭浮點型與整形在內存當中的存儲規則是不一樣的,那我們先瞭解一下浮點數究竟怎麼儲存的。

浮點數存儲規則

根據國際標準IEEE(電氣和電子工程協會)754,任意一個二進制浮點數v可以表示成下面的形式:

(-1)^S * M * 2^E

(-1)^S表示符號位,當S=0,V為正數;當S=1,V為負數。

M表示有效數字,大於等於1,小於2。

2^E表示指數位。

例如:十進制5.0,寫成二進制是101.0,相當於1.0122

所以s = 0,m = 1.01, e = 2。

IEEE 754規定:

對於32位的浮點數,最高的1位是符號位s,接著的8位是指數E,剩下的23位為有效數字M。對於64位的浮點數,最高的1位是符號位S,接著的11位是指數E,剩下的52位為有效數字M。

IEEE 754對有效數字M和指數E,還有一些特別規定:

前面說過, 1≤M<2 ,也就是說,M可以寫成 1.xxxxxx 的形式,其中xxxxxx表示小數部分。IEEE 754規定,在計算機內部保存M時,默認這個數的第一位總是1,因此可以被舍去,隻保存後面的xxxxxx部分。比如保存1.01的時候,隻保存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的,是節省1位有效數字。以32位浮點數為例,留給M隻有23位,將第一位的1舍去以後,等於可以保存24位有效數字。

至於指數E,情況就比較復雜:

首先,E為一個無符號整數(unsigned int)這意味著,如果E為8位,它的取值范圍為0 ~ 255;如果E為11位,它的取值范圍為0 ~ 2047。但是,我們知道,科學計數法中的E是可以出現負數的,所以IEEE 754規定,存入內存時E的真實值必須再加上一個中間數,對於8位的E,這個中間數是127;對於11位的E,這個中間數是1023。比如,2^10的E是10,所以保存成32位浮點數時,必須保存成10+127=137,即10001001。

既然我們可以將浮點數存儲,我們就可以將他們取出來,但是指數E從內存中取出分為三種情況:

E不全為0或不全為1

這時,浮點數就采用下面的規則表示,即指數E的計算值減去127(或1023),得到真實值,再將 有效數字M前加上第一位的1。

E全為0

這時,浮點數的指數E等於1-127(或者1-1023)即為真實值,有效數字M不再加上第一位的1,而是還原為0.xxxxxx的小數。這樣做是為瞭表示±0,以及接近於0的很小的數字。

E全為1

這時,如果有效數字M全為0,表示±無窮大(正負取決於符號位s)

剖析題目

這些就是浮點數在內存當中的存儲規則,瞭解瞭這些後讓我們回到最開始的那道題,我們進行分析。

首先我們創建瞭一個整形變量,那麼他在內存中存儲的是什麼呢?是他的補碼00000000000000000000000000001001,當我們將他看作一個浮點數打印時,我們可以得到S = 0,M = 000 0000 0000 0000 0000 1001,E = 00000000。所以經過計算(-1)^ 0 × 0.00000000000000000001001×2 ^ (-126) = 1.001×2 ^ (-146), 所以根據規則他是一個非常接近0的數,使用浮點數打印就是0.000000

筆試題的第二部分,我們將一個浮點型使用整形打印,同樣的道理,我們先將9.0用浮點數的方式存進內存當中,9.0寫成二進制為1001.0,可以寫成(-1)^0 * 1.001*2^3,我們可以推出二進制形式:0 10000010 001 0000 0000 0000 0000 0000,所以將他作為一個整形打印時就會是一個非常大的數為1091567616

到此這篇關於C語言詳細分析不同類型數據在內存中的存儲的文章就介紹到這瞭,更多相關C語言數據存儲內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: