一.數據在內存中的存儲
1.整數在內存中的存儲
整數在內存中以二進制的形式儲存,分別為原碼,補碼,反碼
有符號的整數,在上述三種形式都有符號位和數值位兩個部分,符號位為0是正數,1是負數,最高位的一位是符號位,剩余的都是數值位
正整數的原碼,補碼,反碼都一樣
負整數的原碼補碼和反碼都各不相同
原碼:直接將數值按照正負數的形式變成二進制就是原碼
反碼:符號位不變,數值位取反
補碼:反碼+1
注意,整數存放數值的補碼形式
2.大小端字節序和字節判斷
實例一
2.1大小端字節序存儲
大端字節序存儲:將數據的低位字節放在內存的高位地址上,高位字節放在內存的低位地址上
小端字節序存儲:將數據的低位字節放在內存的低位地址上,高位字節放在內存的高位地址上
所以實例一是小端字節序存儲
2.2為什么有大小端字節序存儲
這是因為在計算機系統中,我們是以字節為單位的,每個地址單元都對應著?個字節,?個字節為8bit位,但是在C語?中除了8bit的char之外,還有16bit的 short 型,32bit的 long 型(要看具體的編譯器),另外對于位數?于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于?個字節,那么必然存在著?個如何將多個字節安排的問題,因此就導致了?端存儲模式和小端存儲模式。
例如:?個 16bit的 short 型 x ,在內存中的地址為0x0010, x 的值為 0x1122,那么0x11為?字節, 0x22 為低字節。對于?端模式,就將0x11放在低地址中,即 0x0010 中,0x22 放在?地址中,即 0x0011 中。
我們常用的 X86 結構是小端模式,而KEIL C51 為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端還是小端。
2.3練習
2.3.1
如何判斷系統是大端字節序存儲還是小端字節序存儲?
實例二
注釋:取數據的首地址進行char*形式的轉化,然后對其進行解應用看是否是1來進行判斷
2.3.2
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;
}
運算一下,算算答案,下列是運行結果
解析:
2.3.3
int main()
{char a = -128;printf("%u\n", a);return 0;
}
求a的打印數值是多少
運行結果以及解析:
2.3.4
int main()
{char a = 128;printf("%u\n", a);return 0;
}
求a經過無符號打印的數值是多少
下列是解析和結果:
2.3.5
int main()
{char a[1000];int i;for (i = 0; i < 1000; i++){a[i] = -1 - i;}printf("%d", strlen(a));return 0;
}
求出結果
下列是解析和運行結果
解析:char默認是有符號的char,也就是signed char類型,它的有效值是從-128到127,所以數值的變化是-1,-2,-3,-4,...-128,127,126,...0,-1,-2,....一直這么循環。strlen則是一直到\0就會停下,,所以最終結果是255。
2.3.6
unsigned char i = 0;
int main()
{for (i = 0; i <= 255; i++){printf("hello world\n");}return 0;
}
想想運行結果
下列是運行結果以及解析:
?解析:運行結果陷入無限循環,因為unsigned char類型范圍是從0~255,并且數值變化是循環變化,所以i的值是不會大于255,所以無限循環。
2.3.7
int main()
{unsigned int i;for(i = 9; i >= 0; i--){printf("%u\n",i);}return 0;
}
想想運算結果
下列是結果和解析:
解析:依舊是陷入了無限循環的結果,因為unsigend char的數值一定大于0,所以i<=0一直成立。
2.3.8
#include <stdio.h>
//X86環境 ?端字節序
int main()
{int a[4] = { 1, 2, 3, 4 };int *ptr1 = (int *)(&a + 1);int *ptr2 = (int *)((int)a + 1);printf("%x,%x", ptr1[-1], *ptr2);return 0;
}
想想運行結果
下列是運行結果:
3.浮點數在內存中的存儲
常見的浮點數:3.14159,1E10(意思是1.0*10**10)等,浮點數類型有float、double、long double類型
浮點數表示的范圍在float.h中定義
3.1引子
int main()
{int n = 9;float* pfloat = (float*)&n;printf("%d\n", n);printf("%f\n", pfloat);*pfloat = 9.0;printf("%d\n", n);printf("%f\n", pfloat);return 0;
}
想一下運行結果會是什么
上述的運行結果是否和你想的一樣呢
上一個例子說明了整數和浮點數在內存的存儲是不一樣的。
3.2浮點數的存儲
根據國際標準IEEE(電?和電??程協會)754,任意?個?進制浮點數V可以表示成下面的形式:
例如:十進制的10.5轉化成上述的形式的話,10.5化成二進制是1010.1,進而轉化成1.0101*2^3,所以最后的形式就是(-1)^0*1.0101*2^3
(S=0;M=1.0101;E=3)
所以對于浮點數的存儲,只需要存儲S,M,E的相關值就可以表示出浮點數
IEEE 754規定:
對于32位的浮點數,最高的1位存儲符號位S,接著的8位存儲指數E,剩下的23位存儲有效數字M
對于64位的浮點數,最高的1位存儲符號位S,接著的11位存儲指數E,剩下的52位存儲有效數字M
3.2.1浮點數存的過程
對于數字M
因為1<=M<2
IEEE754規定,在計算機內部保存M時,默認這個數的第?位總是1,因此可以被舍去,只保存后面的 xxxxxx部分。比如保存1.01的時候,只保存01,等到讀取的時候,再把第?位的1加上去。
這樣做的目的,是節省1位有效數字。以32位浮點數為例,留給M只有23位,將第?位的1舍去以后,等于可以保存24位有效數字。
對于數字E
首先,E是一個無符號整數
這就意味著,如果E為8位,它的取值范圍就是0~255;如果E為11位,則取值范圍是0~2047.
但是E的取值是可以為負數的,比如0.1中E就是-1,所以IEEE 754規定,存入內存時E的真實值必須再放入一個中間值,對于8位的E,中間值是127,對于11位的E,中間值是1023.
所以如果8位的E的真實值是5,則存入內存的值就是5+127=132,換算成二進制就是10000100
3.2.2浮點數取的過程
指數E從內存中取有三種情況
E不全為0或者不全為1(常規情況)
這時,浮點數就采用下面的規則表示,即指數E的計算值減去127(或1027),得到真實值,再在前面加上1作為第一位
E全為0
這時,浮點數的指數E等于1-127(或者1-1023)即為真實值,有效數字M不再加上第?位的1,而是還原為0.xxxxxx的小數。這樣做是為了表示±0,以及接近于0的很小的數字
E全為1
這時,如果有效數字M全為0,表示±無窮大(正負取決于符號位s)
例子:
int main()
{float a = 5.5f;//S=0//M=1.011//E=2return 0;
}
自己推算的話就是0 1000000?101100000000000000000000
整理一下就是 0100 0000 1011 0000 0000 0000 0000 0000
? ? ? ? ? ? ? ? ? ? ? ?40? ? ? ? ? ? ? ? ?b0? ? ? ? ? ? ? ? ?00? ? ? ? ? ? 00?
就是下面的結果