在x86平台下的函数参数传递

本文介绍了一个通过修改printf函数实现参数地址打印的方法。该方法适用于32位编译器,通过指针移动访问不同类型的参数,并展示如何在栈中排列参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在学习ARM过程中,想要了解函数调用时参数传递是如何用堆栈来实现的,就需要写一段代码将每个参数的地址打印出来,查看堆栈的存储内容;以重写printf为例,首先查看x_86平台下的各数据类型所占的字节数,

sizeof int           = 4
sizeof char        = 1
sizeof float        = 4
sizeof double    = 8
sizeof char *      = 4
sizeof int  *        = 4
sizeof flaot*       = 4
sizeof double*   =4
sizeof int **       = 4

定义一个重写 printf 的函数,

int test_printf(const char *format, ...)

再定义一个结构体,在main中定义一个结构体变量,赋值,并传递给test_printf()函数,在test_printf中通过指针的移动进行访问

struct person{
    char *name;
    int age;
    char score;
    int id;
};//定义结构体

main:

struct person per= {"www.100ask.org",21,'A',123456};

在main函数中向test_printf()传递三个参数,通过指针的移动进行访问,并将每个变量的地址打印

push_test("abcd",123,per);  //传递参数

 

int test_printf(const char *format, ...)
{
    char *p =(char *) &format; //将一个char型指针指向第一个参数的地址
    int i;
    struct person  per1;


    printf("p value :0x%x\n",p); //打印p的值,就是第一个参数的地址
    printf("p addr :0x%x\n",&p);//打印p的地址
    printf("arg1 : %s\n",format);//打印出第一个参数
    printf("format addr :0x%x\n",&format);
  
    printf("/-------------------------1\n");
    p = p + (sizeof(char *));//将指针向上移动,移动第一个参数的大小
    printf("p value :0x%x\n",p);//打印出现指针的值

    i = *((int *)p);//将p强制类型转换成指向int型的指针,并把指向的内容赋给变量i
    printf("arg2 : %d\n",i);//打印出变量i
    printf("&i = %p\n",&i);//打印出i的地址


    printf("/-------------------------2\n");
    p = p + sizeof(int);//再将指针向上移动,移动第二个参数的大小
    per1 = *((struct person*)p);//将指针强制类型转换为指向结构体的指针,并将指向的内容赋给per1
    printf("p value :0x%x\n",p);//打印出现在指针的值

    printf(" &per1.name  = %p ,  pe1.name  = %s\n" ,&per1.name,per1.name);
    printf(" &per1.age     = %p ,  pe1.age     = %d\n" ,&per1.age ,per1.age );
    printf("&per1.score   = %p ,  pe1.score   = %c\n" ,&per1.score,per1.score);
    printf("&per1.id         = %p ,  pe1.id         = %d\n" ,&per1.id,per1.id);

//打印出结构体变量的地址与值

使用32位编译器进行编译

gcc -m32 -o my_printf my_printf.c

p value :0xffb01210
p addr :0xffb011fc
arg1 : abcd
format addr :0xffb01210
/-------------------------1
p value :0xffb01214
arg2 : 123
&i = 0xffb011f8
/-------------------------2
p value :0xffb01218
arg3 : .name = www.100ask.org; .age = 21; .score = A; .id = 123456
 &per1.name = 0xffb011c8 ,  pe1.name = www.100ask.org
  &per1.age = 0xffb011cc ,   pe1.age = 21
&per1.score = 0xffb011d0 , pe1.score = A
   &per1.id = 0xffb011d4 ,    pe1.id = 123456

由输出参数成功可见,参数在栈中由低地址到高地址依次排列。地址的间隔为各个数据类型所占的内存大小。变量的地址在堆栈中较低于参数的地址,也是依次排列。通过不断的向高地址移动指针,即可将参数不断地读入函数中。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

### 函数参数传递机制 在x86架构下,函数调用约定规定了数如何从调用方传给被调用方。对于原子(标量)数或复杂数的不同部分有特定的分配顺序;这些数可以放在栈上、寄存器中或者是两者的组合形式[^2]。 #### 寄存器与栈的作用 当涉及到浮点数或其他类型的数值时,在某些情况下会优先考虑使用XMM寄存器来存储这类数据而不是传统的整型寄存器。这不仅提高了处理效率还简化了程序逻辑的设计[^1]。 #### 参数传递方式 具体来说: - 对于前四个小型整数或指针大小的数 (即`RCX`, `RDX`, `R8`, 和 `R9`) 将通过通用目的寄存器直接传送; - 如果存在更多数量的小型数,则超出的部分会被压栈内; - 浮点数通常利用 XMM0 至 XMM7 这样的SIMD寄存器来进行快速传输; - 大尺寸结构体则可能需要借助内存位置作为间接地址进行交换[^3]。 ```assembly ; Example of passing parameters via registers and stack in x86_64 assembly push rbp ; Save base pointer mov rbp, rsp ; Set up new base pointer sub rsp, 32 ; Allocate shadow space for call ; Assume we have three arguments: two integers and one float. ; Integer args go into RCX and RDX while the float goes into XMM0. mov rcx, arg1 ; First integer argument -> RCX mov edx, arg2 ; Second integer argument -> EDX movss xmm0, dword ptr [arg3] ; Float argument loaded into XMM0 from memory location 'arg3' call some_function ; Call target function which expects these inputs according to calling convention add rsp, 32 ; Clean up allocated shadow space after returning from callee pop rbp ; Restore previous base pointer before exiting caller routine ret ; Return control back to original program flow ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值