关于运算符的问题
{:10_254:} 当z--的值为0的时候,再判断z--的值是某为真还是会再--吗?之前论坛有大佬给我讲了副作用和顺序点,但是我还能没能get到它的意思,while语句这里好像没有顺序点,z的--是什么时候发生的呢
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int z = 2;
while (z--)
{
printf("z=%d\n", z);
}
printf("z=%d\n", z);
system("pause");
return 0;
} 我好像总是理解成z的值不为0了就不会执行整个while循环了,但是实际是还是执行了第六行,然后z--再跳过while循环吗 {:10_254:}然后最二个printf那里我以为会打印0 full expression 之间是有顺序点的
expression statement (大体上就是一个语句就是一个表达式,比如这里的 printf 调用)和 while 的条件语句都是 full expression,所以它们的求值之间有顺序点 本帖最后由 两手空空儿 于 2023-2-5 10:09 编辑
while (z--)
{
printf("z=%d\n", z);
}
等效如下
int temp = z;
z--;
while (temp)
{
printf("z=%d\n", z);
}
后--运算的时候,编译器会先copy 一个副本,然后--,用副本参加运算
前--是直接--
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
1149: 55 pushq%rbp
114a: 48 89 e5 movq %rsp,%rbp
114d: 48 83 ec 10 subq $0x10,%rsp
int z = 2;
1151: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
while (z--)
1158: eb 19 jmp 1173 <main+0x2a>
{
printf("z=%d\n", z);
115a: 8b 45 fc movl -0x4(%rbp),%eax
115d: 89 c6 movl %eax,%esi
115f: 48 8d 05 9e 0e 00 00 leaq 0xe9e(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1166: 48 89 c7 movq %rax,%rdi
1169: b8 00 00 00 00 movl $0x0,%eax
116e: e8 cd fe ff ff callq1040 <printf@plt>
while (z--)
1173: 8b 45 fc movl -0x4(%rbp),%eax
1176: 8d 50 ff leal -0x1(%rax),%edx
1179: 89 55 fc movl %edx,-0x4(%rbp)
117c: 85 c0 testl%eax,%eax
117e: 75 da jne 115a <main+0x11>
}
printf("z=%d\n", z);
1180: 8b 45 fc movl -0x4(%rbp),%eax
1183: 89 c6 movl %eax,%esi
1185: 48 8d 05 78 0e 00 00 leaq 0xe78(%rip),%rax # 2004 <_IO_stdin_used+0x4>
118c: 48 89 c7 movq %rax,%rdi
118f: b8 00 00 00 00 movl $0x0,%eax
1194: e8 a7 fe ff ff callq1040 <printf@plt>
system("pause");
1199: 48 8d 05 6a 0e 00 00 leaq 0xe6a(%rip),%rax # 200a <_IO_stdin_used+0xa>
11a0: 48 89 c7 movq %rax,%rdi
11a3: e8 88 fe ff ff callq1030 <system@plt>
return 0;
11a8: b8 00 00 00 00 movl $0x0,%eax
}
11ad: c9 leaveq
11ae: c3 retq
1173: 8b 45 fc movl -0x4(%rbp),%eax
1176: 8d 50 ff leal -0x1(%rax),%edx
1179: 89 55 fc movl %edx,-0x4(%rbp)
117c: 85 c0 testl%eax,%eax
117e: 75 da jne 115a <main+0x11>
上面这5条指令就是while(z--)对应的汇编语言指令
其实应该是6条指令
1158: eb 19 jmp 1173 <main+0x2a>
这个应该也属于是 while(z--) 语句
首先,一上来先给z的值赋值成2
1151: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
-0x4(%rbp) 就是 z 的地址
然后就是一个jmp
1158: eb 19 jmp 1173 <main+0x2a>
这个jmp让cpu从1173的位置继续执行,就是
1173: 8b 45 fc movl -0x4(%rbp),%eax
这就是while语句的部分了
先把z的值读取出来,读取到eax里面
1176: 8d 50 ff leal -0x1(%rax),%edx
然后给eax的值减 1
减 1 之后的结果给edx
一开始z的值是2
1173: 8b 45 fc movl -0x4(%rbp),%eax
把这个2读取到eax里面
1176: 8d 50 ff leal -0x1(%rax),%edx
2 - 1 = 1
把1给edx
1179: 89 55 fc movl %edx,-0x4(%rbp)
把edx中的这个1写回到z里面
执行完这条指令之后,z的值就变成1了
117c: 85 c0 testl%eax,%eax
接下来判断eax中的值,eax中的值是2
117e: 75 da jne 115a <main+0x11>
所以转移到 115a的地方继续执行指令
115a就是去执行printf
printf("z=%d\n", z);
115a: 8b 45 fc movl -0x4(%rbp),%eax
115d: 89 c6 movl %eax,%esi
115f: 48 8d 05 9e 0e 00 00 leaq 0xe9e(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1166: 48 89 c7 movq %rax,%rdi
1169: b8 00 00 00 00 movl $0x0,%eax
116e: e8 cd fe ff ff callq1040 <printf@plt>
执行完这个printf之后又来到了while的部分
116e是printf的最后一条指令,这条指令的下一条指令是1173
就是while的部分
1173: 8b 45 fc movl -0x4(%rbp),%eax
一样的,把z中的1读取到eax
1176: 8d 50 ff leal -0x1(%rax),%edx
给这个值减 1 ,结果写到edx
就是把1 - 1 的结果写到edx
就是把 0 写到edx
1179: 89 55 fc movl %edx,-0x4(%rbp)
然后又把0写到z里面
执行完这条指令之后z里面保存的值就是0了
117c: 85 c0 testl%eax,%eax
117e: 75 da jne 115a <main+0x11>
然后判断eax里面的值是不是0,eax中的值是1,不是0
所以再跳回去执行printf
执行完printf之后又来到了while
1173: 8b 45 fc movl -0x4(%rbp),%eax
1176: 8d 50 ff leal -0x1(%rax),%edx
1179: 89 55 fc movl %edx,-0x4(%rbp)
117c: 85 c0 testl%eax,%eax
117e: 75 da jne 115a <main+0x11>
还是这个
把z里面的那个0读取到eax
减1的结果写到edx
0 - 1 等于几?
等于 -1,没错,把-1写回z里面
1179: 89 55 fc movl %edx,-0x4(%rbp)
执行完这条指令之后,z里面保存的就是-1
然后接下来判断eax里面的值是不是0
这一次eax里面的值是0了
那就不跳上去了,就是说117e的这条jne指令执行完之后要执行1180位置的指令,而不是之前的115a了
printf("z=%d\n", z);
1180: 8b 45 fc movl -0x4(%rbp),%eax
1183: 89 c6 movl %eax,%esi
1185: 48 8d 05 78 0e 00 00 leaq 0xe78(%rip),%rax # 2004 <_IO_stdin_used+0x4>
118c: 48 89 c7 movq %rax,%rdi
118f: b8 00 00 00 00 movl $0x0,%eax
1194: e8 a7 fe ff ff callq1040 <printf@plt>
1180开始又是一个printf
再之后就是system函数了
然后就没了
我们把z--改成--z看看
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
1149: 55 pushq%rbp
114a: 48 89 e5 movq %rsp,%rbp
114d: 48 83 ec 10 subq $0x10,%rsp
int z = 2;
1151: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
while (--z)
1158: eb 19 jmp 1173 <main+0x2a>
{
printf("z=%d\n", z);
115a: 8b 45 fc movl -0x4(%rbp),%eax
115d: 89 c6 movl %eax,%esi
115f: 48 8d 05 9e 0e 00 00 leaq 0xe9e(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1166: 48 89 c7 movq %rax,%rdi
1169: b8 00 00 00 00 movl $0x0,%eax
116e: e8 cd fe ff ff callq1040 <printf@plt>
while (--z)
1173: 83 6d fc 01 subl $0x1,-0x4(%rbp)
1177: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
117b: 75 dd jne 115a <main+0x11>
}
printf("z=%d\n", z);
117d: 8b 45 fc movl -0x4(%rbp),%eax
1180: 89 c6 movl %eax,%esi
1182: 48 8d 05 7b 0e 00 00 leaq 0xe7b(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1189: 48 89 c7 movq %rax,%rdi
118c: b8 00 00 00 00 movl $0x0,%eax
1191: e8 aa fe ff ff callq1040 <printf@plt>
system("pause");
1196: 48 8d 05 6d 0e 00 00 leaq 0xe6d(%rip),%rax # 200a <_IO_stdin_used+0xa>
119d: 48 89 c7 movq %rax,%rdi
11a0: e8 8b fe ff ff callq1030 <system@plt>
return 0;
11a5: b8 00 00 00 00 movl $0x0,%eax
}
11aa: c9 leaveq
11ab: c3 retq
一开始一样,给z赋值成2
然后跳到1173执行while
1173: 83 6d fc 01 subl $0x1,-0x4(%rbp)
这个是把z的值减 1
或者你可以理解成是
先把z的值读取出来,然后减1,然后再把减1之后的结果写回到z里面
这1条指令做了这3件事
把z值读取出来,减1,再写回去
1177: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
然后判断z是不是0
117b: 75 dd jne 115a <main+0x11>
不是0就跳回去执行printf
可以用这个程序研究研究在底层是怎么做的
#include <stdio.h>
int main(void) {
int a = 1;
int b = 2;
a = ++b;
a = --b;
a = b++;
a = b--;
return 0;
}
#include <stdio.h>
int main(void) {
1119: 55 pushq%rbp
111a: 48 89 e5 movq %rsp,%rbp
int a = 1;
111d: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
int b = 2;
1124: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
a = ++b;
112b: 83 45 fc 01 addl $0x1,-0x4(%rbp)
112f: 8b 45 fc movl -0x4(%rbp),%eax
1132: 89 45 f8 movl %eax,-0x8(%rbp)
a = --b;
1135: 83 6d fc 01 subl $0x1,-0x4(%rbp)
1139: 8b 45 fc movl -0x4(%rbp),%eax
113c: 89 45 f8 movl %eax,-0x8(%rbp)
a = b++;
113f: 8b 45 fc movl -0x4(%rbp),%eax
1142: 8d 50 01 leal 0x1(%rax),%edx
1145: 89 55 fc movl %edx,-0x4(%rbp)
1148: 89 45 f8 movl %eax,-0x8(%rbp)
a = b--;
114b: 8b 45 fc movl -0x4(%rbp),%eax
114e: 8d 50 ff leal -0x1(%rax),%edx
1151: 89 55 fc movl %edx,-0x4(%rbp)
1154: 89 45 f8 movl %eax,-0x8(%rbp)
return 0;
1157: b8 00 00 00 00 movl $0x0,%eax
}
115c: 5d popq %rbp
115d: c3 retq
a = ++b;
112b: 83 45 fc 01 addl $0x1,-0x4(%rbp)
112f: 8b 45 fc movl -0x4(%rbp),%eax
1132: 89 45 f8 movl %eax,-0x8(%rbp)
112b: 83 45 fc 01 addl $0x1,-0x4(%rbp)
把b的值加1
112f: 8b 45 fc movl -0x4(%rbp),%eax
1132: 89 45 f8 movl %eax,-0x8(%rbp)
把b的值写到eax,然后再把eax写到a里面
a = b++;
113f: 8b 45 fc movl -0x4(%rbp),%eax
1142: 8d 50 01 leal 0x1(%rax),%edx
1145: 89 55 fc movl %edx,-0x4(%rbp)
1148: 89 45 f8 movl %eax,-0x8(%rbp)
113f: 8b 45 fc movl -0x4(%rbp),%eax
1142: 8d 50 01 leal 0x1(%rax),%edx
把b的值读取到eax,减1的结果写到edx
1145: 89 55 fc movl %edx,-0x4(%rbp)
把edx里面的值写回b里面
就是减1之后的那个值
1148: 89 45 f8 movl %eax,-0x8(%rbp)
把eax里面的值写到a里面
eax里面的值是减1之前的
人造人 发表于 2023-2-5 10:51
a = ++b;
112b: 83 45 fc 01 addl $0x1,-0x4(%rbp)
112f: ...
{:10_250:}大佬我看不懂这些,呜呜呜,因为考试只考c语言和数据结构我目前就只学了c语言和数据结构 两手空空儿 发表于 2023-2-5 09:48
懂了,谢谢 1613551 发表于 2023-2-5 15:04
大佬我看不懂这些,呜呜呜,因为考试只考c语言和数据结构我目前就只学了c语言和数据结构
哪里看不懂?
addl $0x1,-0x4(%rbp)
给b的值加1,看不懂?
人造人 发表于 2023-2-5 15:07
哪里看不懂?
addl $0x1,-0x4(%rbp)
给b的值加1,看不懂?
{:10_245:}说实话全部都没看懂,从说是汇编语言指令后面就看不懂了 1613551 发表于 2023-2-5 15:14
说实话全部都没看懂,从说是汇编语言指令后面就看不懂了
int a = 1;
111d: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
movl $0x1,-0x8(%rbp)
就是把1赋值给变量a,这个不难吧?
哪里不懂? 人造人 发表于 2023-2-5 15:30
int a = 1;
111d: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
就是int a=1;这些我懂,然后其余的像是
111d: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
movl $0x1,-0x8(%rbp)
这些就没看懂了 1613551 发表于 2023-2-5 15:50
就是int a=1;这些我懂,然后其余的像是
111d: c7 45 f8 01 00 00 00 movl $0x1,-0x ...
movl $0x1,-0x8(%rbp)
其他不用管,就看这个就可以了
movl一条指令
movl a, b
把a复制给b
$0x1就是1,但是要加$,语法要求这样
-0x8(%rbp)
这个是变量b的地址
movl $0x1,-0x8(%rbp)
把1写到b这个变量里面
给变量b赋值成1
人造人 发表于 2023-2-5 16:08
movl $0x1,-0x8(%rbp)
其他不用管,就看这个就可以了
movl一条指令
懂了懂了,我说怎么根本看不懂
页:
[1]