1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00

ch3-05-fix typo

This commit is contained in:
sfw 2018-06-29 17:27:16 +08:00
parent 5d9d697030
commit 9e49c74cb6

View File

@ -113,7 +113,7 @@ TEXT ·main(SB), $16-0
## if/goto跳转
早期的Go虽然提供了goto语句但是并不推荐在编程中使用。有一个和cgo类似的原则如果可以不使用goto语句那么就不要使用goto语句。Go语言中的goto语句是有严格限制的它无法跨越代码块并且在被跨越的代码中不能含变量定义的语句。虽然Go语言不喜欢goto但是goto确实每个汇编语言码农的最爱。goto近似等价于汇编语言中的无条件跳转指令JMP配合if条件goto就组成了有条件跳转指令而有条件跳转指令正是构建整个汇编代码控制流的基石。
早期的Go虽然提供了goto语句但是并不推荐在编程中使用。有一个和cgo类似的原则如果可以不使用goto语句那么就不要使用goto语句。Go语言中的goto语句是有严格限制的它无法跨越代码块并且在被跨越的代码中不能含变量定义的语句。虽然Go语言不喜欢goto但是goto确实每个汇编语言码农的最爱。goto近似等价于汇编语言中的无条件跳转指令JMP配合if条件goto就组成了有条件跳转指令而有条件跳转指令正是构建整个汇编代码控制流的基石。
为了便于理解我们用Go语言构造一个模拟三元表达式的If函数
@ -123,7 +123,7 @@ func If(ok bool, a, b int) int {
}
```
比如求两个数最大值的三元表达式`(a>b)?a:b`用If函数可以这样表达`If(a>b, a, b)`。因为语言的限制用来模拟三元表达式的If函数不支持可以将a、b和返回类型改为空接口使用会繁琐一些
比如求两个数最大值的三元表达式`(a>b)?a:b`用If函数可以这样表达`If(a>b, a, b)`。因为语言的限制用来模拟三元表达式的If函数不支持可以将a、b和返回类型改为空接口使用会繁琐一些
这个函数虽然看似只有简单的一行但是包含了if分支语句。在改用汇编实现前我们还是先用汇编的思维来重写If函数。在改写时同样要遵循每个表达式只能有一个运算符的限制同时if语句的条件部分必须只有一个比较符号组成if语句的body部分只能是一个goto语句。
@ -138,7 +138,7 @@ L:
}
```
因为汇编语言中没有bool类型我们改用int类型代替bool类型真实的汇编是用byte表示bool类型可以通过MOVBQZX指令加载byte类型的值。当ok参数非0时返回变量a否则返回变量b。我们将ok的逻辑反转下当ok参数为0时表示返回b否则返回变量a。在if语句中当ok参数为0时goto到L标号指定的语句也就是返回变量b。如果if条件不满足也就考试ok残非0执行后门的语句返回变量a。
因为汇编语言中没有bool类型我们改用int类型代替bool类型真实的汇编是用byte表示bool类型可以通过MOVBQZX指令加载byte类型的值。当ok参数非0时返回变量a否则返回变量b。我们将ok的逻辑反转下当ok参数为0时表示返回b否则返回变量a。在if语句中当ok参数为0时goto到L标号指定的语句也就是返回变量b。如果if条件不满足也就是ok参数非0执行后面的语句返回变量a。
上述函数的实现已经非常接近汇编语言,下面是改为汇编实现的代码:
@ -158,7 +158,7 @@ L:
RET
```
首选是将三个参数加载到寄存器中ok参数对应CX寄存器a、b分别对应AX、BX寄存器。然后使用CMPQ比较指令将CX寄存器和常数0进行比较。如果比较的结果为0那么下一条JZ为0时跳转指令将跳转到L标号对应的指令也就是返回变量b的值。如果比较的结果不为0那么JZ指令讲没有效果,继续执行后的指令也就是返回变量a的值。
首选是将三个参数加载到寄存器中ok参数对应CX寄存器a、b分别对应AX、BX寄存器。然后使用CMPQ比较指令将CX寄存器和常数0进行比较。如果比较的结果为0那么下一条JZ为0时跳转指令将跳转到L标号对应的指令也就是返回变量b的值。如果比较的结果不为0那么JZ指令将没有效果,继续执行后面的指令也就是返回变量a的值。
在跳转指令中跳转的目标一般是通过一个标号表示。不过在有些通过宏实现的函数中更希望通过相对位置跳转这时候可以通过PC寄存器的来计算跳转的位置。
@ -166,7 +166,7 @@ L:
Go语言的for循环有多种用法我们这里只选择最经典的for结构来讨论。经典的for循环由初始化、结束条件、迭代步长三个部分组成再配合循环体内部的if条件语言这种for结构可以模拟其它各种循环类型。
基于经典的for循环结构我们定一个一个LoopAdd函数可以用于计算任意等差数列的和
基于经典的for循环结构我们定一个LoopAdd函数可以用于计算任意等差数列的和
```go
func LoopAdd(cnt, v0, step int) int {
@ -236,6 +236,6 @@ LOOP_END:
RET
```
其中v0和result变量复用了一个BX寄存器。在LOOP_BEGIN标号对应的指令部分用MOVQ将DX寄存器初始化为0DX对应变量i循环的迭代变量。在LOOP_IF标号对应的指令部分使用CMPQ指令比较AX和AX如果循环没有结束则跳转到LOOP_BODY部分否则跳转到LOOP_END部分结束循环。在LOOP_BODY部分更新迭代变量并且执行循环体中累加语句然后直接跳转到LOOP_IF部分进入下一轮循环条件判断。LOOP_END标号之后就是返回返回累加结果到语句。
其中v0和result变量复用了一个BX寄存器。在LOOP_BEGIN标号对应的指令部分用MOVQ将DX寄存器初始化为0DX对应变量i循环的迭代变量。在LOOP_IF标号对应的指令部分使用CMPQ指令比较DX和AX如果循环没有结束则跳转到LOOP_BODY部分否则跳转到LOOP_END部分结束循环。在LOOP_BODY部分更新迭代变量并且执行循环体中累加语句然后直接跳转到LOOP_IF部分进入下一轮循环条件判断。LOOP_END标号之后就是返回累加结果的语句。
循环是最复杂到控制流,循环中隐含了分支和跳转语句。掌握了循环到下方基本也就掌握了汇编语言到写法。掌握规律之后,其实汇编语言编程会变得异常简单。
循环是最复杂到控制流,循环中隐含了分支和跳转语句。掌握了循环的写法基本也就掌握了汇编语言的写法。掌握规律之后,其实汇编语言编程会变得异常简单。