剖析C语言自增运算规律

来源 :科技创新导报 | 被引量 : 0次 | 上传用户:love_day
下载到本地 , 更方便阅读
声明 : 本文档内容版权归属内容提供方 , 如果您对本文有版权争议 , 可与客服联系进行内容授权或下架
论文部分内容阅读
  摘 要:C语言中提供的自增运算符能让程序的书写更加简便和灵活,但如果运用不当也会使 程序的运行结果与预期大相径庭,再者,现在的教程中并未从不同编译器的编译执行角度进行深入剖析,使得初学者对一些语句有诸多困惑。本文通过实验,利用不同的环境编写调试程序,并对运行结果进行反汇编分析,总结出自增运算符在不同编译环境中的运算规律以及在教学过程中的注意事项。
  关键词:C语言 自增 反汇编
  中图分类号:TN925 文獻标识码:A 文章编号:1674-098X(2017)09(c)-0143-03
  Abstract: The auto-increasing operator in C language can make writing more convenient and flexible, but it can also make the result different with expected if used undeserved, moreover, the tutorials do not analyze deeply in the angle of different compliers’ comping and executing, and that makes beginners be confused with some codes. This article analyze the result with disassembling in different compliers, then conclude the rules of auto-increasing and notices in the teaching process.
  Key Words: C language; Auto-increasing; Disassemble
  C语言历经了几十年,成为经典的编程语言,凭借语句的精炼、语法的灵活、对系统环境要求低、拥有较高的执行效率而风靡整个世界,再经过历代改进,C语言发展至今非但未被淘汰,更是广泛的应用于各种生产环境中,并且早已成为计算机专业学生的入门语言。
  作为语句精炼的语言,比早期的汇编语言简单了很多,一条语句能表示多个运算步骤或者多次寄存器变化,例如自增语句i++,能够在参加其他运算的同时对本身值进行增加1而又不用增加额外的语句。但是如此精炼的语句却在实际生产环境中会产生歧义导致严重的后果,这么说并不是说自增语句是洪水猛兽,而是由于C语言应用的场景太广泛,早期发展标准尚未成形,不同编译器厂商在语句执行顺序上尤其是复杂语句不尽相同,例如自增语句并列使用的时候,不同编译器对它的解析方式会有不同:(++i)+(++i)+(++i),在不同的编译器计算出的结果是不同的:在VC6.0中结果为10;在VC2012中结果为12;在TurboC中结果是9。那为什么会出现如此大的结果差异呢?首先了解一下自增运算符的运算规则。
  1 自增运算符的运算规则
  自增运算的规则:自增运算符是一元运算符,它的作用是使变量的值增1。运算符出现在变量的前面称为前缀运算符,运算符出现在变量的后面称为后缀运算符。前缀自增运算符在程序执行过程中遵循“先自增后运算”的规则,后缀自增运算符遵循“先运算后自增”的规则。同理,自减运算符也是一样[1]。下面用两个简单的实验来实际运行一下自增运算符:
  实验1:
  int main( ){
  int a=8,b;
  b=a++; /*后缀形式*/
  printf(“a=%d, b=%d ”,a,b);
  return 0;
  }
  实验2:
  int main( ){
  int a=8,b;
  b = ++a; /*前缀形式*/
  printf(“ a=%d,b=%d”,a,b);
  return 0;
  }
  在实验1中,b = a++; 相当于如下两条语句:b = a; 和a=a+1; 即先将a的初始值8赋值给b,此时b的值即为8,然后a自增1。执行结果是:a=9,b=8。
  在实验2中,b = ++a; 相当于如下两条语句:a = a+1; 和 b=a; 即a先做自增1操作,然后再将a自增后的值9赋值给b,此时b的值即为9。执行结果是:a=9,b=9。
  也就是说对于自增符号++在变量后面的操作 a++,是先做其他运算,再对a进行自增运算;而自增符号++在变量前面的操作++a,是先对a进行自增运算,再做其他运算[2]。
  上面这两种自增操作,是自增操作的最基础版本,而在很多教学资料中会出现类似于求 (i++)+(i++)+(i++) 、++i * ++i / i++ ; 、 (++i)+(++i)+(++i) 等等计算结果的题目分析,由于绝大多数资料中分析的方法不同,且都从语法的角度来进行解析,而非从编译器的操作流程入手,从而给出的解释各异,本文就针对两个不同的编译平台对这段程序进行分析。
  在当前的C语言教学中,教师们最常用的编译器是微软公司出品的VC6.0,VS2012或者更新的版本,而由于编译器内部对C语言的语法解析有些不同,导致上例:当i=1的时候,(++i)+(++i)+(++i) 在不同的编译器上得到的结果完全不同,本文针对这个式子,在VC6.0 和 VS2012 两个平台上的具体计算过程做一个对比。
  用来做分析的式子为:(++i)+(++i)+(++i),写一个简单的示例程序以使其能在上述两个编译器中能运行:
  int main() {   int i = 1;
  int result = (++i)+(++i)+(++i);
  printf(“result=%d\n”,result);
  return 0;
  }
  运行结果:
  在两款编译器中计算得到的结果分别是10和12。
  接下来在两款编译器中分别启动单步调试,然后右键选择查看反汇编,我们能看到每一条语句在汇编中的执行顺序是如何的:
  首先看示例程序在VC6.0中的解析过程,我们重点看 int result=(++i)+(++i)+(++i);这条语句下面的反汇编代码:
  通过查看上面的反汇编代码,可以感受到一个普通的C语言语句:int result=(++i)+(++i)+(++i);在具体的运算过程中会执行多少操作,凝练了多少步骤,如果是纯粹的机器语言,步骤还会更多,从此可见C语言如此精炼。
  言归正传,我们通过对上面反汇编代码的分析,能看出来编译器是将自增操作执行两次之后(变量i从1变到3),再对这两个数进行了相加运算,得到结果为6,之后再对变量i做了一次自增操作(变量i从3变到4),再同之前的结果6进行了相加运算,结果为10。
  其中[ebp-4] 代表的是变量i,eax、ecx、edx是寄存器,运算过程简析:
  1:将变量i的值存到寄存器eax中,此时寄存器eax值为1。
  2:将寄存器eax中的值加1存到寄存器eax中,此时寄存器eax值为2 。
  3:将寄存器eax中的值传递给变量i,此时变量i的值是2。
  4:将变量i的值存放到寄存器ecx中,寄存器ecx此时为2。
  5:将寄存器ecx中的值加1存到寄存器ecx中,此时寄存器ecx值为3。
  6:将寄存器ecx中的值传递给变量i,此时变量i的值是3。
  7:将变量i的值存到寄存器edx中,此时寄存器edx值为3。
  8:将变量i的值同寄存器edx中的值相加,存放到寄存器edx中,此时寄存器edx值为6。
  9:将变量i的值存到寄存器寄存器eax中,此时寄存器eax值为3。
  10:将寄存器eax中的值加1存到寄存器eax中,此时寄存器eax值为4。
  11:将寄存器eax中的值传递给变量i,此时变量i的值是4。
  12:将变量i的值同寄存器edx中的值相加存放到寄存器edx中,此时寄存器edx中的值是10。
  13:将寄存器 edx中的值传递到变量i中,变量i的值为10,最后再赋值给变量result,变量result的值就是10 。
  接下来我们看VS2012中是如何解析的:
  在int result=(++i)+(++i)+(++i);这条语句下面的反汇编代码如下:
  通过上面反汇编代码的查看,能看出来编译器是将自增操作执行三次之后(变量i从1变到4),再对这三个数进行了相加运算,得到结果为12。
  [i]代表的是变量i(同VC6.0中的[ebp–4]是相同的含义),eax、ecx、edx是寄存器,运算过程简析:
  1:将变量i的值存到寄存器eax中,此时寄存器eax值为1。
  2:将寄存器eax中的值加1存到eax中,此时寄存器eax值为2 。
  3:将寄存器eax中的值传递给变量i,此时变量i的值是2。
  4:将变量i的值存放到寄存器ecx中,寄存器ecx此时为2。
  5:将寄存器ecx中的值加1存到寄存器ecx中,此时寄存器ecx值为3。
  6:将寄存器ecx中的值传递给变量i,此时变量i的值是3。
  7:将变量i的值存到寄存器edx中,此时寄存器edx值为3。
  8:将寄存器edx中的值加1存到寄存器edx中,此时寄存器edx值为4。
  9:将寄存器edx中的值传递给变量i,此时变量i的值是4。
  10:将变量i的值传递给寄存器eax,此时寄存器eax值为4。
  11:将变量i的值同寄存器eax中的值相加存放到寄存器eax中,此時寄存器eax的值是8。
  12:将变量i的值同寄存器eax中的值相加存放到寄存器edx中,此时寄存器eax中的值是12。
  13:将寄存器eax中的值传递到变量result中,变量result的值就是12。
  2 结语
  通过前面两个实验,我们能看出来,不同版本的编译器,在汇编的过程对程序语法做了不同的解析,这个是我们编程人员无法控制的,并且C语言最主要的应用领域就是硬件程序开发、驱动程序开发、编解码、算法优化方向的开发,在这些开发中,由于程序逻辑相对复杂,如果我们在编写程序的过程中过分简化程序行数,使用优先级不明确或者是不同编译器不明确的运算方法,会导致很严重的错误,类似于上例的程序,在程序出现异常的情况下,开发人员往往很难定位错误出现的位置以及原因,会极大的浪费时间,作为教师应该要求学生养成良好的编程习惯,用科学、严谨的态度对待遇到的问题,在熟练掌握基规律之后,编写程序之时,有选择地小心谨慎地使用自增(自减)运算符来简化程序,在易错的地方可用其他方法来代替,从而保证程序的执行万无一失[3]。
  参考文献
  [1] 张秀建.试析C语言中的自增自减运算符[J].电脑编程技巧与维护,2017(11):22-24,64.
  [2] 阚钿玉.C语言中自增(自减)运算符号的应用于分析[J].现代计算机,2016(15):40-43.
  [3] 唐婷,吕浩音.C语言自增(自减)运算符运算规律的探讨[J].陇东学院学报,2016(5):8-11.
其他文献
非营利法人并非空穴来风,而是有着非常悠久的立法传统,也有着比较丰厚的法律资源。我国《民法典》规定的非营利法人制度,存在着从管理性公法规范向治理性私法规范转换的难题,
从新自由制度主义的复合相互依赖、合作以及国际制度等理论要素出发,可以理解人类命运共同体话语构建与传播的基石、路径及其保障机制。全球新冠疫情发生以来,人类命运共同体
摘要:对于中职教师来说,激发学生的课堂兴趣是现在教育工作的关键,中职计算机教学中如何培养学生学习兴趣成为教师们的讨论焦点。借助多元化的教学方式,培养学生的学习兴趣,发挥学生学习的主观能动性,让学生在探索、实践中自主地寻找计算机学习的乐趣,提升课堂的效率。  关键词:中职 计算机教学 学习兴趣 策略     在中职计算机的教学上,要想提升学生的学习兴趣,首先要确定学生的课堂主体地位,这样学生才能发挥
高校课题,是指教育教学效率或效果能够有相当高的目标达成的课堂,教师都希望自己的每节课都是高效课堂,那如何构建高效课堂呢?笔者结合自己的教学经验,提出一些构建高效课堂
本文在对采空区巷道工作面围岩的破损机理与控制对策研究上,利用雷达、窥视仪设备等进行采空区围岩破损规律进行探测观察,通过对围岩破损相关数据获得围岩破损的机理,根据破
摘 要:煤矿是我国重要的矿产资源,不仅对我国经济的发展起着至关重要的作用,还是工业生产中必不可缺的重要能源。由于煤矿大多深埋地下,这就需要矿工深入到地底进行煤矿的挖掘,矿井自身具有一定的封闭性,虽然有排风口,但是依旧存在较大的作业隐患,尤其是在供电系统方面。煤矿的开采需要完善的煤矿供电系统,为矿工的地下作业提供照明条件,然而井下作业的方式,存在较多的易燃易爆的气体,极易发生矿井事故。该文主要研究如
工程概况某高速公路改建工程按照原路线走向双侧各加宽8m,将原有双向四车道高速公路扩宽改建为双向八车道高速公路。由于新旧道路施工时间相差达12年之久.旧路基固结沉降已基本
"1+1=2"是正确的吗?在数学逻辑中答案是肯定的。然而,在许多方面,"1+1=2"的观点却未必永恒。例如,2020年7月1日,美卓公司和奥图泰公司合并,创建了一家设备、服务和工艺技术领
在新课程改革的背景下,如何通过议题式教学更好地发挥小学数学的学科价值是数学教师思索的问题。小学数学教学方式 多样,如何在引入议题式教学提升教学效果和激发学生学习积
近年来,随着经济的发展,政府对公路建设的投资力度加大.繁重的交通对公路质量的要求也越来越高。粉粘土是塑性指数介于10~17之间.且粉粒组含量大于砂粒组合量的粘性土.普遍存在与我