C语言的函数参数与栈

C语言的函数参数与栈,第1张

C语言的函数参数与栈 前言

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

函数参数在栈上么?

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

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

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

static int *pa0;
static int *pa1;

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

        pa0 = &a0;
        pa1 = &a1;
}

反汇编如下:

42004f7c :
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 
	pa1 = &a1;
42004f88:	3fc8b7b7          	lui	a5,0x3fc8b
42004f8c:	0038                	addi	a4,sp,8
42004f8e:	7ee7a223          	sw	a4,2020(a5) # 3fc8b7e4 

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

不难看出,由于a0和a1实际未使用,所以a0++和a1++直接被优化掉了。取地址仍然在栈上,但编译器并没有先把寄存器上的a0和a1入栈,这就是编译器聪明的地方,不做没有意义的 *** 作。此时就算在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%xn", *pa0, *pa1);
}

反汇编如下:

42004f7c :
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 
	pa1 = &a1;
42004f94:	3fc8b7b7          	lui	a5,0x3fc8b
42004f98:	0038                	addi	a4,sp,8
42004f9a:	7ee7a223          	sw	a4,2020(a5) # 3fc8b7e4 

	printf("0x%x, 0x%xn", *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 
}
42004fac:	40f2                	lw	ra,28(sp)
42004fae:	6105                	addi	sp,sp,32
42004fb0:	8082                	ret

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

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4752284.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-08
下一篇 2022-11-08

发表评论

登录后才能评论

评论列表(0条)

保存