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:
parent
5d9d697030
commit
9e49c74cb6
@ -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寄存器初始化为0,DX对应变量i,循环的迭代变量。在LOOP_IF标号对应的指令部分,使用CMPQ指令比较AX和AX,如果循环没有结束则跳转到LOOP_BODY部分,否则跳转到LOOP_END部分结束循环。在LOOP_BODY部分,更新迭代变量并且执行循环体中到累加语句,然后直接跳转到LOOP_IF部分进入下一轮循环条件判断。LOOP_END标号之后就是返回返回累加结果到语句。
|
||||
其中v0和result变量复用了一个BX寄存器。在LOOP_BEGIN标号对应的指令部分,用MOVQ将DX寄存器初始化为0,DX对应变量i,循环的迭代变量。在LOOP_IF标号对应的指令部分,使用CMPQ指令比较DX和AX,如果循环没有结束则跳转到LOOP_BODY部分,否则跳转到LOOP_END部分结束循环。在LOOP_BODY部分,更新迭代变量并且执行循环体中的累加语句,然后直接跳转到LOOP_IF部分进入下一轮循环条件判断。LOOP_END标号之后就是返回累加结果的语句。
|
||||
|
||||
循环是最复杂到控制流,循环中隐含了分支和跳转语句。掌握了循环到下方基本也就掌握了汇编语言到写法。掌握规律之后,其实汇编语言编程会变得异常简单。
|
||||
循环是最复杂到控制流,循环中隐含了分支和跳转语句。掌握了循环的写法基本也就掌握了汇编语言的写法。掌握规律之后,其实汇编语言编程会变得异常简单。
|
||||
|
Loading…
x
Reference in New Issue
Block a user