段落1. 百度百科char
char用於C或C++中定義字符型變量,只占一個字節,取值范圍為 -128 ~ +127(-2^7~2^7-1)
知識點:負數在計算機中以補碼存儲,而正數以原碼存儲。
為什么負數在計算機中以補碼存儲呢?
因為所以科學道理,可以看這篇知乎文章:https://www.zhihu.com/question/335614901/answer/755608419
不看也可以,只要知道負數在計算機中有用補碼來存儲的必要性就行了,而且這個必要性是有科學道理的。
段落2. 求正數在計算機中存儲的二進制值
請問 char j = 5 這句代碼中, j 在計算機中存的二進制值是多少?
正數以原碼存儲,5的原碼是0b0000 0101. 所以 j 在計算機中存的二進制值是 0b0000 0101 。
段落3. 求負數在計算機中存儲的二進制值
請問 char j = -12這句代碼中, j 在計算機中存的二進制值是多少?
負數以補碼存儲
先找對該負數對應的正數,即12。 12 的原碼是 0b0000 1100 .
我們將上述原碼的最高位置為1, 得到0b1000 1100.
然后再求補碼:
0b1000 1100 , 符號位不變,其余各位取反 =》0b11110011 再加1 =》得到0b11110100 。
我們最終理論計算得到0b11110100, 這等價於0xF4。
但是寫代碼驗證的時候還是有所區別,在%s、%c、%d、%x等這些輸出格式選項中,我們只能使用%x來打印出其十六進制的內存值。
而且%x會打印出4字節,並非只有1字節哦,所以,我們只需關注最低一個字節就行了。
下面是代碼:
#include <stdio.h> #include <string.h> #include <assert.h> #include <stdlib.h> #include <unistd.h>
int main(){ char j = -12; printf("%x \n", j); return 0; }
root@lmw-virtual-machine:/home/lmw/桌面/C_Text# ./ab
fffffff4
root@lmw-virtual-machine:/home/lmw/桌面/C_Text#
可以看到,最低字節是F4。
上面提到可以先找到該負數對應的正數12, 那么12的原碼可不僅僅是0b0000 1100 , 還可以是0b 0000 0000 0000 0000 0000 0000 0000 1100 (32位, 合計4字節),
相同的操作手法我們再來一遍,
我們將上述原碼的最高位置為1, 得到0b1000 0000 0000 0000 0000 0000 0000 1100 .
然后再求補碼:
符號位不變,其余各位取反 =》0b1111 1111 1111 1111 1111 1111 1111 0011, 再加1 =》得到 1111 1111 1111 1111 1111 1111 1111 0100 。
我們最終得到的0b1111 1111 1111 1111 1111 1111 1111 0100 則等價於 0xFFFFFF4。
0b1111 1111 1111 1111 1111 1111 1111 0100 和 0b11110100 都可以表示 -12 的計算機存儲值啊 , 那什么時候是前者,什么時候是后者呢?
取決於我們希望用什么方式去訪問這片內存。
如果是使用%x訪問, %x一定會訪問到4字節內容並以十六進制顯示, 那么得到的就是4字節的0xFFFFFFF4,
對應的是4字節的0b1111 1111 1111 1111 1111 1111 1111 0100,
即使用%x訪問時,12的原碼應該按照4字節來分析。
段落4. 超限處理
如果賦值的數是超過char類型的上限值的,那么先減去256的倍數, 再根據上面方法計算其在計算機中存儲的二進制值。
例如上圖,我們要計算500賦值給char,那么只需要計算500-256=244, 發現244還是大於char的取值上限,
那么再次244-256 = -12, 發現-12 在char類型范圍內,所以我們只需要計算-12在計算機中的存儲值即可,
這樣我們也能夠可以分析出使用%x打印變量a會得到怎樣的十六進制值了。
換句話說,這基於一個觀點: char a = 500; 等價於 char a = -12;
段落5. 不同符號類型數做加法時
知識點:當有符號數與無符號數一起運算時,會將有符號數轉換為無符號數!
下面的demo可以作為面試題使用。如果面試者只能解釋到34-12=22這種地步,那么顯然是非常錯誤的。
#include <stdio.h> #include <string.h> #include <assert.h> #include <stdlib.h> #include <unistd.h>
int main(){ char j = -12; // 0xFFFFFFF4 unsigned char x = 34; // 0xFFFFFFF4 + 0x22(十進制的34) ,得到0x1 0000 0016
printf("%x \n", j+x); // 0x1 0000 0016 超過了4字節了,截斷得到 0x00000016, 即0x16
unsigned int sum = j+x; printf("sum = %d \n", sum); // 0x16,即 22
return 0; }
補充點:
補充一個%x 和 %lx的實驗
#include <stdio.h> #include <string.h> #include <assert.h> #include <stdlib.h> #include <unistd.h>
int main(){ printf("%d, %d, %d \n", sizeof(char),sizeof(int),sizeof(unsigned long int)); char j = -12; printf("%x \n", j); printf("%lx \n", j); int j_int = -12; printf("%x \n", j_int); printf("%lx \n", j_int); unsigned long int j_long_int = -12; printf("%lx \n", j_long_int); printf("%x \n", j_long_int); return 0; }
root@lmw-virtual-machine:/home/lmw/桌面/C_Text# ./ab 1, 4, 8 fffffff4 fffffff4 fffffff4 fffffff4 fffffffffffffff4 fffffff4 root@lmw-virtual-machine:/home/lmw/桌面/C_Text#
結論:
%lx的特點:如果被打印的數據不足8字節,例如只有4字節或1字節,那么就只會打印4字節。如果被打印的數據有8字節,那么就會打印出8字節。
而%x一定會打印出恰好剛好4字節。
.