From 9e49c74cb62abbe2a117e5d4db86292130a5a6ce Mon Sep 17 00:00:00 2001 From: sfw Date: Fri, 29 Jun 2018 17:27:16 +0800 Subject: [PATCH] ch3-05-fix typo --- ch3-asm/ch3-05-control-flow.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ch3-asm/ch3-05-control-flow.md b/ch3-asm/ch3-05-control-flow.md index 8b38c60..2388be9 100644 --- a/ch3-asm/ch3-05-control-flow.md +++ b/ch3-asm/ch3-05-control-flow.md @@ -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标号之后就是返回累加结果的语句。 -循环是最复杂到控制流,循环中隐含了分支和跳转语句。掌握了循环到下方基本也就掌握了汇编语言到写法。掌握规律之后,其实汇编语言编程会变得异常简单。 +循环是最复杂到控制流,循环中隐含了分支和跳转语句。掌握了循环的写法基本也就掌握了汇编语言的写法。掌握规律之后,其实汇编语言编程会变得异常简单。