文章目錄
本章重點
數(shù)據(jù)類型詳細介紹整形在內(nèi)存中的存儲:原碼、反碼、補碼大小端字節(jié)序介紹及判斷浮點型在內(nèi)存中的存儲解析 1. 數(shù)據(jù)類型介紹
基本的內(nèi)置類型:
char //字符數(shù)據(jù)類型
short //短整型
int //整形
long //長整型
long long //更長的整形
float //單精度浮點數(shù)
//雙精度浮點數(shù)
類型的意義:
使用這個類型開辟內(nèi)存空間的大?。ù笮Q定了使用范圍)。如何看待內(nèi)存空間的視角。 1.1 類型的基本歸類:
整形家族:
char
char
char
short
short [int]
short [int]
int
int
int
long
long [int]
long [int]
long long
long long [int]
long long [int]
注:
1.字符在存儲的時候存儲的是ASCII碼值,ASCII是整數(shù),所以在歸類的時候,字符屬于整形家族
2.short int num = = short num(int可以省略)
3.int num = = int num(short,long,long long同理)
4.char是 char還是 char是取決于編譯器的,常見情況下char = = char
浮點數(shù)家族:
float
構(gòu)造類型:(自定義類型)
數(shù)組類型
結(jié)構(gòu)體類型
枚舉類型 enum
聯(lián)合類型 union
指針類型:
int * pi;
char * pc;
float * pf;
void * pv;
空類型:
void 表示空類型(無類型)
通常應(yīng)用于函數(shù)的返回類型、函數(shù)的參數(shù)、指針類型。
void test()
{}
void test(void)
{}
void * p
2. 整形在內(nèi)存中的存儲
我們之前講過一個變量的創(chuàng)建是要在內(nèi)存中開辟空間的,空間的大小是根據(jù)不同的類型而決定的。那接下來我們談?wù)剶?shù)據(jù)在所開辟內(nèi)存中到底是如何存儲的?
比如:
int a = 20;
int b = -10;
我們知道為 a 分配四個字節(jié)的空間。
如何存儲?要了解原碼、反碼、補碼的概念。
2.1 原碼、反碼、補碼
原返補詳解:
對于整形來說:數(shù)據(jù)存放內(nèi)存中其實存放的是補碼。
這是因為:
在計算機系統(tǒng)中,數(shù)值一律用補碼來表示和存儲。原因在于,使用補碼,可以將符號位和數(shù)值域統(tǒng)一處理;
同時,加法和減法也可以統(tǒng)一處理(CPU只有加法器)此外,補碼與原碼相互轉(zhuǎn)換,其運算過程是相同的,不需要額外的硬件電路。
我們看看在內(nèi)存中的存儲:
int main()
{
int a = 20;
int b = -10;
//20
//00000000000000000000000000010100 - 原碼、反碼、補碼
//0000 0000 0000 0000 0000 0000 0001 0100
//0x00 00 00 14
//-10
//10000000000000000000000000001010 - 原碼
//11111111111111111111111111110101 - 補碼
//1111 1111 1111 1111 1111 1111 1111 0110 - 反碼
//0xff ff ff f6
return 0;
}
我們可以看到對于a和b分別存儲的是補碼。但是我們發(fā)現(xiàn)順序有點不對勁。
這里就涉及到大小端的知識了。
2.2 大小端介紹
什么大端小端:
大端(大端字節(jié)序存儲)模式:是指數(shù)據(jù)的低位保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中;
小端(小端字節(jié)序存儲)模式:是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù)據(jù)的高位,,保存在內(nèi)存的高地址中。
百度2015年系統(tǒng)工程師筆試題:
請簡述大端字節(jié)序和小端字節(jié)序的概念,設(shè)計一個小程序來判斷當前機器的字節(jié)序。
#include
int main()
{
int a = 1;
char *pc=(char*)&a;//取出第一個字節(jié)的內(nèi)容放在pc的指針中
if (*pc == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
2.3 練習(xí)
1.下面程序輸出什么?
#include
int main()
{
char a = -1;

//10000000000000000000000000000001 - 原碼
//11111111111111111111111111111110 - 反碼
//11111111111111111111111111111111 - 補碼
//11111111 - a 截斷
//11111111111111111111111111111111 - 提升(char a的高位有符號位)(補碼)
//a=-1
signed char b = -1;
//10000000000000000000000000000001 - 原碼
//11111111111111111111111111111110 - 反碼
//11111111111111111111111111111111 - 補碼
//11111111 - b 截斷
//11111111111111111111111111111111 - 提升(signed char b有符號位)(補碼)
//b=-1
unsigned char c = -1;
//10000000000000000000000000000001 - 原碼
//11111111111111111111111111111110 - 反碼
//11111111111111111111111111111111 - 補碼
//11111111 - c 截斷
//00000000000000000000000011111111 - 提升(unsigned char c無符號位)(補碼、反碼、原碼)
//c=255
printf("a=%d,b=%d,c=%d", a, b, c);
//%d打印有符號的整數(shù),打印的是原碼
//a=-1;
//b=-1;
//c=255;
return 0;
}
2.下面程序輸出什么?
#include
int main()
{
char a = -128;
//10000000000000000000000010000000 - 原碼
//11111111111111111111111101111111 - 反碼
//11111111111111111111111110000000 - 補碼
//10000000 -(a) 截斷
//11111111111111111111111110000000 - 提升(補碼)
printf("%u\n", a);
//%u - 打印無符號10進制整數(shù)
// 是正數(shù),原返補相同,直接打印提升后的值
//a=4294967168
return 0;
}
3.下面程序輸出什么?
#include
int main()
{
char a = 128;
//00000000000000000000000010000000 - 原碼
//01111111111111111111111101111111 - 反碼
//01111111111111111111111110000000 - 補碼
//10000000 -(a) 截斷
//11111111111111111111111110000000 - 提升(補碼)
printf("%u\n", a);

//%u - 打印無符號10進制整數(shù)
// 是整數(shù),原返補相同,直接打印提升后的值
//a=4294967168
return 0;
}
4.下面程序輸出什么?
#include
int main()
{
int i = -20;
//10000000000000000000000000010100 - 原碼
//11111111111111111111111111101011 - 反碼
//11111111111111111111111111101100 - 補碼
unsigned int j = 10;
//00000000000000000000000000001010 - 原碼、反碼、補碼
//i+j 補碼相加
// 11111111111111111111111111101100
// +
// 00000000000000000000000000001010
//= 11111111111111111111111111110110 - 補碼
// 10000000000000000000000000001010 - 原碼
printf("%d\n", i + j);
//i+j=-10;
return 0;
}
5.下面程序輸出什么?
#include
int main()
{
unsigned int i;
//i為無符號數(shù),所以i>=0恒成立
//該程序為死循環(huán)
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
6.下面程序輸出什么?
#include
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
//-1 -2 -3 ...-128 127 126 ...3 2 1 0 -1 -2...依次循環(huán)
//128+127
printf("%d", strlen(a));//'\0'停止
//結(jié)果為:255
return 0;

}
7.下面程序輸出什么?
#include
unsigned char i = 0;
//char為無符號,取值范圍0~255
int main()
{
//所以i<=255恒成立,打印結(jié)果死循環(huán)
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
3. 浮點型在內(nèi)存中的存儲
常見的浮點數(shù):
3.14159
1E10
浮點數(shù)家族包括: float、、long 類型。
浮點數(shù)表示的范圍:float.h中定義
3.1 一個例子
浮點數(shù)存儲的例子:
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é)果是:
為了弄清楚結(jié)果,要了解浮點數(shù)存儲規(guī)則。
3.2 浮點數(shù)存儲規(guī)則
num 和 * 在內(nèi)存中明明是同一個數(shù),為什么浮點數(shù)和整數(shù)的解讀結(jié)果會差別這么大?
要理解這個結(jié)果,一定要搞懂浮點數(shù)在計算機內(nèi)部的表示方法。
詳細解讀:
根據(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.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。
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 IEEE 754規(guī)定,在計算機內(nèi)部保存M時,默認這個數(shù)的第一位總是1,因此可以被舍去,只保存后面的部分。比如保存1.01的時候c 數(shù)據(jù)儲存類型,只保存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位浮點數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字。
至于指數(shù)E,情況就比較復(fù)雜。
首先c 數(shù)據(jù)儲存類型,E為一個無符號整數(shù)( int)
這意味著,如果E為8位,它的取值范圍為0~ 255;如果E為11位,它的取值范圍0~2047。但是,我們知道,科學(xué)計數(shù)法中的E是可以出現(xiàn)負數(shù)的,所以IEEE 754規(guī)定,存入內(nèi)存時E的真實值必須再加上一個中間數(shù),對于8位的E,這個中間數(shù)是127;對于11位的E,這個中間數(shù)是1023。比如,2^10的E是10,所以保存成32位浮點數(shù)時,必須保存成10+127=137,即。
然后,指數(shù)E從內(nèi)存中取出還可以再分成三種情況:
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,表示為:
,而尾數(shù)1.0去掉整數(shù)部分為0,補齊0到23位0,則其二進
制表示形式為: 0
E全為0
這時,浮點數(shù)的指數(shù)E等于1-127(或者1-1023)即為真實值,有效數(shù)字M不再加上第一位的1,而是還原為0.的小數(shù)。這樣做是為了表示±0,以及接近于0的很小的數(shù)字。
E全為1
這時,如果有效數(shù)字M全為0,表示±無窮大(正負取決于符號位s)
解釋前面的題目:
#include
int main()
{
int n = 9;
//00000000000000000000000000001001 - 9的補碼
//0 00000000 00000000000000000001010
//(-1)^0 * 0.00000000000000000001010 * 2^-127
//0.000000
float* pFloat = (float*)&n;
printf("n的值為:%d\n", n);//9
printf("*pFloat的值為:%f\n", *pFloat);//0.000000
*pFloat = 9.0;
//1001.0
//(-1)^0 * 1.001 * 2^3
//s=0;
//e=3;
//m=1.001
//0 100000010 00100000000000000000000
//010000001000100000000000000000000
printf("num的值為:%d\n", n);//1091567616
printf("*pFloat的值為:%f\n", *pFloat);//9.0
return 0;
}
下面,讓我們回到一開始的問題:為什么 還原成浮點數(shù),就成了 0. ?
首先,將 拆分,得到第一位符號位s=0,后面8位的指數(shù) E= ,最后23位的有效數(shù)字M= 0。
9 -> 0 0 0000 1001
由于指數(shù)E全為0,所以符合上一節(jié)的第二種情況。因此,浮點數(shù)V就寫成:
V=(-1)^0 × 0.1×2 ^ (-126)=1.001×2 ^ (-146)
顯然,V是一個很小的接近于0的正數(shù),所以用十進制小數(shù)表示就是0.
再看例題的第二部分。
請問浮點數(shù)9.0,如何用二進制表示?還原成十進制又是多少?
首先,浮點數(shù)9.0等于二進制的1001.0,即1.001×2^3
9.0 -> 1001.0 ->(-1) ^ 01.0012^3 -> s=0, M=1.001,E=3+127=130
那么,第一位的符號位s=0,有效數(shù)字M等于001后面再加20個0,湊滿23位,指數(shù)E等于3+127=130,即。
所以,寫成二進制形式,應(yīng)該是s+E+M,即
0 0
這個32位的二進制數(shù),還原成十進制,正是 。