zoukankan      html  css  js  c++  java
  • 从一个乘法来分析C语言

      昨天碰到一个很奇怪的问题,首先来看这段代码:

     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
    ",res1);
    11     printf("res2 = %lld
    ",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中。所以我们最终得到的结果也是一个溢出的结果。

      分析完了之后,发现我这是舍进求远啊,现在也不知怎么了,遇到点啥就喜欢反汇编出来看看。。。

  • 相关阅读:
    Java实现蓝桥杯 算法提高 线段和点
    Java实现蓝桥杯 算法提高 线段和点
    Java实现蓝桥杯 算法提高 线段和点
    Java实现第八届蓝桥杯国赛 数字划分
    Java实现第八届蓝桥杯国赛 数字划分
    Java实现第八届蓝桥杯国赛 数字划分
    Java实现第八届蓝桥杯国赛 数字划分
    Java实现第八届蓝桥杯国赛 数字划分
    Java实现蓝桥杯-算法提高 P1003
    必须得是一万小时的 刻意训练(deliberate practice)
  • 原文地址:https://www.cnblogs.com/bwangel23/p/4274391.html
Copyright © 2011-2022 走看看