昨天碰到一個很奇怪的問題,首先來看這段代碼:
1 #include<stdio.h> 2 int main(int argc,char *argv[]) 3 { 4 long num1 = 203879; 5 long long num2 = 203879; 6 7 long long res1 = num1 * num1; 8 long long res2 = num2 * num2; 9 10 printf("res1 = %lld\n",res1); 11 printf("res2 = %lld\n",res2); 12 13 return 0; 14 }
程序的運行結果如下:
這裡感覺很奇怪,203879並沒有超過4個字節的范圍,但是它的平方超過了,於是我把它的結果存放在一個8字節數中,為什麼最終結果還是顯示溢出了呢?
然後我又寫了一段程序,把它的匯編代碼拿出來分析了一下?程序如下:
1 int main(int argc,char *argv[]) 2 { 3 long muln = 203879; 4 long long mulnl = 203879; 5 6 long long num1 = 203879 * 203879; 7 long long num2 = muln * muln; 8 long long num3 = mulnl * mulnl; 9 10 return 0; 11 }
這裡我分成三種情況,一種是直接的一個整數當乘數,一個是long型的整數當乘數,還有一個是long long型的整數當作乘數,然後分別計算他們的平方,我用gdb調試的結果如下:
其中前兩種情況都溢出了,只有第三種情況正常。然後我們再來查看一下他們的匯編代碼,這是我用objdump反匯編出來的匯編代碼:
1 int main(int argc,char *argv[]) 2 { 3 8048394: 55 push %ebp 4 8048395: 89 e5 mov %esp,%ebp 5 8048397: 83 e4 f8 and $0xfffffff8,%esp 6 804839a: 83 ec 30 sub $0x30,%esp 7 long muln = 203879; 8 804839d: c7 44 24 0c 67 1c 03 movl $0x31c67,0xc(%esp) 9 80483a4: 00 10 long long mulnl = 203879; 11 80483a5: c7 44 24 10 67 1c 03 movl $0x31c67,0x10(%esp) 12 80483ac: 00 13 80483ad: c7 44 24 14 00 00 00 movl $0x0,0x14(%esp) 14 80483b4: 00 15 16 long long num1 = 203879 * 203879; 17 80483b5: c7 44 24 18 71 b1 90 movl $0xad90b171,0x18(%esp) 18 80483bc: ad 19 80483bd: c7 44 24 1c ff ff ff movl $0xffffffff,0x1c(%esp) 20 80483c4: ff 21 long long num2 = muln * muln; 22 80483c5: 8b 44 24 0c mov 0xc(%esp),%eax 23 80483c9: 0f af 44 24 0c imul 0xc(%esp),%eax 24 80483ce: 89 c2 mov %eax,%edx 25 80483d0: c1 fa 1f sar $0x1f,%edx 26 80483d3: 89 44 24 20 mov %eax,0x20(%esp) 27 80483d7: 89 54 24 24 mov %edx,0x24(%esp) 28 long long num3 = mulnl * mulnl; 29 80483db: 8b 44 24 14 mov 0x14(%esp),%eax 30 80483df: 89 c1 mov %eax,%ecx 31 80483e1: 0f af 4c 24 10 imul 0x10(%esp),%ecx 32 80483e6: 8b 44 24 14 mov 0x14(%esp),%eax 33 80483ea: 0f af 44 24 10 imul 0x10(%esp),%eax 34 80483ef: 01 c1 add %eax,%ecx 35 80483f1: 8b 44 24 10 mov 0x10(%esp),%eax 36 80483f5: f7 64 24 10 mull 0x10(%esp) 37 80483f9: 01 d1 add %edx,%ecx 38 80483fb: 89 ca mov %ecx,%edx 39 80483fd: 89 44 24 28 mov %eax,0x28(%esp) 40 8048401: 89 54 24 2c mov %edx,0x2c(%esp) 41 8048405: 89 44 24 28 mov %eax,0x28(%esp) 42 8048409: 89 54 24 2c mov %edx,0x2c(%esp) 43 44 return 0; 45 804840d: b8 00 00 00 00 mov $0x0,%eax 46 }
首先來看num1的代碼(16~20行),203879(31C67H)平方為41566646641(9AD90B171H),編譯器直接把這個結果計算了出來,然後取出結果的4個字節,存放到了num1中,然後再用符號位來填充高4字節。
接下來我們再來看num2的代碼(21~27行),首先它把203879存放到eax裡面,再把相乘的平方結果存放到eax裡面,由於eax是32位,所以存放的時候就捨去了高4位,只存放了低4個字節。接下來做的就是判斷這個數的符號位是什麼,然後再用移位運算得到32個1存放在edx裡面,最後再把這個edx的值存放到num2的高四個字節裡面。
OK,通過上面這段匯編代碼分析,我們再來從C語言的概念上來分析這句代碼:
long long num2 = muln * muln ;
首先muln是一個4字節的整數,然後muln * muln得到的結果也是一個四字節的整數(這裡產生了溢出),然後再把這個結果轉換成8字節的整數,存放到num2中。所以我們最終得到的結果也是一個溢出的結果。
分析完了之後,發現我這是捨進求遠啊,現在也不知怎麼了,遇到點啥就喜歡反匯編出來看看。。。