1613551 发表于 2023-2-4 20:46:17

关于运算符的问题

{: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;
}

1613551 发表于 2023-2-4 20:48:31

我好像总是理解成z的值不为0了就不会执行整个while循环了,但是实际是还是执行了第六行,然后z--再跳过while循环吗

1613551 发表于 2023-2-4 20:49:17

{:10_254:}然后最二个printf那里我以为会打印0

dolly_yos2 发表于 2023-2-4 21:53:42

full expression 之间是有顺序点的
expression statement (大体上就是一个语句就是一个表达式,比如这里的 printf 调用)和 while 的条件语句都是 full expression,所以它们的求值之间有顺序点

两手空空儿 发表于 2023-2-5 09:48:40

本帖最后由 两手空空儿 于 2023-2-5 10:09 编辑

while (z--)
    {
      printf("z=%d\n", z);
    }

等效如下
int temp = z;
z--;
while (temp)
    {
      printf("z=%d\n", z);
    }

后--运算的时候,编译器会先copy 一个副本,然后--,用副本参加运算
前--是直接--

人造人 发表于 2023-2-5 10:31:12

#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函数了
然后就没了

人造人 发表于 2023-2-5 10:44:52

我们把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

人造人 发表于 2023-2-5 10:47:31

可以用这个程序研究研究在底层是怎么做的

#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


人造人 发表于 2023-2-5 10:51:19

    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之前的

1613551 发表于 2023-2-5 15:04:21

人造人 发表于 2023-2-5 10:51
a = ++b;
    112b:      83 45 fc 01                  addl   $0x1,-0x4(%rbp)
    112f:      ...

{:10_250:}大佬我看不懂这些,呜呜呜,因为考试只考c语言和数据结构我目前就只学了c语言和数据结构

1613551 发表于 2023-2-5 15:05:19

两手空空儿 发表于 2023-2-5 09:48


懂了,谢谢

人造人 发表于 2023-2-5 15:07:38

1613551 发表于 2023-2-5 15:04
大佬我看不懂这些,呜呜呜,因为考试只考c语言和数据结构我目前就只学了c语言和数据结构

哪里看不懂?
addl   $0x1,-0x4(%rbp)
给b的值加1,看不懂?

1613551 发表于 2023-2-5 15:14:58

人造人 发表于 2023-2-5 15:07
哪里看不懂?
addl   $0x1,-0x4(%rbp)
给b的值加1,看不懂?

{:10_245:}说实话全部都没看懂,从说是汇编语言指令后面就看不懂了

人造人 发表于 2023-2-5 15:30:55

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,这个不难吧?
哪里不懂?

1613551 发表于 2023-2-5 15:50:15

人造人 发表于 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)
这些就没看懂了

人造人 发表于 2023-2-5 16:08:29

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

1613551 发表于 2023-2-5 16:10:12

人造人 发表于 2023-2-5 16:08
movl   $0x1,-0x8(%rbp)
其他不用管,就看这个就可以了
movl一条指令


懂了懂了,我说怎么根本看不懂
页: [1]
查看完整版本: 关于运算符的问题