IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c/c++专题-常考易错点(i++) + (++i)计算原理分析 -> 正文阅读

[C++知识库]c/c++专题-常考易错点(i++) + (++i)计算原理分析

目录

杂言

问题分析

单个表达式输出结果

多个表达式加赋值输出结果

赋值给自己的问题,值永远不变

总结


杂言

? 对它简单处理,它就简单;
??对它复杂处理,它就复杂;
? 复杂难以把控,无法预测;
? 可预测的程度,关系成败;

问题分析

最近在csdn上的问题区有很多关于i++的表达式的求值输出和使用有很多的问题,这边总结了各种情况。

说明下,下面的代码运行环境的编译器是gcc,大家可以在其它环境可以多试试, 比如vc上

主要有3类:

  • 单个表达式输出结果,例如 printf ("%d",? (i++) + (++i) )
  • 多个表达式加赋值输出结果,例如 printf("%d, %d\n", i+=1, i++)
  • 赋值给自己的问题,值永远不变,例如 i = i++;

单个表达式输出结果

? ? ?先看如下3个运行的结果:

#include <stdio.h>
int main()
{
    int i=3, j=3, k=3;
 
    printf("%d\n",(++j)+(++j));

    printf("%d\n",(++i)+(++i)+(++i));

    printf("%d\n",(++k)+(++k)+(++k)+(++k));

    return 0;
}

输出结果
    10
    16
    23 

结果分析
    输出a的结果是10,16,23 而不是9,15,22

? 我们来分析一下?(++i)+(++i)+(++i) 这个表达式计算过程
??按照一般的理解如果i=3,(++i)+(++i)+(++i) 是改写成 4 + 5 +6 = 15 但是答案不对

? 那么需要找到一种方法看它到底是怎么运行的,最直接的方法去看下编译完成后汇编语言是怎么? 个执行顺序,为啥汇编能看清楚呢,那是因为程序需要编译成一条一条可执行的语句,一条语句就是两个操作数一个操作符,可以理解是最简单的一条语句不能再分解了,这样就不会有二义性。

??我们来看下简单的代码看看汇编怎么样的,代码如下, 文件名叫 htest.c :

#include <stdio.h>
int main()
{
    int i=3;
    int a=0;
 
    a = (++i)+(++i);
    printf("%d\n", a);

    return 0;
}

通过?gcc -S ./htest.c? 得到一个htest.s 编译后的中间文件,里面包含了汇编指令, 如下

 说明:rbp是指栈指针   
    subq	$16, %rsp
	movl	$3, -4(%rbp)   // int i=3; 给i赋值
	movl	$0, -8(%rbp)   // int a=0; 给a赋值
	addl	$1, -4(%rbp)   // ++i;  执行一次++i,i=4
	addl	$1, -4(%rbp)   // ++i;  执行一次++i,i=5
	movl	-4(%rbp), %eax // i+i;  执行2次++后开始计算 5+5
	addl	%eax, %eax     // a = i+i; 把计算结果给 a,结果是10 
	movl	%eax, -8(%rbp) // 
	movl	-8(%rbp), %eax
	movl	%eax, %esi
	movl	$.LC0, %edi
	movl	$0, %eax
	call	printf
	movl	$0, %eax
	leave

? 从上面的汇编分析很容易看出来 i 是执行了2次 ++ 操作后,才开始执行 a = i+i 操作。具体的汇编指令不需要理解很深,大致看下操作指令add和mov就大概知道他们是什么意思了。

? 总结
? ? ? 通过跟踪发现对于这种单表达式 a = (++i)+(++i)+(++i) 有如下规则:我们先改写下
? ? ? a = a1 + a2 +a3? ?其中 a1=a2=a3=++i, i=3;

  • ?第一步:先取出来一个完整的计算,包括一个操作符 + 号,两个操作数a1和a2,这个时候就开始计算了
  • 第二步:取出a1+a2这个完整计算式后开始计算,因为是++号前置所以先加1, a1和a2都先执行加1操作, 这个时候i=5,所以 a1+a2=10
  • 第三步:重复第一步,取出一个完整的表达式, 整个表达式可以改写成?a = 10 + a3, 那么先a3加执行1 之后 i=6,? 所以 a = 10 + 6 =16

? ? ? 可以自测下面两种case,有问题可以讨论讨论

  • ?后置i++
  • (i+=1) + (++i)

多个表达式加赋值输出结果

? ? 先看如下运行的结果

#include <stdio.h>
int main()
{
    int i=3;

    printf("%d %d %d\n", ++i, ++i, i+=2);

    return 0;
}

输出结果:
    7 7 7

结果分析:
    不是4 5 7

我们来分析一下,看起来在printf里面,后面的表达式都计算完成了后才开始打印的, 来通过汇编来验证一下?

	subq	$16, %rsp
	movl	$3, -4(%rbp)   //i=3赋值
	addl	$2, -4(%rbp)   //计算i+=2 i=5
	addl	$1, -4(%rbp)   //计算++i  i=6
	addl	$1, -4(%rbp)   //计算++i  i=7
	movl	-4(%rbp), %ecx  //printf参数1 =i=7  
	movl	-4(%rbp), %edx  //printf参数2 =i=7
	movl	-4(%rbp), %eax  //printf参数3 =i=7
	movl	%eax, %esi
	movl	$.LC0, %edi
	movl	$0, %eax
	call	printf
	movl	$0, %eax
	leave

总结

? ? 通过上面汇编的分析,printf是从右到左开始计算参数,先计算 i+=2, 在计算两个++i。所有的参数计算完了后再开始传参数,并且++操作符合我们的想法,还可以测试以下例子,++后置

printf("%d %d %d\n", i++, ++i, i+=2);

赋值给自己的问题,值永远不变

先看如下运行的结果

#include <stdio.h>
int main()
{
    int i=0;

    while(i<3) {
        printf("%d\n", i);

        i = i++; //重点分析
    }

    return 0;
}

输出结果:
    死循环了, 并且i的值一直是0

结果分析:
    看起来有 i++,怎么死循环了呢

? 来分析一下,为啥结果是0呢,还是老样子分析如下简单的代码的汇编

#include <stdio.h>
int main()
{
    int i=0;

    i = i++;

    return 2;
}

? 汇编代码如下:

	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$0, -4(%rbp)      //i=0 赋值
	movl	-4(%rbp), %eax    //保存i的值到eax, eax=0
	leal	1(%rax), %edx
	movl	%edx, -4(%rbp)    //i++操作 i=1
	movl	%eax, -4(%rbp)    //eax=0 又赋值给了 i,i=0
	movl	$2, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8

总结:可以看出上面的运行是?有一个临时变量 t = i;之后 i++? i=1;再之后 i=t,又转回来了,i的值没有变化
? ??

总结

? ? 写代码的时候尽量简单,预期结果很明确的标准。遇到问题我们可以慢慢想办法去分解一下。知识尽量的全面和深入,关键时刻还是管用的

? ? 有什么不对的地方请指出,大家多讨论讨论

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:13:43  更:2022-10-17 12:16:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年6日历 -2024/6/18 14:16:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码