【C++和C#的区别杂谈】后自增运算符的结算时机
C++和C#的前自增++n和后自增n++,都是先自增后取值和先取值后自增的含义,但在复杂一点的赋值语句中,我发现细节上有很大的差异。
发现这个问题主要是一个无聊的晚上,我想搞清楚后自增是什么时候结算,自己捣鼓了一下之后我把我自己的测试结果告诉了朋友,结果学java的朋友和我争论了半天,最后发现同样的代码,大家输出是不一样的。之后我又用了C#写了同样的测试代码,发现输出和他java是一样的,这让我豁然开朗,遂写下这篇博客希望分享我的结论,如果有错误的话,欢迎评论区指正。
前排提醒:C++和C#我都是用的VS2017,不同编译环境的C++代码输出结果可能不一样
先说我的结论:
C++:i++遇到顺序点(逗号,分号)之后i才自增,即我一直以来认为的一条语句结束后结算。但++i前自增的值会影响赋值语句所有i的值。
C#:C#不允许使用‘,’逗号运算符,但也并非到';'分号才结算,赋值语句是从左到右,按序取值,i++在取完i的值之后马上就自增了,但不影响之前取的i的值,只影响后续i的取值。
心得:实际开发还是尽量自增单行写,第一个是可读性好,第二个是不容易出问题。(应该也就只有笔试会出现以下实验的代码了)
结论看不明白的可以看看我无聊的小实验:
最简单常见的版本:
int arr[] = { 0,0,0 };
int n = 0;
arr[n] = n++;//语句a
for (int num : arr)
{
cout << num << ' ';
}
//输出是0 0 0,语句a结束后n自增到1
上面这段代码其实在C#也是一样的输出,但如果下面这样写,结果就不一样了。
C++版:
int arr[] = { 0,0,0 };
int n = 0;
arr[n++] = n;//语句a
for (int num : arr)
{
cout << num << ' ';
}
//输出是0 0 0,语句a结束后n自增到1,C++结果和上面的一样
C#版:
int[] arr = { 0, 0, 0 };
int n = 0;
arr[n++] = n;//语句a
foreach (int num in arr)
{
Console.Write(num + " ");
}
//输出是1 0 0,现在应该能理解结论了,从左到右,先取n作为索引值,然后自增之后影响到了等号右边的n的值
//语句a则为arr[0] = 1;
就是这样的差异开始引申出了问题,也是让我迷惑的开始,接下来我会通过更复杂的例子和反汇编的指令来解释和证明我的结论,先列举C++的部分,之后再C#,感兴趣的可以往下看。
C++部分:
//代码01
int arr[] = { 0,0,0 };
int n = 0;
arr[n++] = n + (n++) + ++n;
for (int num : arr)
{
cout << num << ' ';
}
//输出为0 3 0,具体操作流程为语句先结算了++n,使得n自增到1.而出现的两个n++都在赋值语句结束后结算
//所以编译结果为arr[1] = 1 + 1 + 1; 然后i自增两次到3
以下为反汇编的结果:

一开始看到这个我有点懵逼,但是通过比对下面这段代码的反汇编指令,就能看出来了。
//代码02
int[] arr = { 0, 0, 0 };
int n = 0;
arr[++n] = n + (n++) + ++n;//把索引处的n++改为了++n
foreach (int num in arr)
{
Console.Write(num + " ");
}
//输出为0 0 6,具体操作流程为语句先结算两次++n,使得n自增到2.而n++在赋值语句结束后结算
//所以编译结果为arr[2] = 2 + 2 + 2; 然后i自增到3
//这下应该发现n在这条语句中取值的时候都是同样的值了
以下为上面代码02的反汇编结果:

通过和上面的指令比对可以看出来了,具体差异在00251F3A这段指令,arr[++n]这段代码02在累加前多进行了一次对n的自增,然后将自增后的值赋给了n,然后开始进行累加,而上面arr[n++]那段代码01是在累加结束之后,在01161F4A那条指令对n++进行自增的结算,所以就算看不懂全部的指令,也应该能通过比对这两段代码看出他们的差异,从而证明了C++对于后自增的处理,是在语句结束之后结算。
说到语句结束,我之前写了一篇关于逗号运算符的博客,可以结合今天这个结论看看下面这段代码的输出结果。
int arr[] = { 0,0,0 };
int n = 0;
arr[n] = (n++, n + (n++) + ++n);//在逗号左边添加了n++的语句
for (int num : arr)
{
cout << num << ' ';
}
//输出为0 0 6
//原因为逗号也属于一条语句结束的标志,所以结算了n++,n=1,然后执行新的语句n + (n++) + ++n
//逗号右边的语句先结算了++n,n=2,所以最后赋值语句为arr[2] = 2 + 2 + 2; 然后n++到3
至此C++的部分结束。
接下来C#的测试结果将颠覆上面的结论,如果不用java或者C#的话,下面的内容可能没有用(朋友java的输出结果和我C#一样,不过也是希望大家自己试试,我自己没有试过)
C#部分:
//代码01 C#版
int[] arr = { 0, 0, 0 };
int n = 0;
arr[n++] = n + (n++) + ++n;
foreach (int num in arr)
{
Console.Write(num + " ");
}
//输出为5 0 0 和VC++完全不一样对吧
//具体原因为C#的赋值语句是从左到右,先取了n的值作为索引,然后马上对n自增,n=1
//来到等号右边,第一个n取值为1,第二个n取值为1,然后自增到2,第三个n先自增到3,然后取值为3
//所以最终编译结果为arr[0] = 1 + 1 + 3; 即5 0 0的原因
//为了节省篇幅,我直接告诉你arr[++n] = n + (n++) + ++n的结果为0 5 0,如果你上面看懂了这个应该也懂
贴一下C#这段代码的反汇编结果:

可以看到第一行是取n的值,然后把n作为索引值,之后inc指令对n自增1。证明了上面的结论。
即:C#的赋值语句为从左到右,按序取值,n++在取完n的值之后马上自增,但不影响之前取的值,只影响后续n的取值。
小结:
所以++n 先自增后取值和n++ 先取值后自增是能直接套在C#上的,而对于VC++来说,后自增的结算是非常“缓慢”的,到语句结束才结算。
感谢您的观看。
【C++和C#的区别杂谈】后自增运算符的结算时机的更多相关文章
- C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式
自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序.括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况. ++.--和*的优先级 ...
- 为什么mysql事务回滚后, 自增ID依然自增
事务回滚后,自增ID仍然增加,回滚后,自增ID仍然增加.比如当前ID是7,插入一条数据后,又回滚了.然后你再插入一条数据,此时插入成功,这时候你的ID不是8,而是9.因为虽然你之前插入回滚,但是ID还 ...
- js中script的上下放置区别 , Dom的增删改创建
回顾 javascript分为三部分: 1.ECMAScript5.0 es6(阮一峰) es7 es8 es6中有类的概念 声明变量 var let(es6中语法) 内置函数 Date Math.r ...
- Swift3.0 更新后出现比较运算符方法
在将项目更新到swift3.0之后,在一些controller头部会出现 比较运算符的方法 // FIXME: comparison operators with optionals were rem ...
- 用Jdbc连接数据库后实现增删改查功能
增删改用的都是executeUpdate()方法: 查用的是executeQuery()方法 package cn.lideng.dbc; import java.lang.management.Ma ...
- python之while循环用法举例,break与continue的区别,格式化输出及运算符
一.while循环的基本结构 while 条件: 代码块(循环体) else: 当上面的条件为假. 才会执行 执行顺序:判断条件是否为真. 如果真. 执行循环体. 然后再次判断条件....直到循环条件 ...
- [转]说说C语言运算符的“优先级”与“结合性”
补充自己的一点理解: 1.关于++i 与 i++的区别. ++i 和 i++如果是单独使用的语句,即二者后面均加上分号,或者其他单独使用的语句,没有任何区别.例如: for(i=0;i<100; ...
- C++回顾day02---<运算符重载>
一:运算符重载的限制 (一)可以重载的运算符: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << > ...
- 说说C语言运算符的“优先级”与“结合性”
论坛和博客上常常看到关于C语言中运算符的迷惑,甚至是错误的解读.这样的迷惑或解读大都发生在表达式中存在着较为复杂的副作用时.但从本质上看,仍然是概念理解上的偏差.本文试图通过对三个典型表达式的分析,集 ...
随机推荐
- 最小生成树——Prim算法理解
背景:本文是在小甲鱼数据结构教学视频中的代码的基础上,添加详细注释而完成的.该段代码并不完整,仅摘录了核心算法部分,结合自己的思考,谈谈理解. Prim算法理解: 如图(摘录自小甲鱼教学视频中的图片) ...
- jchdl - GSL值的传播
https://mp.weixin.qq.com/s/jgMljoca-Cwe9x0NaTLzZg GSL的拓扑模型是线和节点连接的模型,值的传播,即是值在线和节点之间传播和转化的过程. 值的 ...
- MethodHandle(方法句柄)系列之一:MethodHandle和MethodType
阅读此文章的作者建议先了解java反射和动态代理. java7中为间接调用方法引入了新的api,其中最关键的是java.lang.invoke包,即方法句柄.我们可以看成是java ...
- Java实现 LeetCode 519 随机翻转矩阵
519. 随机翻转矩阵 题中给出一个 n 行 n 列的二维矩阵 (n_rows,n_cols),且所有值被初始化为 0.要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位 ...
- java实现第八届蓝桥杯平方十位数
平方十位数 题目描述 由0~9这10个数字不重复.不遗漏,可以组成很多10位数字. 这其中也有很多恰好是平方数(是某个数的平方). 比如:1026753849,就是其中最小的一个平方数. 请你找出其中 ...
- docker-compose mysql和node连接认证mongo问题
前言 最近,想部署一个自己的项目,鉴于自己的服务器是VPS(虚拟主机),配置也不够,就想到了用 docker 直接部署好了,这样既方便部署也方便不用的时候卸载或更新 然后本地搭建了环境,踩了一些坑,在 ...
- JAVA第三次blog总结
JAVA第三次blog总结 0.前言 这是我们在博客园上第三次写博客,也是本学期最后一次的JAVA学习大总结.现在我们的JAVA已经接近尾声了,对于编程思想和方法的改变依旧是难点,但是经过这一段时间的 ...
- 温故知新-Mysql索引结构&页&聚集索引&非聚集索
文章目录 摘要 索引 索引概述 索引优势劣势 索引结构 BTREE 结构 B+TREE 结构 页 索引分类 索引语法 索引设计原则 聚触索引 & 非聚触索引 你的鼓励也是我创作的动力 Post ...
- RabbitMQ(2)---高级使用
1.ack和限流 ack也就是消息确认签收,分为自动签收和手动签收.之前的交换机demo中:channel.basicConsume(queueName,true, consumer); 第二个参数 ...
- WinUI 3试玩报告
1. 什么是 WinUI 3 在微软 Build 2020 开发者大会上,WinUI 团队宣布可公开预览的 WinUI 3 Preview 1,它让开发人员可以在 Win32 中使用 WinUI.Wi ...