1 unsigned int i=3; 2 cout<<i * -1;
第一反應:-3。不過結果似乎不是這樣的,寫了個程序,運行了一下,發現是:4294967293。
0到255,C語言中專門用兩個關鍵字來描述兩種表示方法,於是,就產生了一些不可思議的問題。
1、溢出
在有符號運算中可能會產生溢出問題,歸納起來就是:兩個整數相加可能會溢出,兩個負數相加也可能會溢出,一正一負相加肯定不會溢出。在《C深度剖析》中看到一個有趣的問題。
1 #include <stdio.h> 2 #include <string.h> 3 int main(void) 4 { 5 char a[1000]; 6 int k = 0; 7 for (; k < 1000; k++) 8 { 9 a[i] = -1-i; 10 11 } 12 printf("%d\n", strlen(a)); 13 return 0; 14 }
最終的結果是255。因為數組a[1000]是char類型的,在C語言中明確規定char類型占一個字節內存空間,且在x86的gcc平台上 char默認是signed,一開始,k=0,a[0]=-1,隨着k不斷增大,當k=127,則a[127]=-128,對應的二進制是 10000000,我們知道-128是編譯器能表示的最小值,當k=128,a[128]當然不可能存儲-129這個值了,因為最高位發生了溢出,所以在 計算機中儲存的補碼值是01111111,。隨着k繼續增大,當k=254時,a[254]在計算機中存放的補碼是00000001,而 k=255,a[255]對應的存儲值是00000000,即0,strlen函數遇到第一個0就停止,所有最后的結果是k從0到254,總共長度是 255。
2、signed和unsigned混合運算
C語言中除了char類型,編譯器默認其他整型都是signed,在x86的gcc平台上包括char在內所有整型都是signed。
1 #include <stdio.h> 2 3 int main(void) 4 { 5 unsigned a = 10; 6 unsigned b = -10; 7 if (a) printf("yes\n"); else printf("no\n"); 8 if (b) printf("yes\n"); else printf("no\n"); 9 10 int c = b; 11 printf("%d\n", c); 12 if (c) printf("yes\n"); else printf("no\n"); 13 14 int d = -20; 15 int e = a + d; 16 printf("%d\n", e); 17 if (e) printf("yes\n"); else printf("no\n"); 18 19 return 0; 20 }
最后結果是
yes
yes
-10
yes
-10
yes
從上面例子可以看出,在C語言中,有符號數可以賦值給無符號數,結果是一個無符號數,而無符號數也可以賦值給有符號數,結果還是一個無符號數;在混合運算中,只要有一個無符號數,都會將有符號數轉化成無符號數參加運算,結果也以無符號保存。
注意:signed 和 unsigned 在電腦中的存儲形式是一樣的,只是解釋方法不同,即一個有符號,一個無符號。
%d打印的是signed類型的,而%u才是打印的unsignd類型的,用不同的打印相當於一個類型轉換了。當然C++比較直觀,能直接自動識別出類型並打印值。
就用上面的例子:
unsigned c = 3; printf("%d\n",(c*(-1))); // 打印出的是-3 printf("%u\n",(c*(-1))); // 打印出的是4294967293