C | 一种需要特别留心的编程错误(++i) + (++i) + (++i)

简介: 诸如此类的表达式`(++i) + (++i) + (++i)`,很多学校都喜欢用在学生的期末考里,看似经典的考题,有没有可能本身就是错误的呢?这种错误并不是语法错误,是可以正常运行的,这就造成了“==它是正确的编程==”这种假象

在这里插入图片描述
啊我摔倒了..有没有人扶我起来学习....


@TOC


前言

诸如此类的表达式(++i) + (++i) + (++i),很多学校都喜欢用在学生的期末考里,看似经典的考题,有没有可能本身就是错误的呢?这种错误并不是语法错误,是可以正常运行的,这就造成了“==它是正确的编程==”这种假象


一、为什么(++i) + (++i) + (++i)是错误编程?

因为(++i) + (++i) + (++i)在不同编译器上有不同的结果,这取决于编译器本身对操作符优先级的实现方式不同。它在Linus环境下结果是10,而在VS环境下却是12。能造成这种模棱两可的结果,当然不能认为是正确的编程。

二、探究VS如何实现(++i) + (++i) + (++i)

因为每种编译器的实现方式不同,但都可以通过观察了解实现方式,这边用VS举个例子

1. 反汇编

  • 相信看了博主的《C | 函数栈帧的创建和销毁》的铁汁都很了解这套流程啦,如果没看过的铁汁想更深入地了解可以点击跳转过去
  • 废话不多说啦,咱们撸起袖子开干!
  • 进入反汇编前,需要先点击键盘F10F11(目前先不作区分,都一样)进入调试,然后在空白处用鼠标右键点击,呼出菜单后点击==转到反汇编==即可

在这里插入图片描述

2. 分析指令

  1. 前面部分(红色方框内)我们直接略过,都不是这次的重点,想了解的铁汁同样可以去《C | 函数栈帧的创建和销毁》

在这里插入图片描述

  1. 为了方便观察地址的变化,我们把显示符号名去掉

在这里插入图片描述

  1. 开始分析与(++i) + (++i) + (++i)的计算相关的指令:
    该行指令表示,把1赋值给地址为ebp-8的这个地方(其实就是i所在),于是i = 1
    注:ebp一种存地址的寄存器,也成为栈底指针;dword为双字,占4个字节

在这里插入图片描述
在这里插入图片描述

  1. 此行命令表示,把i的值赋给寄存器eax

在这里插入图片描述

  1. 然后eax1,此时eax中存着2

在这里插入图片描述

  1. 再把eax的值赋给i,此时i = 2,也就是i完成了第一次自加

在这里插入图片描述
在这里插入图片描述

  1. 下面三步同理,使得i完成了第二次自加,此时i = 3

在这里插入图片描述
在这里插入图片描述

  1. 下面三步同理,使得i完成了第三次自加,此时i = 4

在这里插入图片描述
在这里插入图片描述

  1. i的值赋给寄存器eax,此时eax = 4

在这里插入图片描述

  1. i的值加给寄存器eax,此时eax = 8

在这里插入图片描述

  1. i的值加给寄存器eax,此时eax = 12

在这里插入图片描述

  1. 最后把eax的值赋给地址为ebp-14h的这个地方(其实就是ret所在),此时ret = 12(0x0000000c)

在这里插入图片描述
在这里插入图片描述

三、总结

  • 可以发现,VS是先把三个括号里的i++自加完,再将三个i进行相加的,即4 + 4 + 4 = 12
  • 不难猜出,linus环境下应该是先算前两个i++,此时i = 3,然后先加起来3 + 3存放在寄存器中,接着才算第三个i++,此时i = 4(此时的i虽然变了,但是无法影响寄存器里的3 + 3),再把此时的i加在寄存器中,即最终为3 + 3 + 4 = 10,然后寄存器再把10赋值给ret
  • 从中我们可以知道,并不是代码越复杂就显得我们越厉害,就连编译器都读不懂的代码我们又要来何用呢?高效且可读性强的代码才是我们的追求!希望通过本文可以让铁汁们省去不必要的时间去钻研这些鬼东西~

在这里插入图片描述

相关文章
|
1天前
|
运维 程序员
程序员在企业中是如何做需求的
需求从哪里来,到哪里去
6 0
程序员在企业中是如何做需求的
|
1月前
|
算法 安全 数据安全/隐私保护
深入探究一个长期隐藏的底层bug的学习报告
在软件开发的过程中,底层bug往往像一颗定时炸弹,随时可能引发严重的问题。本文将分享我在开发过程中遇到的一个长期未被发现的底层bug,以及我如何逐步排查并最终解决这个问题的全过程。通过这次排查,我深刻认识到了代码规范性的重要性。一个不规范的代码修改,虽然短期内可能不会引起问题,但长期累积下来,可能会引发灾难性的后果。此外,我也意识到了底层模块的通用性和风险意识的重要性。在解决一个问题的同时,应该审视是否有相似的问题存在,以避免未来的风险。
89 3
|
1月前
|
算法 搜索推荐 数据挖掘
掌握程序员之剑:解析常见算法与其在生活和工作中的影响
掌握程序员之剑:解析常见算法与其在生活和工作中的影响
46 1
|
8月前
|
程序员
接受平庸,特别是程序员
接受平庸,特别是程序员
|
11月前
|
NoSQL 前端开发 测试技术
编程之你不知道的坑,足以致命!
编程之你不知道的坑,足以致命!
74 0
编程之你不知道的坑,足以致命!
推荐5款你可能没见过的效率软件
你有没有想过,有些软件能让你的电脑用起来更方便,更快,更好看?这篇文章就为你介绍了五款这样的软件,它们分别是BreeZip,ClipClip,燃精灵,Sticky Notes和Tabby。下面我们来看看它们都能做什么吧。
72 1
|
安全 Windows
这5款软件虽然知名度不高,但不代表不好用
其实有许多工具,知名度不高,用的人也很少,不过并不代表它们不好用,小编励志做一个合格的搬运工,让大家都能用上好用的软件。
84 1
|
Web App开发 人工智能 自然语言处理
ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗?
ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗?
|
人工智能 算法
为什么很少有游戏支持场景破坏?是因为技术问题吗?
最近很多游戏狂热迷们正火热讨论的一个问题是:为什么很少有游戏支持场景破坏?说实话小编也非常好奇,于是乎小编去查了好多资料。接下来小编带领大家一起去深挖究竟!
99 0
为什么很少有游戏支持场景破坏?是因为技术问题吗?
|
设计模式 缓存 JavaScript
90%的人会遇到性能问题,如何用1行代码快速定位?
今天,齐光将会基于之前列举的众多指标,给出一些常见的调优分析思路,即:如何在众多异常性能指标中,找出最核心的那一个,进而定位性能瓶颈点,最后进行性能调优。整篇文章会按照代码、CPU、内存、网络、磁盘等方向进行组织,针对对某一各优化点,会有系统的「套路」总结,便于思路的迁移实践。
2284 0