i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?

❤️作者主页:微凉秋意
✅作者简介:后端领域优质创作者,CSDN内容合伙人,阿里云专家博主
✨精品专栏:C++面向对象

文章目录

  • 前言
  • 1、第一题揭秘
    • 1.1、认为是100 的原因
    • 1.2、0 的正确打开方式
  • 2、第二题揭秘
    • 2.1、Java 中的printf 是格式化输出
    • 2.2、C/C++ 打印时按照从右向左顺序入栈
  • 3、 留一道课后作业


前言

最近身边的朋友分享了一些有趣的算法题给我,我看到题后直接不假思索的说出了我的答案,然而没有一道说对的,顿时我的脾气就上来了,通过查阅资料并与同学讨论,最终从语言特点以及汇编层面解决了问题。接下来我把三道题目放出来,大家尽情作答,看看你是否能成功入坑。

第一题:你觉得 i 的结果为多少?A. 99 B. 100 C. 0

i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第1张图片

第二题:C/C++以及Java 输出的结果会不会相同呢?A. 相同 B. 不相同

i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第2张图片i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第3张图片

1、第一题揭秘

正确答案应该是 0

i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第4张图片

1.1、认为是100 的原因

i = 0;
i = i++;

如上,i++0++,把结果0 赋给 i,然后再进行加1操作,for 循环了100次,那么最终 i 也递增了100次,答案为什么不是100??

实际上,i 确实递增了,但是属于 “无效递增”

1.2、0 的正确打开方式

不妨从 JVM 内存模型上分析,赋值运算符两边会分别进入内存中的两个部分,前者是局部变量表,后者是操作数栈,对于i = 0来说,0会先进入操作数栈,i 进入局部变量表中,操作数栈弹出栈顶元素赋值给局部变量,也就是把 0 赋值给了 i;那么对于i = i++,i++ 首先入操作数栈,也就是操作数栈的栈顶是0++ ,局部变量表中的 i 会进行自增变为 1,最终操作数栈弹出栈顶,将 0 赋值给局部变量表中的 i。这样无论进行多少次赋值操作,最终 i 的结果只能是 0

而对于 i = ++i,操作数栈中进入的是 ++i,也就是操作数栈的栈顶元素等于1,局部变量表中的 i 自增后结果也是1,最后弹出栈赋值结果也会是1,因此对于 ++i 操作直接按照字面意思理解即可。

2、第二题揭秘

先来看一下打印结果吧:

Java 运行结果:
i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第5张图片C/C++ 运行结果:
i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第6张图片

可以清晰地看到 Java 的打印结果与 C/C++ 不同,在下面将做出我的解释。

2.1、Java 中的printf 是格式化输出

诸如 %d%f%s 分别代表着整型、浮点型、字符串类型的占位符,在进行打印的时候会被后续的变量替代。在 a 为 2 时, a++ 的结果为 2 ,自增后 a 变为 3,然后进行 ++a,结果为 4,而 Java 的打印结果也确实如此。

按照同样的理解,为什么 C/C++ 中的结果是 3 4 呢?这就涉及到了 函数调用约定 问题,printfcout 参数是右向左进行入栈操作的。那到底这个入栈操作是怎么进行的呢,那就往下看看汇编的指令吧!

2.2、C/C++ 打印时按照从右向左顺序入栈

汇编层面解析 printf 函数执行顺序:

i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第7张图片

由上面汇编指令得知执行流程为:

  1. 先处理最后一个参数 ++a
    • 通过 mov 传送指令将 内存中a对应的值传送到累加器 eax 中
    • 累加器进行加 1 操作后将此值再次送回内存
  2. 然后处理参数 a++
    • 先将此时 a对应的值传送到寄存器 ecx 中,然后存储到另一个内存单元中,为了方便描述,将该内存单元称为 temp
      • mov 指令不允许两边的操作数同时为内存,因此使用 ecx 作为媒介
    • 随后将 a 对应的值送到寄存器 edx 中并进行加 1 操作并重新送入原内存中
  3. 运算完后,将 a 对应的值传送到 eax 中通过 push 指令入栈,将 temp 内存对应的值送到 ecx 中也通过 push 入栈,最后将剩余字符串入栈,调用函数,完成打印。

结果分析:

a++ 的结果就是临时存储单元 temp 对应的结果,而此结果正是 3,++a 的结果就是累加器 eax 存储的值,该值经过两次 add 1 操作,从 2 变为了 4,最终结果也就是 4。这也正是C 语言printf 输出的结果。

3、 留一道课后作业

对于C++中的 cout 执行流程我便不做解释,过程与 printf 一致,都是右向左先进行入栈操作,看了这么多,来一个练习题吧:

i = i++ 计算过程还不会?C/C++ 的输出语句与Java 有何不同?_第8张图片


最终打印结果为?我做个投票吧,一天后我在评论区公布答案,大家快来检验一波学习成果!

你可能感兴趣的:(算法设计与分析,java,c++,c语言)