上篇中我们探索了从汇编的角度看C和Go中的变量声明,这篇我们继续探索下函数的参数传递时的基本类型和指针类型
在我们开始学习编程的时候,相信都接触过类似下面的代码吧:在函数内部改变值,一个传入的是基本类型,一个是指针类型,外部的变量是否改变
下面是go的示例代码:
package main
import "fmt"
func pb(num int) {
num = 10
}
func pbp(num *int) {
*num = 10
}
func main() {
b := 100
pb(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, AX
0x0034 00052 (main.go:23) PCDATA $1, $0
0x0034 00052 (main.go:23) CALL main.pb(SB)
// 函数pb的赋值对应的代码:num = 10
0x0000 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), AX
0x00cc 00204 (main.go:26) CALL main.pbp(SB)
// 函数pbp的赋值对应的代码:*num = 10
0x0000 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), %eax
movl %eax, (%esp)
call _pb
// 函数pb的赋值对应的代码:num = 10
pushl %ebp
movl %esp, %ebp
movl $10, 8(%ebp)
nop
popl %ebp
// 函数pbp的调用
leal 28(%esp), %eax
movl %eax, (%esp)
call _pbp
// 函数pbp的赋值对应的代码:*num = 10
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl $10, (%eax)
nop
popl %ebp
可以看到,C编译器约定的寄存器是eax,第一个函数复制给了函数栈上,第二个给了eax对应的内存变量
可能对应100这个值的存储和赋值有点疑惑:
通过上面的Go和C对一个的函数调用的汇编探索,对于函数传值和传指针时能不能改变值应该有了一定的认知了
核心感觉还是两点:
但还是有很多的疑问:
了解更多,不知道的更多,后面继续探索