目錄
一、數(shù)據(jù)類型的介紹
1.類型的意義:
2.類型的基本分類
二、整形在內(nèi)存中的存儲(chǔ)
1.原碼 反碼 補(bǔ)碼
2.大小端介紹
3.練習(xí)
三、浮點(diǎn)型在內(nèi)存中的存儲(chǔ)
1.一個(gè)例子
??2.浮點(diǎn)數(shù)存儲(chǔ)規(guī)則
一、數(shù)據(jù)類型的介紹
前面我們已經(jīng)學(xué)習(xí)了基本的內(nèi)置類型以及他們所占存儲(chǔ)空間的大?。?/p>
char? ? ? ? ? //字符數(shù)據(jù)類型
short? ? ? ? ?//短整型
int? ? ? ? ? ? ?//整形
long? ? ? ? ? //長整形
long long? ?//更長的整形
float? ? ? ? ? ?//單精度浮點(diǎn)型
double? ? ? //雙精度浮點(diǎn)型
1.類型的意義:
1.使用這個(gè)類型開辟內(nèi)存空間的大?。ù笮Q定了使用的范圍)
2.決定了如何看待內(nèi)存空間的視角:
int 和float類型都是4個(gè)字節(jié),但是一個(gè)是整形,一個(gè)是浮點(diǎn)型,看待內(nèi)存空間的視角不一樣
2.類型的基本分類
(1)整形家族:
char:
? ? ? ? unsigned char
? ? ? ? signed? char
short :
? ? ? ?unsigned short? [int]? ? ?//短整型,這個(gè)int整形可以省略
? ? ? ? signed? short? ?[int]
int :
? ? ??unsigned int
? ? ? ? signed? int
long :
? ? ? ?unsigned long [int]
? ? ? ? signed? long [int]
【溫馨提示】:char類型也是整形家族的原因:
字符在內(nèi)存中存儲(chǔ)的是字符的ACSII碼值(0-127),ASCII碼值是整形,所以字符類型歸類到整形家)族?
signed -有符號(hào)的:當(dāng)?shù)谝晃淮矸?hào)位的時(shí)候,就是有符號(hào)的
unsigned -無符號(hào)的:當(dāng)每一位都是數(shù)值位,有效位的時(shí)候就是無符號(hào)的
【注意】:
當(dāng)我們沒有寫signed和unsigned時(shí),int,short和long類型默認(rèn)就是signed有符號(hào)的
eg:當(dāng)我們寫出int a的默認(rèn)的其實(shí)就是signed int類型
but?:C語言并沒有規(guī)定char是否是signed char(這個(gè)取決于編譯器,大部分是signed char)
(2)浮點(diǎn)數(shù)家族:都可以表示小數(shù)
?float? ? ?//精度小一些,單精度
double? ?//精度大一些,雙精度
(3)構(gòu)造類型(自定義類型)
>數(shù)組類型
>結(jié)構(gòu)體類型 struct
>枚舉類型 enum
>聯(lián)合類型 union
(4)指針類型
int *pi
char *pc
float *pf
void * pv? (無具體類型的指針)
(5) 空類型
void 表示空類型(無類型)
通常應(yīng)用于函數(shù)的返回類型,函數(shù)的參數(shù),指針類型
eg:int main(void)就表示main函數(shù)不需要參數(shù)
但是實(shí)際上main函數(shù)是有三個(gè)參數(shù)的int main(int argc,char *argv[? ],char *envp[? ]),這三個(gè)參數(shù)需要用的時(shí)候才需要寫,不需要括號(hào)直接寫void即可
二、整形在內(nèi)存中的存儲(chǔ)
計(jì)算機(jī)能夠處理的是二進(jìn)制數(shù)據(jù),整形和浮點(diǎn)型在內(nèi)存中也都是以二進(jìn)制的形式進(jìn)行存儲(chǔ)的
1.原碼 反碼 補(bǔ)碼
整形的二進(jìn)制表示有三種:原碼,反碼,補(bǔ)碼
正的整數(shù):原碼,反碼,補(bǔ)碼相同
負(fù)的整數(shù):原碼,反碼,補(bǔ)碼要進(jìn)行計(jì)算
整數(shù)在內(nèi)存中存儲(chǔ)的是補(bǔ)碼的二進(jìn)制序列
eg:
int a = -10;//int類型占4個(gè)字節(jié)-32bit位
?? ?10000000 00000000 00000000 00001010 ?原碼
?? ?11111111 11111111 11111111 11110101 ?反碼
?? ?1 1111111 11111111 11111111 11110110 ?補(bǔ)碼(最高一位表示符號(hào)位,其他31位表示數(shù)值位)?? ?unsigned int b = -10;
?? ?1 1111111 11111111 11111111 11110110 ?補(bǔ)碼(32位全都表示數(shù)值位)
對(duì)于整形來說,數(shù)據(jù)存放內(nèi)存中其實(shí)存放的是補(bǔ)碼
為什么呢?
使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理;同時(shí),加減法也可以統(tǒng)一處理(cpu只有加法器),此外,補(bǔ)碼和原碼相互轉(zhuǎn)換,其運(yùn)算過程是相同的,不需要額外的硬件電路
eg:
? ? 1-1
? ? 電腦轉(zhuǎn)化為1+(-1)
?? ?00000000 00000000 00000000 00000001 ?1的原反補(bǔ)碼
?? ?10000000 00000000 00000000 00000001 ?-1的原碼
?? ?11111111 11111111 11111111 11111110 ?-1的補(bǔ)碼
?? ?11111111 11111111 11111111 11111111 ?-1的補(bǔ)碼
?? ?如果就是簡單的原碼相加得到的就是-2(還會(huì)猶豫要不要加符號(hào)位)
?? ?但是如果是補(bǔ)碼相加得到的就是正確的結(jié)果,每個(gè)位上不斷進(jìn)1,最后最前面多出來一位為1直接舍棄,其他位都為0
2.大小端介紹
int a=0x11223344(根據(jù)數(shù)據(jù)的存儲(chǔ)44位于低字節(jié)處,11位于高字節(jié)處)
大端字節(jié)序存儲(chǔ):
把一個(gè)數(shù)據(jù)的低位字節(jié)處的數(shù)據(jù)存放在內(nèi)存的高地址處,高位字節(jié)處的數(shù)據(jù)存放在內(nèi)存的低地址處
小端字節(jié)序存儲(chǔ):
把一個(gè)數(shù)據(jù)的低位字節(jié)處的數(shù)據(jù)存放在內(nèi)存的低地址處,高位字節(jié)處的數(shù)據(jù)存放在內(nèi)存的高地址處
【注意】:數(shù)據(jù)存放的時(shí)候是以字節(jié)為單位存儲(chǔ)討論順序的,所以叫做大小端字節(jié)序存儲(chǔ)
char類型不需要考慮大小端,char類型就占一個(gè)字節(jié),沒有順序可言
為什么存在大小端字節(jié)序存儲(chǔ)呢?
這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8 bit。但是在C語言中除了8 bit的char之外,還有16 bit的short型,32 bit的long型(要看具體的編譯器),另外,對(duì)于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問題。因此就導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式
百度筆試題:
請(qǐng)簡述大端字節(jié)序和小端字節(jié)序的概念,設(shè)計(jì)一個(gè)程序來判斷當(dāng)前機(jī)器的字節(jié)序
思路:
給一個(gè)int類型的變量a:讓其為1(這樣十六進(jìn)制簡單0x 00 00 00 01),然后再通過char*一次訪問一個(gè)字節(jié),打印出來看是00還是01,從而判斷大小端
代碼實(shí)現(xiàn):
#include<stdio.h>
int main()
{
int a = 1;
char* p = (char*)&a; //要將&a(int *)強(qiáng)制轉(zhuǎn)化為char *
if (*p == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
【自定義函數(shù)進(jìn)行判斷】:
#include<stdio.h>
int check_sys()
{
int a = 1;
return *(char*)&a;
}
int main()
{
if(check_sys()==1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
3.練習(xí)
<1>下面程序輸出什么?
#include <stdio.h>
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
答案:
-1 -1 255
解釋:
首先-1是整數(shù),原碼:10000000 00000000 00000000 00000001
? ? ? ? ? ? ? ? ? ? ? ? ?反碼:111111111?111111111?111111111?111111110
? ? ? ? ? ? ? ? ? ? ? ?? 補(bǔ)碼:111111111?111111111?111111111?111111111
但是char類型只有8個(gè)比特位,所以補(bǔ)碼存起來就是111111111,而且第一位為符號(hào)位(對(duì)于a和b)
%d是10進(jìn)制的形式打印有符號(hào)的整數(shù)
那么就需要進(jìn)行整形提升(無符號(hào)數(shù)高位補(bǔ)0,有符號(hào)數(shù)高位補(bǔ)符號(hào)位)(對(duì)原碼整形提升)
對(duì)于a和b:整形提升后補(bǔ)碼為111111111?111111111?111111111?111111111(也就是-1)
對(duì)于c:整形提升后補(bǔ)碼為00000000 00000000 00000000?111111111(又因?yàn)槭菬o符號(hào)的整形,補(bǔ)碼和原碼一樣)(也就是255)
?<2>下面程序輸出什么?
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n", a);
return 0;
}
答案:
4294967168
解釋:
-128的原碼:10000000 00000000 00000000 10000000
? ? ? ? ? ?反碼:111111111?111111111?111111111 011111111
? ? ? ? ? ?補(bǔ)碼:111111111?111111111?111111111?10000000
存進(jìn)a的補(bǔ)碼:10000000(1為符號(hào)位)
對(duì)a進(jìn)行整形提升:111111111?111111111?111111111?10000000(有符號(hào)位高位補(bǔ)符號(hào)位1)
%u是10進(jìn)制的形式打印無符號(hào)的整數(shù)
那么打印就當(dāng)a是無符號(hào)數(shù)打印,對(duì)于無符號(hào)數(shù)原反補(bǔ)碼相同,直接算即可
?<3>下面程序輸出什么?
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n", a);
return 0;
}
答案:
4294967168
解釋:
雖然signed char最大只能是127,但是還是可以賦值為128,可以自行截?cái)?/strong>
128的原碼:00000000 00000000 00000000 10000000
存進(jìn)a的補(bǔ)碼:10000000(1為符號(hào)位)
對(duì)a進(jìn)行整形提升:111111111?111111111?111111111?10000000(有符號(hào)位高位補(bǔ)符號(hào)位1)
10進(jìn)制無符號(hào)形式打印
【總結(jié)】:?
signed char:-128~127
char-假設(shè)是有符號(hào)的char(1個(gè)字節(jié)=8bit)?(第一位為符號(hào)位)第一列為原碼
00000000? ?0
00000001? ?1
00000010? ?2
00000011? ?3
...? ? ? ? ? ? ? ? ...
011111111? 127
10000000 -128? 11111111(反) 110000000(補(bǔ):多出來一位要?jiǎng)h去)
10000001? -127? 11111110? ? ? ? ? ?111111111
...
111111110? -2? ? ? 10000001? ? ? ? ? ? 10000010
111111111? -1? ? ? 10000000? ? ? ? ? ? 10000001
??
假設(shè)是unsigned char:0~255
?00000000
00000001? ?1
00000010? ?2
00000011? ?3
...? ? ? ? ? ? ? ??
011111111? 127
10000000? 128
...
111111110? 254
111111111? 255
??<4>下面程序輸出什么?
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
答案:
-10
解釋:
-20:原碼:10000000 00000000 00000000 00010100
? ? ? ? ?反碼:111111111 111111111 111111111 11101011
? ? ? ? ?補(bǔ)碼:111111111 111111111 111111111 11101100
10:原反補(bǔ)碼:00000000 00000000 00000000 00001010(相加時(shí)最高位變?yōu)榉?hào)位)
補(bǔ)碼進(jìn)行相加:111111111 111111111 111111111 11110110(補(bǔ)碼)
反碼:10000000 00000000 00000000?00001001
原碼:10000000 00000000 00000000?00001010(-10)
???<5>下面程序輸出什么?
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
答案:
9到0再到4294967295,一直減小,死循環(huán)
解釋:
unsigned int的范圍就是>=0的,所以for循環(huán)的判斷條件恒成立,類比unsigned char當(dāng)0繼續(xù)減小,就到了255,unsigned int也是這樣的
????<6>下面程序輸出什么?
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
答案:
255
解釋:
strlen是統(tǒng)計(jì)\0(也就是0)之前的字符個(gè)數(shù)
a[ i ]里面放的是-1,-2,-3...-128 127 ...6 5 4 3 2 1 0
一共就是128+127=255個(gè)數(shù)
?????<7>下面程序輸出什么?
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
答案:
死循環(huán)
解釋:
unsigned char的范圍就是0-255,for循環(huán)的條件恒成立,進(jìn)入死循環(huán)
三、浮點(diǎn)型在內(nèi)存中的存儲(chǔ)
常見的浮點(diǎn)數(shù):
3.14159
1E10(也就是1.0*10^10)
浮點(diǎn)數(shù)家族包括:float,double,long double類型
浮點(diǎn)數(shù)表示的范圍:float.h中定義
1.一個(gè)例子
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;
}
結(jié)果:
??2.浮點(diǎn)數(shù)存儲(chǔ)規(guī)則
任意一個(gè)二進(jìn)制浮點(diǎn)數(shù)V可以表示成下面的形式:
eg:10進(jìn)制的5.5轉(zhuǎn)化為二進(jìn)制
101.1(小數(shù)點(diǎn)后面一位就是2的-1次方也就是0.5)
二進(jìn)制浮點(diǎn)數(shù)表示也就是(-1)^0*1.011*2^2(小數(shù)點(diǎn)提前兩位,也就是*2^2(二進(jìn)制),如果是十進(jìn)制就是2^10)?
得出:S=0,M=1.011,E=2
對(duì)于32位的浮點(diǎn)數(shù),最高的1位是符號(hào)位s,接著的8位是指數(shù)E,剩下的23位為有效數(shù)字M
對(duì)于64位的浮點(diǎn)數(shù),最高的1位是符號(hào)位S,接著的11位是指數(shù)E,剩下的52位為有效數(shù)字M
?
有效數(shù)字M的存儲(chǔ):
對(duì)于有效數(shù)字M,1<=M<2,在計(jì)算機(jī)內(nèi)部保存M的時(shí)候,默認(rèn)小數(shù)點(diǎn)前面一位為1,所以保存只保存小數(shù)點(diǎn)后面的數(shù)字,這樣就節(jié)省了一位數(shù)的空間,以32位為例,雖然留給M只有23位,但是相當(dāng)于保存了24位有效數(shù)字?
有效數(shù)字E的存儲(chǔ):
?首先E是一個(gè)為無符號(hào)數(shù),如果E為8位,它的取值范圍為0-255;如果E為11位,它的取值范圍為0-2047。存入E的真實(shí)值時(shí)必須加上一個(gè)中間值,對(duì)于8位的E這個(gè)中間值為127,對(duì)于11的E,這個(gè)中間值為1023
eg:2^10的E為10,所以保存32位浮點(diǎn)數(shù)時(shí),必須保存成10+127=137,即10001001
?指數(shù)E從內(nèi)存中取出還可以再分成三種情況:
(1)E不全為0或不全為1:
指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將M小數(shù)點(diǎn)前面的1補(bǔ)上
eg:
0.5的二進(jìn)制為0.1,浮點(diǎn)數(shù)表示:1.0*2^(-1),E存儲(chǔ)為-1+127=126,也就是01111110,而尾數(shù)1.0去除1就是0,那么0.5的二進(jìn)制表示形式就是:
0 01111110 00000000000000000000000
(2)E全為0:
這時(shí)浮點(diǎn)數(shù)的指數(shù)E等于1-127(或者1-1023)即為真實(shí)值
M這時(shí)也不需要加上小數(shù)點(diǎn)前面的1,而是還原成0.xxxx的小數(shù),這樣做是為了表示正負(fù)0,以及接近于0的很小的數(shù)
(3)E全為1:
這時(shí),如果有效數(shù)字M全為0,表示±無窮大(正負(fù)取決于符號(hào)位s)
?現(xiàn)在再來解釋一下前面的例子:
從int類型的9來看:
int n=9;
00000000?00000000?00000000?00001001(int類型二進(jìn)制)
但是當(dāng)它強(qiáng)制類型轉(zhuǎn)化為float*時(shí),代表的含義就不一樣了
0 00000000 00000000000000000001001
這時(shí)的E為全0,那么E=-126,M也不用補(bǔ)0,即M=0.00000000000000000001001,S=0
那么*pFloat也就是(-1)^0*0.00000000000000000001001*2^(-126),這個(gè)數(shù)是極其小的,打印出來就直接是0.000000(float打印小數(shù)點(diǎn)后6位)
從float類型的9.0來看:(當(dāng)*pFloat=9.0以后)
9.0(1001.0)
浮點(diǎn)型表示形式:(-1)^0*1.001*2^3
二進(jìn)制表示:0 10000010 00100000000000000000
然后%d形式打?。簄的視角看這是補(bǔ)碼,符號(hào)位是0,為正數(shù),原反補(bǔ)碼相同,轉(zhuǎn)化為10進(jìn)制也就是1091567616
本次內(nèi)容就到此啦,歡迎評(píng)論區(qū)或者私信交流,覺得筆者寫的還可以,或者自己有些許收獲的,麻煩鐵汁們動(dòng)動(dòng)小手,給俺來個(gè)一鍵三連,萬分感謝 !?文章來源:http://www.zghlxwxcb.cn/news/detail-688742.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-688742.html
到了這里,關(guān)于【C進(jìn)階】深度剖析數(shù)據(jù)在內(nèi)存中的存儲(chǔ)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!