-
進制
二進制以0b或0B開頭,如0b001。(數字0不是字母O)
八進制以0開頭,如067。
十六進制以0x或0X開頭,如0x48B。
-
打印一個數的八進制和十六進制:
int a = 13; printf("10->8: %o\n", a); printf("10->16: %x\n", a);
-
定義一個二進制數、八進制數、十六進制,打印其對應的10 進制
int a = 0b00000000000000000000000000001101; printf("2->10: %d\n", a); a = 015; printf("8->10: %d\n", a); a = 0xd; printf("16->10: %d\n", a); 輸出結果: 2->10: 13 8->10: 13 16->10: 13
進制轉換
-
原碼,反碼,補碼
正數的原碼,反碼,補碼都一樣。三碼合一。
負數的第一位為符號位,反碼是原碼取反,補碼是反碼加一。
正數和負數在計算機的內存中都以補碼的形式存在。
對于負數, 補碼表示方式也是人腦無法直觀看出其數值的。 通常也需要轉換成原碼在計算其數值。
-
為什么要引入反碼和補碼?
現在我們知道了計算機可以有三種編碼方式表示一個數. 對于正數因為三種編碼方式的結果都相同, 所以不需要過多解釋。
但是對于負數, 可見原碼, 反碼和補碼是完全不同的. 既然原碼才是被人腦直接識別并用于計算表示方式, 為何 還會有反碼和補碼呢?
首先, 因為人腦可以知道第一位是符號位, 在計算的時候我們會根據符號位, 選擇對真值區域的加減。但是對于計算機, 加減乘數已經是最基礎的運算, 要設計的 盡量簡單. 計算機辨別"符號位"顯然會讓計算機的基礎電路設計變得十分復雜! 于是人們想出了 將符號位也參與運算的方法. 我們知道, 根據運算法則減去一個正數等于加上一個負數, 即: 1-1 = 1 + (-1) = 0 , 所以機器可以只有加法而沒有減法, 這樣計算機運算的設計就更簡單了.
-
例:
計算十進制的表達式: 1-1=0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原碼表示, 讓符號位也參與計算, 顯然對于減法來說, 結果是不正確的.這也就是為何計算 機內部不使用原碼表示一個數.
為了解決原碼做減法的問題, 出現了反碼:
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 (1111 1111,符號位不變,其他為逐位取反) = -0
發現用反碼計算減法, 結果的真值部分是正確的. 而唯一的問題其實就出現在"0"這個特殊的數值 上. 雖然人們理解上+0和-0是一樣的, 但是0帶符號是沒有任何意義的. 而且會有[0000 0000]原和 [1000 0000]原兩個編碼表示0。
于是補碼的出現, 解決了0的符號以及兩個編碼的問題:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]補 + [1111 1111]補 = [0000 0000]補 = [0000 0000]原
- 這樣0用[0000 0000]表示, 而以前出現問題的-0則不存在了.而且可以用[1000 0000]表示-128: (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]補 + [1000 0001]補 = [1000 0000]補
- -1-127的結果應該是-128, 在用補碼運算的結果中, [1000 0000]補 就是-128. 但是注意因為實際 上是使用以前的-0的補碼來表示-128, 所以-128并沒有原碼和反碼表示.(對-128的補碼表示[1000 0000]補算出來的原碼是[0000 0000]原, 這是不正確的)
-
位運算符
-
& 按位與
規律:二進制中,與1相&就保持原位,與0相&就為0。
-
應用場景:
按位與運算通常用來對某些位清0或保留某些位。例如把a的高位都清0,保留低八位,那么就a&255。
判斷奇偶: 將變量a與1做位與運算,若結果是1,則 a是奇數;若結果是0,則 a是偶數。
-
任何數和1進行&操作,得到這個數的最低位。
1001
&0001
=0001
?
-
想把某一位置0。
11111111
&11111011
=11111011
| 按位或
-
^ 按位異或
-
規律:
- 相同整數相的結果是0。比如55=0。
- 多個整數相的結果跟順序無關。比如567=576。因此得出結論:__ab^a = b__。
-
使用位運算實現交換兩個數的值:
a = a^b; b = b^a; a = a^b;
-
~ 取反
-
-
左移運算符和右移運算符
把整數a的各二進位全部左移n位,高位丟棄,低位補0。左移n位其實就是乘以2的n次方。由于左移是丟棄最高位,0補最低位,所以符號位也會被丟棄,左移出來的結果值可能會改變正負性。
把整數a的各二進位全部右移n位,保持符號位不變。右移n位其實就是除以2的n次方。為正數時, 符號位為0,最高位補0。為負數時,符號位為1,最高位是補0或是補1,取決于編譯系統的規定。
-
例:
寫一個函數把一個10進制數按照二進制格式輸出
-
分析:
13 -----> 0000 0000 0000 0000 0000 0000 0000 1101
0000 0000 0000 0000 0000 0000 0000 1101 13
0000 0000 0000 0000 0000 0000 0000 0001 1
每次取 一個數的最后一個二進制位
任何一個數和1進行&(按位與)得到任何一個數的二進制的最后
一位
-
實現:
int len = sizeof(int)*8; int temp; for (int i=0; i<len; i++) { temp = num; //每次都在原數的基礎上進行移位運算 temp = temp>>(31-i); //每次移動的位數 int t = temp&1; //取出最后一位 if(i!=0&&i%4==0)printf(" "); printf("%d",t); }
-
變量的存儲
一個變量所占用的存儲空間,不僅跟變量類型有關,而且還跟編譯器環境有關系。同一種類型的變量,在不同編譯器環境下所占用的存儲空間又是不一樣的。
任何變量在內存中都是以二進制的形式存儲。一個負數的二進制形式,其實就是對它的正數的二進制形式進行取反后再+1。
-
變量的首地址,是變量所占存儲空間字節地址最小的那個地址。(因為變量的字節的地址在內存中是由大到小的,以0b00000000 00000000 00000000 00001010為例,00001010屬于低字節,位于這個變量所占4個字節的最上面的那個字節,也就是地址最小的字節。類似Excel中的四個表格,低字節位于最上面(地址最小))。
?
-
類型說明符
在64位編譯器環境下:
short占2個字節(16位)
int占4個字節(32位)
long占8個字節(64位)
因此,如果使用的整數不是很大的話,可以使用short代替int,這樣的話,更節省內存開銷。
ANSI \ ISO制定了以下規則:
? short跟int至少為16位(2字節)
? long至少為32位(4字節)
? short的長度不能大于int,int的長度不能大于long
? char一定為為8位(1字節),畢竟char是我們編程能用的最小數據類型
- 32位編譯器:long long 占 8個字節, long 占 4個字節。
- 64位編譯器:long long 和 long 都是 8個字節。
-
- long long int == long long
- long int == long
- short int == short
?
-
char型數據存儲原理
- char a=‘a' ——> 取出'a'的ASCII碼值,97,然后轉換2進制,存儲在一個字節中。
- 個人理解,ASCII表就是為字符設計的,因為字符在內存中存儲時首先取出這個字符的ASCII碼值,然后轉換成二進制之后存儲。而且一個字符占一個字節,所以共8位,取值范圍是-27~27-1,所以ASCII表也是0~127。
-
數組
-
初始化數組
- int ages[3] = {4, 6, 9};
- int nums[10] = {1,2}; // 其余的自動初始化為0
- int nums[] = {1,2,3,5,6}; // 根據大括號中的元素個數確定數組元素的個數
- int nums[5] = {[4] = 3,[1] = 2}; // 指定元素個數,同時給指定元素進行初始化
- int nums[3]; nums[0] = 1; nums[1] = 2; nums[2] = 3; // 先定義,后初始化
- 定義但是未初始化,數組中有值,但是是垃圾值。
- 對于數組來說,一旦有元素被初始 化,其他元素都被賦值0。
-
計算數組中元素的個數
-
int count = sizeof(數組) / sizeof(數組[0]) // 數組的長度 = 數組占用的總字節數 / 數組元素占用的字節數
?
-
-
數組注意事項
-
在定義數組的時候[]里面只能寫整型常量或者是返回整型常量的表達式。
int ages['A'] = {19, 22, 33};
printf("ages[0] = %d\n", ages[0]);
int ages[5 + 5] = {19, 22, 33};
printf("ages[0] = %d\n", ages[0]);
int ages['A' + 5] = {19, 22, 33};
printf("ages[0] = %d\n", ages[0])
-
錯誤寫法。
-
沒有指定元素個數(int nums[] = {1,2,3,5,6}; 這樣是可以的,但是如果先聲明,并沒有初始化,則是錯誤的)
int a[]; // 錯誤
-
[]中不能放變量
int number = 10;
int ages[number]; // 不報錯, 但是沒有初始化, 里面是隨機值
-
-
-
> int number = 10;
>
> int ages[number] = {19, 22, 33} // 直接報錯
- > int ages10[5];
>
> ages10 = {19, 22, 33}; // 錯誤。只能在定義數組的時候進行一次性(全部賦值)的初始化
- 訪問數組越界。