计算机组成原理if,深入浅出计算机组成原理学习笔记:原来if...else就是goto(第6讲)...

一、CPU 是如何执行指令的?

1、软件程序员的理解

写好的代码变成了指令之后、是一条条顺序执行的就可以了

2、CPU的逻辑组成

ba532880bf1c80a89cd33f38445c2bd1.png

3、寄存器

N 个触发器或者锁存器,就可以组成一个 N 位(Bit)的寄存器,能够保存 N 位的数据。比方说,我们用的 64 位 Intel 服务器,寄存器就是 64 位的。

04f6a47ed661d79463f22100009c3337.png

4、特殊寄存器

ffa05aac3fe685ff020dc84499eb0cbc.png

5、CPU执行指令流程

f0c8ea351c1bffbb72e0148d8cef37ce.png

1、CPU会根据PC寄存器里的地址,从内存里面把需要执行的指令读取到指令寄存器里面直面执行

2、然后根据指令长度自增、开始顺序读取下一条指令。可以看到一个程序的一条条指令在内存里面是连续保存的。也会一条条顺序加载

3、而有些特殊指令(J类跳转指令)、会修改寄存器里面的地址

4、这样下一条要执行的指令就不是从内存里面顺序加载的

5、事实上、这些跳转指令存在,也就是我们在写程序的时候,使用了 if…else 条件语句和 while/for 循环语句的原因

二、从 if…else 来看程序的执行和跳转

[[email protected] c]# cat test.c

#include

#include

int main()

{

srand(time(NULL));

int r = rand() % 2;

int a = 10;

if (r == 0)

{

a = 1;

} else {

a = 2;

}

我们用 rand 生成了一个随机数 r,r 要么是 0,要么是 1。当 r 是 0 的时候,我们把之前定义的变量 a 设成 1,不然就设成 2。

[[email protected] c]# gcc -g -c test.c

test.c: In function ‘main’:

test.c:15:3: error: expected declaration or statement at end of input

}

^

[[email protected] c]# cat -n test.c

1#include

2#include

3

4

5int main()

6{

7 srand(time(NULL));

8 int r = rand() % 2;

9 int a = 10;

10 if (r == 0)

11 {

12 a = 1;

13 } else {

14 a = 2;

15 }

abe4627efdc7a9863e26b89fb1002044.png

执行报错,是因为少了一个}

[[email protected] c]# cat test.c

#include

#include

int main()

{

srand(time(NULL));

int r = rand() % 2;

int a = 10;

if (r == 0)

{

a = 1;

} else {

a = 2;

}

}

[[email protected] c]# gcc -g -c test.c

[[email protected] c]# objdump -d -M intel -S test.o

test.o: file format elf64-x86-64

Disassembly of section .text:

0000000000000000 :

#include

#include

int main()

{

0:55 push rbp

1:48 89 e5 mov rbp,rsp

4:48 83 ec 10 sub rsp,0x10

srand(time(NULL));

8:bf 00 00 00 00 mov edi,0x0

d:e8 00 00 00 00 call 12

12:89 c7 mov edi,eax

14:e8 00 00 00 00 call 19

int r = rand() % 2;

19:e8 00 00 00 00 call 1e

1e:99 cdq

1f:c1 ea 1f shr edx,0x1f

22:01 d0 add eax,edx

24:83 e0 01 and eax,0x1

27:29 d0 sub eax,edx

29:89 45 fc mov DWORD PTR [rbp-0x4],eax

int a = 10;

2c:c7 45 f8 0a 00 00 00 mov DWORD PTR [rbp-0x8],0xa

if (r == 0)

33:83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0

37:75 09 jne 42

{

a = 1;

39:c7 45 f8 01 00 00 00 mov DWORD PTR [rbp-0x8],0x1

40:eb 07 jmp 49

} else {

a = 2;

42:c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2

}

}

49:c9 leave

4a:c3 ret

可以看到,这里对于 r == 0 的条件判断,被编译成了cmp 和 jne 这两条指令。cmp 指令比较了前后两个操作数的值,这里的 DWORD PTR 代表操作的数据类型是 32 位的整数

而 [rbp-0x4] 则是一个寄存器的地址。所以,第一个操作数就是从寄存器里拿到的变量 r 的值。第二个操作数 0x0 就是我们设定的常量 0 的 16 进制表示。cmp 指令的比较结果,会存入到条件码寄存器当中去。

在这里,如果比较的结果是 True,也就是 r == 0,就把零标志条件码(对应的条件码是 ZF,Zero Flag)设置为 1。除了零标志之外,Intel 的 CPU 下还有进位标志(CF,Carry Flah)

符号标志(SF,Sign Flag)以及溢出标志(OF,Overflow Flag),用在不同的判断条件下。

cmp 指令执行完成之后,PC 寄存器会自动自增,开始执行下一条 jne 的指令。

f95cc8d8cabd4f2d31d7613422768771.png

三、如何通过 if…else 和 goto 来实现循环?

[[email protected] c]# cat test.c

int main()

{

int a = 0;

for (int i = 0; i < 3; i++)

{

a += i;

}

}

[[email protected] c]# gcc -g -c test.c

test.c: In function ‘main’:

test.c:4:5: error: ‘for’ loop initial declarations are only allowed in C99 mode

for (int i = 0; i < 3; i++)

^

test.c:4:5: note: use option -std=c99 or -std=gnu99 to compile your code

错误:使用gcc编译代码会报错:

35801b74cc249c999943eedbfd8bbc95.png

原因:这是因为gcc是基于c89标准,不能直接在for循环中初始化增量。而C99标准可以在for循环内定义变量。

解决方法:

我们再看一段简单的利用 for 循环的程序。我们循环自增变量i 三次,三次之后,i>=3,就会跳出循环。整个程序,对应的 Intel 汇编代码就是这样的:

[[email protected] c]# objdump -d -M intel -S test.o

test.o: file format elf64-x86-64

Disassembly of section .text:

0000000000000000 :

int main()

{

0:55 push rbp

1:48 89 e5 mov rbp,rsp

int a = 0;

4:c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0

int i;

for (i = 0; i < 3; i++)

b:c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0

12:eb 0a jmp 1e

{

a += i;

14:8b 45 f8 mov eax,DWORD PTR [rbp-0x8]

17:01 45 fc add DWORD PTR [rbp-0x4],eax

for (i = 0; i < 3; i++)

1a:83 45 f8 01 add DWORD PTR [rbp-0x8],0x1

1e:83 7d f8 02 cmp DWORD PTR [rbp-0x8],0x2

22:7e f0 jle 14

}

}

24:5d pop rbp

25:c3 ret

7b5ccec0447e3505c9b326603b5f82bb.png

可以看到,对应的循环也是用 1e 这个地址上的 cmp 比较指令,和紧接着的 jle 条件跳转指令来、实现的。主要的差别在于,这里的 jle 跳转的地址,在这条指令之前的地址 14,而非 if…else 编

译出来的跳转指令之后。往前跳转使得条件满足的时候,PC 寄存器会把指令地址设置到之前执行过的指令位置,重新执行之前执行过的指令,直到条件不满足,顺序往下执行 jle 之后的指令,整个循环才结束。

其实,你有没有觉得,jle和jmp指令,有点像逻辑程序里面的goto命令,直接指定了一个特定条件下的跳转位置

原文:https://www.cnblogs.com/luoahong/p/10862085.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值