本文源于博主的好奇心,没啥意义,点开的朋友可以关掉了。
函数参数在栈上么?说函数参数存储在栈上也没什么错,但较真的说,真就不一定。虽然不同体系结构有着不同的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
这会儿编译器就只能老老实实先将位于寄存器的参数入栈,然后再取地址。
总的来说,编译器还是很聪明的,至少在写汇编这件事上,比得过大部分的人,毕竟那是一群研究编程语言的人搞的。有时间的话,想看看编译原理了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)