从一个乘法来分析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\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中。所以我们最终得到的结果也是一个溢出的结果。

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

时间: 2024-08-29 20:38:23

从一个乘法来分析C语言的相关文章

Go将统治下一个10年?Go语言发展现状分析

"本文是国内Go语言大中华区首席布道师--许式伟,在QCon2015上海站上的分享.他预测Go语言10年内一定会超过C和java,并且统治这一个10年. Go语言语法及标准库变化 Go从1.0版本到现在(2015年)已经有三年多的时间,大的版本发布了五个,下面大家一起看看每个大版本分别都改了什么,当然这里不可能把所有的细节都提到,但我认为重要的会提出来. 首先是Go1.1,Go1.0于2012年4月发布,此后基本维持了每半年发布一个新版本的时间间隔.Go1.5比较例外,在Go1.5的拖累下,Go

C++写一个简单的解析器(分析C语言)

该方案实现了一个分析C语言的词法分析+解析. 注意: 1.简单语法,部分秕.它可以在本文法的基础上进行扩展,此过程使用自上而下LL(1)语法. 2.自己主动能达到求First 集和 Follow 集. 3.处终结符外(有些硬编码的成分),终结符的文法能够自己定义,也就是说读者能够自己定义文法. 4.为方便理解.C语言的文法描写叙述写成中文. 5.程序将词法分析和语法分析结合起来.词法分析的结果作为语法分析的输入. 6.终于结果在控制台显示的有:词法分析.First集.Follow集.Select

《C专家编程》第三章——分析C语言的声明

前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解.现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其次就是学习怎样对C语言的声明进行分析,另外本文将详细来探讨一个分析C语言声明的工具--cdecl,分析和编写它的源代码. C语言的声明晦涩难懂这一点应该是名不虚传的,比如说下面这个声明: void (*signal(int sig, void(*func) (int)))(int); 这可不是吓人的

一个轻客户端,多语言支持,去中心化,自动负载,可扩展的实时数据写服务的实现方案讨论

背景 背景是设计一个实时数据接入的模块,负责接收客户端的实时数据写入(如日志流,点击流),数据支持直接下沉到HBase上(后续提供HBase上的查询),或先持久化到Kafka里,方便后续进行一些计算和处理,再下沉到文件系统或做别的输出. 在设计中,对于客户端和服务端有这么些目标. 客户端需要支持多语言(Java,C++),做得尽量轻量级,只要连上服务端的ip:port,以RPC的形式调用简单的write就可以把数据写出去.客户端不承担任何逻辑的处理,服务端的负载均衡对客户端是透明的. 服务端想要

用node.js对一个英语句子分析页面进行一个小爬虫

最近遇到一个需求,就是要从一个英语句子分析的页面中,根据你输入的英语从句,点击开始分析按钮,这个页面就会将分析的结果解析出来,如 然后我们就是需要从这个页面中把这些解析好的数据(包括句子语法结构详解,句子相关词汇解释等)取出来,这时候我就想到之前学过node.js,这时候就来弄下node.js的小小的爬虫. 首先,电脑要先安装node.js,至于怎么安装,请google,或者找相关教程来看. 然后就需要了解下node,现在我先加载http模块,然后设置url的值,url就是你要爬的那个网页的地址

阿庆SQL智能查询分析器,使用delphi开发的一个数据库查询分析管理工具.分享给大家

为方便自己工作,使用delphi开发的一个数据库查询分析管理工具.分享给大家,具体以下特点: 1.由于使用ADO连接,理论支持SQL Server.Access.MySQL.Oracle等所有数据库 2.支持SQL关键词自动提示 3.支持表名自动提示 4.支持表字段自动提示 5.支持SQ关键词.表名.表字段不同颜色显示 6.支持SQL语句注释(包括ACCESS) 7.支持选择部分文字执行SQL语句 8.查询结果支持增加.修改.编辑 9.绿色程序无附加文件,只有一个文件即可运行,文件大小只有400

python写一个乘法表的脚本

学习脚本的时候经常会被问到会不会写一个99乘法表,现在就用python语句简单写一个乘法表 [[email protected] python_py]# cat while3.py i = 1 while (i<=9):        j=1        while(j<=i):               printj,"x",i,"=",j*i,"\t",               j=j+1        print&quo

一个页面根据访问者的语言或国家来呈现不同的翻译版本

JSP 国际化 在开始前,需要解释几个重要的概念: 国际化(i18n):表明一个页面根据访问者的语言或国家来呈现不同的翻译版本. 本地化(l10n):向网站添加资源,以使它适应不同的地区和文化.比如网站的印度语版本. 区域:这是一个特定的区域或文化,通常认为是一个语言标志和国家标志通过下划线连接起来.比如"en_US"代表美国英语地区. 如果想要建立一个全球化的网站,就需要关心一系列项目.本章将会详细告诉您如何处理国际化问题,并给出了一些例子来加深理解. JSP容器能够根据reques

《C专家编程》笔记(三)——分析C语言的声明

1. 几个C语言声明的分析 char (*j)[20]; j = (char(*)[20]) malloc(20); // j是指向数组的指针 const int * grape; int const * grape; int * const grape_jelly; const int * const grape_jam; int const * const grape_jam; char * const * (*next)(); // next是指向函数的指针,这个函数不接收参数,它的返回值