如实现一个像printf函数格式的函数:
test.c
void myprintf(char *line, ...) // line指针变量是局部变量,在栈里分配空间
{
printf(line); //调用printf时,r0存放字符串地址
}
int main(void)
{
myprintf("hello test %d, %d, %s, %d, %c\n", 11, 22, "nono", 7788, 'K');
//调用myprintf时,共有6个参数, r0存放字符串地址, r1存11, r2存22, r3存放"nono"字符串地址, 后面两个参数需存入栈里
return 0;
}
在myprintf函数是如何知道参数的个数及如何取出参数的值?
调用myprintf函数时的第一个参数是一个字符串的地址,通过遍历字符串里的’%’字符,可以得知后面还带有多少个参数。
参数值的获取只能通过汇编来看了。
看汇编代码前,先回顾下:
在arm程序里,参数是从r0寄存器开始传参数,直到r3寄存器存放要传递的第四个参数,再多的参数就需压栈了。
函数的参数也是局部变量在栈里分配空间。
反汇编得到的代码:
000083f4 <main>:
83f4: e92d4800 push {fp, lr}
83f8: e28db004 add fp, sp, #4
83fc: e24dd008 sub sp, sp, #8
8400: e59f302c ldr r3, [pc, #44] ; 8434 <main+0x40> //取出7788的值存入寄存器r3
8404: e58d3000 str r3, [sp] //把寄存器r3里的值(7788)压栈里
8408: e3a0304b mov r3, #75 ; 0x4b // 字符'K'的ascii值存入寄存器r3
840c: e58d3004 str r3, [sp, #4] // 再把寄存器r3里存放的值压栈, 注意位置是(sp+4)
8410: e59f0020 ldr r0, [pc, #32] ; 8438 <main+0x44> //字符串地址存入寄存器r0
8414: e3a0100b mov r1, #11 // r1存入11
8418: e3a02016 mov r2, #22 // r2存入22
841c: e59f3018 ldr r3, [pc, #24] ; 843c <main+0x48> //把"nono"字符串的地址存入寄存器r3
8420: ebffffea bl 83d0 <myprintf> //调用myprintf
8424: e3a03000 mov r3, #0
8428: e1a00003 mov r0, r3
842c: e24bd004 sub sp, fp, #4
8430: e8bd8800 pop {fp, pc}
8434: 00001e6c andeq r1, r0, ip, ror #28 // 7788的十六进制值存放在此
8438: 00008494 muleq r0, r4, r4
843c: 000084b4 // "nono"字符串的地址是0x84b4
000083d0 <myprintf>:
83d0: e92d000f push {r0, r1, r2, r3}
83d4: e92d4800 push {fp, lr}
83d8: e28db004 add fp, sp, #4
83dc: e59b0004 ldr r0, [fp, #4] //r0存放 line指针变量指向的地址,由此可看出line指针变量它的地址是(fp+4)也就是(sp+8)
83e0: ebffffb7 bl 82c4 <_init+0x20> // printf(line);
83e4: e24bd004 sub sp, fp, #4
83e8: e8bd4800 pop {fp, lr}
83ec: e28dd010 add sp, sp, #16
83f0: e12fff1e bx lr
从main函数跳过来时,栈里存放的内容顺序:
['K' ]
[7788 ]
myprintf函数的前两句压栈后,栈里存放的内容顺序:
['K' ]
[7788 ]
["nono"字符串的地址] // 由r3寄存器存放压栈
[22 ] // r2寄存器
[11 ] // r1寄存器
&line--> ["hello."字符串地址] // r0寄存器
[返回地址 ] // lr寄存器
sp--> [fp寄存器原内容 ] //最后栈顶在此内存单元位置
//注意,全部参数的地址都是连续的,只要获取其中一个参数的地址,即可通过偏移获取其它所有参数的值
//myprintf函数里的指针变量的地址是在sp+8字节的位置, 即&line + 4字节即是参数11的地址.
///
测试参数取值的代码:
test.c
#include <stdio.h>
void myprintf(char *line, ...)
{
unsigned long *p = (unsigned long *)(&line);
printf("%s\n", *p++);
printf("%d\n", *p++);
printf("%d\n", *p++);
printf("%s\n", *p++);
printf("%d\n", *p++);
printf("%c\n", *p++);
}
int main(void)
{
myprintf("hello test %d, %d, %s, %d, %c\n", 11, 22, "nono", 7788, 'K');
return 0;
}
程序执行后的输出结果:
^_^ /mnt # ./a.out
hello test %d, %d, %s, %d, %c
11
22
nono
7788
K
版权声明:本文为jklinux原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。