深度剖析数据在内存中的存储(修炼内功~吊打面试官)
🌈前言
🌟
(???(??? )😉 我是Scort
快上車,握好方向盤跟我有一起打天下嘞!
想送給大家一句話:
🔥我們的選擇決定我們成為怎樣的人,而我們永遠有權利去做 對的選擇。
🌍博客主頁:張小姐的貓~江湖背景🌍
數(shù)據(jù)在內(nèi)存中的存儲
- 🌈前言
- 一、🌟數(shù)據(jù)類型介紹
- 🌽類型的基本歸類
- 🍋整型家族
- 🍋浮點型家族
- 🍋構造類型
- 🍋指針類型
- 🍋空類型
- 二、🌽整數(shù)在內(nèi)存中的存儲
- 🍋原碼,反碼,補碼
- 🍋大小端介紹
- 🍋小練習
- 三、🌽 浮點型在內(nèi)存中的存儲
- 🍋舉個例子
- 🍋浮點數(shù)存儲規(guī)則
- 🤔突發(fā)奇想
- 🏆
一、🌟數(shù)據(jù)類型介紹
前面我們已經(jīng)學習了基本的內(nèi)置類型:
char //字符數(shù)據(jù)類型
short //短整型
int //整形
long //長整型
long long //更長的整形
float //單精度浮點數(shù)
double //雙精度浮點型
注意: C語言中沒有字符串類型!
🌽類型的基本歸類
🍋整型家族
char:
unsigned char
signed char
short:
unsigned short [int]
signed short [int]
int:
unsigned int
signed int
long:
unsigned long [int]
signed long [int]
其中char雖然是字符類型,但是字符類型存儲的時候,存儲的字符的ascii碼是整數(shù)
char c1; signed char c2; unsigned char c3;敲黑板:1、C語言不確定char是有符號還是無符號的?是取決于編譯器的實現(xiàn)
2、C語言中規(guī)定int等價于signed int、short等價于signed short
這里我們來解釋一下什么是有符號,無符號↓
🍋浮點型家族
float
double
long double
🍋構造類型
構造類型(自定義類型)
數(shù)組類型
結構體類型 struct
枚舉類型 enum
聯(lián)合類型 union
int arr[10]中 arr的類型是int [10],同理char arr3[4]的類型是char [4]
其中int a[]={0,0,0}的類型是int [3]
🍋指針類型
int *pi
char *pc
float *pf
void *pv
🍋空類型
void 表示空類型(無類型)通常應用于函數(shù)的返回類型、函數(shù)的參數(shù)、指針類型。
void 則為“無類型指針”,void 可以指向任何類型的數(shù)據(jù)。
注意!!!:
1、如果函數(shù)沒有返回值,那么應聲明為void類型
2、如果函數(shù)無參數(shù),那么應聲明其參數(shù)為void。
3、小心使用void指針類型,不能對void指針進行算法操作
4、如果函數(shù)的參數(shù)可以是任意類型指針,那么應聲明其參數(shù)為void *
5、void不能代表一個真實的變量
若想更加詳細了解void用法可以閱讀C/C++中void用法總結進行更深入的研究。
二、🌽整數(shù)在內(nèi)存中的存儲
我們知道一個變量的創(chuàng)建是要在內(nèi)存中開辟空間的。空間的大小是根據(jù)不同的類型決定的
那接下來我們談談數(shù)據(jù)在所開辟內(nèi)存中到底是如何存儲的?
int a = 10; int b = 20;我們知道為a分配四個字節(jié)的空間,那如何存儲呢?接下來了解一下下面的概念↓
🍋原碼,反碼,補碼
計算機中的整數(shù)有三種表示方法,即原碼、反碼和補碼。
三種方法均有符號位和數(shù)值位兩部分,符號位 0 表示“正”, 用1 表示“負”。
負整數(shù)的三種表示方法各不相同。
原碼:直接將二進制按照正負數(shù)的形式翻譯成二進制就可以了。
反碼:將原碼的符號位不變,其他位依次按位取反就可以得到了。
補碼:反碼 +1 就得到補碼。
正數(shù)的原碼、反碼、補碼相同。
對整形來說:數(shù)據(jù)存放內(nèi)存中其實存放的是補碼。
為什么呢?
1、使用補碼可以將符號位和數(shù)值域統(tǒng)一處理
2、加法和減法可以統(tǒng)一處理(CPU只有加法器)
3、補碼和原碼互相轉(zhuǎn)化,其運算過程是相同的,不需要額外的電路。
舉個例子:
1 - 1;1+(-1) CPU只有加法器使用原碼計算000000000000000000000000000000011000000000000000000000000000000110000000000000000000000000000010 算出來是-2使用補碼的二進制計算00000000000000000000000000000001 (1)11111111111111111111111111111111 (-1)補碼100000000000000000000000000000000 只保留32位00000000000000000000000000000000 所以為010000000000000000000000000000001 (-1)原碼11111111111111111111111111111110 (-1)反碼11111111111111111111111111111111 (-1)補碼由此可見用原碼計算1-1并不為零,而用補碼可計算出正確值。
接下來我們看看在內(nèi)存中的存儲:
我們可以看見對于a和b分別存儲的是補碼。 可是有沒有覺得不對勁嗎?為什么數(shù)字是反過來排列的?難道不是應該是 00 00 00 14和 ff ff ff f6 嗎?
這里就要引入小端存儲和大端存儲。
🍋大小端介紹
什么是大端小端?
大端字節(jié)序存儲:是指數(shù)據(jù)的低位保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中;
小端字節(jié)序存儲:是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的高地址中;
對于16進制0x11223344來說,44是最低位的字節(jié)數(shù)據(jù),33是次低位。
但為什么會有大端小端呢?
這是因為在計算機系統(tǒng)中,我們是以字節(jié)為單位的,每個地址單元都對應著一個字節(jié),一個字節(jié)為8 bit。
但是在C語言中除了8 bit的char之外,還有16 bit的short型,32 bit的long型(要看具體的編
譯器),另外,對于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個字節(jié),那么必然存在著一個如何將多個字節(jié)安排的問題。因此就導致了大端存儲模式和小端存儲模式。
例如:一個 16bit 的 short 型 x ,在內(nèi)存中的地址為 0x0010 , x 的值為 0x1122 ,那么 0x11 為高字節(jié), 0x22 為低字節(jié)。
對于大端模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,剛好相反。
我們常用的 X86 結構是小端模式,而 KEIL C51 則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式
答:因為必然存在著一個如何將多個字節(jié)安排。
🍋小練習
百度2015年系統(tǒng)工程師筆試題:
請簡述大端字節(jié)序和小端字節(jié)序的概念,設計一個小程序來判斷當前機器的字節(jié)序。
🥃解題思路:通過char*來訪問1的首字節(jié)判斷是否為1
📜代碼實現(xiàn)
int main() {int a = 1;char*p=(char*)&a;//要對&a進行類型轉(zhuǎn)化if (*p == 1){printf("小端\n");}else {printf("大端\n");}return 0; }三、🌽 浮點型在內(nèi)存中的存儲
常見的浮點數(shù):
3.14159
1E10
浮點數(shù)家族包括: float、double、long double 類型。
浮點數(shù)表示的范圍:float.h中定義
🍋舉個例子
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; }輸出的結果的什么呢?
num 和 *pFloat 在內(nèi)存中明明是同一個數(shù),為什么浮點數(shù)和整數(shù)的解讀結果會差別這么大?跟我來吧🌋
🍋浮點數(shù)存儲規(guī)則
根據(jù)國際標準IEEE(電氣和電子工程協(xié)會) 754,任意一個二進制浮點數(shù)V可以表示成下面的形式:
- (-1)^S * M * 2^E
- (-1)^s表示符號位 ,當s=0,V為正數(shù);當s=1,V為負數(shù)。
- M表示有效數(shù)字,大于等于1,小于2。
- 2^E表示指數(shù)位。
😈舉兩個例子:↓
十進制的5.5,轉(zhuǎn)化成二進制就變成了101.1,用科學計數(shù)法表示就是 1.011 * 2^2 。那么,按照上面的格式,可以得出S = 0, M = 1.011,E = 2 。
同理十進制的9.0,轉(zhuǎn)化成二進制就變成了1001.0,用科學計數(shù)法表示就是 1.001 * 2^3 。那么,按照上面的格式,可以得出S = 0, M = 1.001,E = 3 。
IEEE 754規(guī)定:
對于32位的浮點數(shù),最高的1位是符號位s,接著的8位是指數(shù)E,剩下的23位為有效數(shù)字M。
對于64位的浮點數(shù),最高的1位是符號位S,接著的11位是指數(shù)E,剩下的52位為有效數(shù)字M。
IEEE 754對有效數(shù)字M和指數(shù)E,還有一些特別規(guī)定:
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ù)字。
至于指數(shù)E,情況就比較復雜。
首先,E為一個無符號整數(shù)(unsigned int)
這意味著,如果E為8位,它的取值范圍為0 ~ 255;如果E為11位,它的取值范圍為0 ~ 2047。但是,我們知道,科學計數(shù)法中的E是可以出現(xiàn)負數(shù)的,所以IEEE 754規(guī)定,存入內(nèi)存時E的真實值必須再加上一個中間數(shù)(修正值),對于8位的E,這個中間數(shù)是127;對于11位的E,這個中間數(shù)是1023 。這個是IEEE 754 的死規(guī)定(只能死記啦)
比如,2^10的E是10,所以保存成32位浮點數(shù)時,必須保存成10+127=137,即10001001。
😈舉個例子:
發(fā)現(xiàn)在內(nèi)存中的地址果然是0x40b00000
指數(shù)E從內(nèi)存中取出還可以再分成三種情況:
1??E不全為0或不全為1 →(怎么樣放怎么樣拿出來)
這時,浮點數(shù)就采用下面的規(guī)則表示,即指數(shù)E的計算值減去127(或1023),得到真實值,再將有效數(shù)字M前加上第一位的1。
比如:
0.5(1/2)的二進制形式為0.1,由于規(guī)定正數(shù)部分必須為1,即將小數(shù)點右移1位,則為
1.0*2^(-1),其階碼為-1+127=126,表示為01111110,而尾數(shù)1.0去掉整數(shù)部分為0,補齊0到23位00000000000000000000000,則其二進
制表示形式為:
0 01111110 00000000000000000000000
2??E全為0
這時,浮點數(shù)的指數(shù)E等于1-127(或者1-1023)即為真實值,
有效數(shù)字M不再加上第一位的1,而是還原為0.xxxxxx的小數(shù)。這樣做是為了表示±0,以及接近于0的很小的數(shù)字。
3??E全為1
這時,如果有效數(shù)字M全為0,表示±無窮大(正負取決于符號位s);
好了,關于浮點數(shù)的表示規(guī)則,就說到這里。
解釋前面的題目:
🤔突發(fā)奇想
既然浮點數(shù)的儲存不能精確存儲,那數(shù)據(jù)會不會不準確呢🤔
因為浮點數(shù)存儲會有誤差,所以不能直接相比較,應該比兩者之間的差值是否在允許的范圍內(nèi)
float f = 0.00001;if (f - 0.0){}return 0;如果同學們還想了解更多細膩的知識,可以看比特蛋哥~深入解刨的視頻
🏆
💯如有錯誤可以指出💯 🥇想學嗎?我教你啊🥇 🎉🎉覺得博主寫的還不錯的可以一鍵三連撒🎉🎉總結
以上是生活随笔為你收集整理的深度剖析数据在内存中的存储(修炼内功~吊打面试官)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用R筛选数据
- 下一篇: Codeforces Global Ro