你真正的了解i++和++i吗?

Posted 啊渊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你真正的了解i++和++i吗?相关的知识,希望对你有一定的参考价值。

一个菜鸟新手问题,佬大你知道以下代码的运行结果吗?

int x=3;
int y=(++x)+(++x)+(++x);

我自豪的答到,这个不简单吗?15
因为++x =4,每一次x的值都被改变+1,因此y=4+5+6 = 15.
菜鸟说,是不是电脑坏了,你过来看看运行结果。
y = 16。

大型翻车现场。
我立马写了一份代码:main.c

#include <stdio.h>
int  main(int agrc,char** args){
	int x = 3,y,z1	,z2,z3,res;
	z1=(++x);
	printf("z1:%d,x:%d\\n",z1,x);
	z2=(++x);
	printf("z2:%d,x:%d\\n",z2,x);
	z3=(++x);
	printf("z3:%d,x:%d\\n",z3,x);
	res = z1 + z2 + z3;
	printf("res:%d\\n",res);
	x = 3;
	y=(++x)+(++x)+(++x);
	printf("y:%d\\n",y);
	return 0;
}

编译代码如下:

gcc main.c -o main
./main
## 运行结果
z1:4,x:4
z2:5,x:5
z3:6,x:6
res:15
y:16

奇了怪了,为啥一个是15,一个是16.
旁边的同事提醒我,你用clang编译试试。
编译代码如下:

clang main.c -o main
./main
## 运行结果
z1:4,x:4
z2:5,x:5
z3:6,x:6
res:15
y:15

两种编译器,代码相同运行结果还不一样!!!!!

gcc 分析

为了方便查看我将代码修改以下代码:

#include <stdio.h>
int  main(int agrc,char** args){
	int x = 3,y;
	y=(++x)+(++x)+(++x);
	printf("y:%d\\n",y);
	return 0;
}

编译

gcc -g test.c -o testcc
gdb testcc

输入 l查看代码:

输入 b 5,在第5行打断点。

运行到第五行停止。

disassenble 查看汇编代码



加法分析

0x0000000000401131 <+15>:    movl   $0x3,-0x4(%rbp) # 赋值给x ,寄存器rbp的值等于3
0x0000000000401138 <+22>:    addl   $0x1,-0x4(%rbp) # x+1 ,寄存器rbp的值等于4
0x000000000040113c <+26>:    addl   $0x1,-0x4(%rbp) # x+1 ,寄存器rbp的值等于5
0x0000000000401140 <+30>:    mov    -0x4(%rbp),%eax # 将rbp的值放入到eax寄存器,eax的值等于5,rax也等于5
0x0000000000401143 <+33>:    lea    (%rax,%rax,1),%edx # 这个时候y=(++x)+(++x) 指令mov (%rax+%ras*1)  edx,会发现这个时候rax的值是5,所以edx的值是10!!!!!
0x0000000000401146 <+36>:    addl   $0x1,-0x4(%rbp) # x+1 ,寄存器rbp的值等于6
0x000000000040114a <+40>:    mov    -0x4(%rbp),%eax 
0x000000000040114d <+43>:    add    %edx,%eax		# 6+10 ,所以结果是16

调试技巧

p $rbp-4  				// 获取rbp的地址
p *(0x7fffffffde1c) 	// 打印值
b 0x0000000000401131 	// 打断点

clang 分析

#include <stdio.h>
int  main(int agrc,char** args){
	int x = 3,y;
	y=(++x)+(++x)+(++x);
	printf("y:%d\\n",y);
	return 0;
}

编译

clang -g test.c -o testclang
gdb testclang

main.c:13:5: warning: multiple unsequenced modifications to 'x' [-Wunsequenced]
        y=(++x)+(++x)+(++x);
           ^     ~~
1 warning generated. 

使用gdb查看

0x0000000000401146 <+22>:    movl   $0x3,-0x14(%rbp) # 给x赋值为3
0x000000000040114d <+29>:    mov    -0x14(%rbp),%edi # 将x的值保存在edi寄存器
0x0000000000401150 <+32>:    add    $0x1,%edi		#  edi +1 寄存器 edi值等于4
0x0000000000401153 <+35>:    mov    %edi,-0x14(%rbp) #  保存rpb的值
0x0000000000401156 <+38>:    mov    -0x14(%rbp),%eax  # 将x的值保存在eax寄存器 4
0x0000000000401159 <+41>:    add    $0x1,%eax		#  edi +1 寄存器 edi值等于5 
0x000000000040115c <+44>:    mov    %eax,-0x14(%rbp) #  保存rpb的值 5 
0x000000000040115f <+47>:    add    %eax,%edi		#   相加!!关键,看到了吗?这里是两个寄存器所以不会 4+ 5=9
0x0000000000401161 <+49>:    mov    -0x14(%rbp),%eax
0x0000000000401164 <+52>:    add    $0x1,%eax        # eax +1 寄存器 eax值等于6
0x0000000000401167 <+55>:    mov    %eax,-0x14(%rbp)
0x000000000040116a <+58>:    add    %eax,%edi
0x000000000040116c <+60>:    mov    %edi,-0x18(%rbp)
0x000000000040116f <+63>:    mov    -0x18(%rbp),%esi
0x0000000000401172 <+66>:    movabs $0x402004,%rdi

所以他的运行结果是15。

好吧!!学到了吧。

以上是关于你真正的了解i++和++i吗?的主要内容,如果未能解决你的问题,请参考以下文章

你了解for循环吗

这个代码片段有啥作用?

js 常用代码片段

有趣的 C++ 代码片段,有啥解释吗? [复制]

简写赋值运算符,+=,真正的含义?

JavaScript基础你真正了解如今的Js数组吗,看这篇就(Go)够了