C语言的函数参数与栈

前言

本文源于博主的好奇心,没啥意义,点开的朋友可以关掉了。

函数参数在栈上么?

说函数参数存储在栈上也没什么错,但较真的说,真就不一定。虽然不同体系结构有着不同的ABI规范,但纯粹用栈来传参的ABI还真不多见(变参是特殊情况)。一般参数是通过寄存器来传递的,如果没有必要,编译器不会将其放到栈上。

参数不在栈上,对它取地址怎么办?

考虑到使用寄存器传递参数的情况,不禁很好奇:对参数取地址怎么办,寄存器哪有内存地址啊。编译器会怎么做呢?要想知道,还是要看反汇编。以riscv体系结构为例,源码如下:

static int *pa0;
static int *pa1;

void test_func(int a0, int a1) {
        a0++;
        a1++;

        pa0 = &a0;
        pa1 = &a1;
}

反汇编如下:

42004f7c <test_func>:
static int *pa0;
static int *pa1;
static int b0;
static int b1;

void test_func(int a0, int a1) {
42004f7c:	1141                	addi	sp,sp,-16
	a0++;
	a1++;
        
	pa0 = &a0;
42004f7e:	3fc8b7b7          	lui	a5,0x3fc8b
42004f82:	0078                	addi	a4,sp,12
42004f84:	7ee7a023          	sw	a4,2016(a5) # 3fc8b7e0 <pa0>
	pa1 = &a1;
42004f88:	3fc8b7b7          	lui	a5,0x3fc8b
42004f8c:	0038                	addi	a4,sp,8
42004f8e:	7ee7a223          	sw	a4,2020(a5) # 3fc8b7e4 <pa1>

}
42004f92:	0141                	addi	sp,sp,16
42004f94:	8082                	ret

不难看出,由于a0a1实际未使用,所以a0++a1++直接被优化掉了。取地址仍然在栈上,但编译器并没有先把寄存器上的a0a1入栈,这就是编译器聪明的地方,不做没有意义的操作。此时就算在test_func外面操作*pa0*pa1也不会影响对test_func的编译,因为栈上的地址本身就不应该返回,编译器不会对这样没有意义的操作负责。

但是,倘如在test_func内部使用*pa0*pa1,那情况就又不一样了,源码如下:

static int *pa0;
static int *pa1;

void test_func(int a0, int a1) {
        a0++;
        a1++;

        pa0 = &a0;
        pa1 = &a1;

        printf("0x%x, 0x%x\n", *pa0, *pa1);
}

反汇编如下:

42004f7c <test_func>:
static int *pa0;
static int *pa1;
static int b0;
static int b1;

void test_func(int a0, int a1) {
42004f7c:	1101                	addi	sp,sp,-32
42004f7e:	ce06                	sw	ra,28(sp)
	a0++;
42004f80:	0505                	addi	a0,a0,1
42004f82:	c62a                	sw	a0,12(sp)
	a1++;
42004f84:	00158613          	addi	a2,a1,1
42004f88:	c432                	sw	a2,8(sp)
        
	pa0 = &a0;
42004f8a:	3fc8b7b7          	lui	a5,0x3fc8b
42004f8e:	0078                	addi	a4,sp,12
42004f90:	7ee7a023          	sw	a4,2016(a5) # 3fc8b7e0 <pa0>
	pa1 = &a1;
42004f94:	3fc8b7b7          	lui	a5,0x3fc8b
42004f98:	0038                	addi	a4,sp,8
42004f9a:	7ee7a223          	sw	a4,2020(a5) # 3fc8b7e4 <pa1>

	printf("0x%x, 0x%x\n", *pa0, *pa1);
42004f9e:	85aa                	mv	a1,a0
42004fa0:	3c022537          	lui	a0,0x3c022
42004fa4:	76050513          	addi	a0,a0,1888 # 3c022760 <__func__.4559+0x250>
42004fa8:	5a1030ef          	jal	ra,42008d48 <printf>
}
42004fac:	40f2                	lw	ra,28(sp)
42004fae:	6105                	addi	sp,sp,32
42004fb0:	8082                	ret

这会儿编译器就只能老老实实先将位于寄存器的参数入栈,然后再取地址。

总的来说,编译器还是很聪明的,至少在写汇编这件事上,比得过大部分的人,毕竟那是一群研究编程语言的人搞的。有时间的话,想看看编译原理了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值