自制操作系统番外2:编程语言中函数参数的传递

简介

上篇中我们探索了从汇编的角度看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 

第一个并没有改变,第二个改变了,下面我们从汇编代码看看其原理

Go汇编探索

将上面的代码生成汇编代码,命令可以参考上篇

下面是关键部分代码的摘抄,就不放全部上去

// 变量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)
  1. 首先是声明了变量在栈上,并赋值100
  2. 开始调用函数pb,可以看到直接将100赋值给了寄存器AX(应该编译器阅读的传参,指定给寄存器AX)
  3. 在函数pb内部(函数内部也有自己的变量,所以将传递过来的参数保存在栈上),这里赋值就直接赋值给了函数内部栈的变量了,和main函数上的不是一个,所以没有改变外部的变量
  4. 开始调用函数pbp,不像第一个第一个函数pb直接传值,而是将变量地址给寄存器AX
  5. 在函数pbp内部,虽然也将AX赋值给了函数内部的变量的,但赋值10时是直接给到了AX对应内存地址,直接改变了变量的值

通过上面的汇编,应该有了些感悟,我们继续看看C的

C汇编探索

对应的C代码如下:

#include <stdio.h>

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这个值的存储和赋值有点疑惑:

  • 100是存储在内存中,不是存放在寄存器SP中,内存中有一个程序对用栈(这个涉及到内部布局、分配相关的点,博主目前还在研究中,暂时还没有通透,等后面稍微清晰了再写篇文章讲讲)
  • SP是个寄存器,是放了100这个值对应的内存地址

总结

通过上面的Go和C对一个的函数调用的汇编探索,对于函数传值和传指针时能不能改变值应该有了一定的认知了

核心感觉还是两点:

  • 函数内部变量是独立于外部的(栈是整个程序通用,只是存放在栈上的位置不同而已)
  • 指针是函数内部直接改变对应的内存地址的值

但还是有很多的疑问:

  1. Java中是没有指针这一手的,那传值对应的情况是啥呢(Java的汇编还有点麻烦,后面再探索探索)
  2. 上面研究是基本类型,如果是字符串或者类,那是不是只有传指针,没有传值这一说
  3. 传类的时候,赋值等操作对应的汇编是怎样的,也就是汇编中是怎样对应类的操作

了解更多,不知道的更多,后面继续探索

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值