上篇中我们探索了从汇编的角度看C和Go中的变量声明,这篇我们继续探索下函数的参数传递时的基本类型和指针类型
在我们开始学习编程的时候,相信都接触过类似下面的代码吧:在函数内部改变值,一个传入的是基本类型,一个是指针类型,外部的变量是否改变
下面是go的示例代码:
package mainimport "fmt"func pb(num int) {num = 10
}func pbp(num *int) {*num = 10
}func main() {b := 100pb(b)fmt.Printf("%d \n", b)pbp(&b)fmt.Printf("%d \n", b)
}
运行结果:
100
10
第一个并没有改变,第二个改变了,下面我们从汇编代码看看其原理
将上面的代码生成汇编代码,命令可以参考上篇
下面是关键部分代码的摘抄,就不放全部上去
// 变量b的声明:0x0026 00038 (main.go:22) MOVQ $100, main.b+40(SP)// 函数pb的调用0x002f 00047 (main.go:23) MOVL $100, AX0x0034 00052 (main.go:23) PCDATA $1, $00x0034 00052 (main.go:23) CALL main.pb(SB)
// 函数pb的赋值对应的代码:num = 100x0000 00000 (main.go:9) MOVQ AX, main.num+8(SP)0x0005 00005 (main.go:10) MOVQ $10, main.num+8(SP)// 函数pbp的调用0x00c7 00199 (main.go:26) LEAQ main.b+40(SP), AX0x00cc 00204 (main.go:26) CALL main.pbp(SB)
// 函数pbp的赋值对应的代码:*num = 100x0000 00000 (main.go:13) MOVQ AX, main.num+8(SP)0x0005 00005 (main.go:14) TESTB AL, (AX)0x0007 00007 (main.go:14) MOVQ $10, (AX)
通过上面的汇编,应该有了些感悟,我们继续看看C的
对应的C代码如下:
#include void pb(int num) {num = 10;
}void pbp(int *num) {*num = 10;
}int main() {int num = 100;pb(num);printf("%d\n", num);pbp(&num);printf("%d\n", num);return 0;
}
将其生成汇编代码(32位的)后,对应的关键汇编代码如下:
// 变量b的声明:movl $100, 28(%esp)// 函数pb的调用movl 28(%esp), %eaxmovl %eax, (%esp)call _pb
// 函数pb的赋值对应的代码:num = 10pushl %ebpmovl %esp, %ebpmovl $10, 8(%ebp)noppopl %ebp// 函数pbp的调用leal 28(%esp), %eaxmovl %eax, (%esp)call _pbp
// 函数pbp的赋值对应的代码:*num = 10pushl %ebpmovl %esp, %ebpmovl 8(%ebp), %eaxmovl $10, (%eax)noppopl %ebp
可以看到,C编译器约定的寄存器是eax,第一个函数复制给了函数栈上,第二个给了eax对应的内存变量
可能对应100这个值的存储和赋值有点疑惑:
通过上面的Go和C对一个的函数调用的汇编探索,对于函数传值和传指针时能不能改变值应该有了一定的认知了
核心感觉还是两点:
但还是有很多的疑问:
了解更多,不知道的更多,后面继续探索