一段程序说明C语言不同调用约定的区别

7 篇文章 0 订阅

x86 程序的调用约定很多,常用调用约定由:cdecl, stdcall, fastcall。
x86-64 程序的调用约定只有两种:Microsoft x64 calling convention, System V AMD64 ABI。

x86 程序的调用约定

编译方法

64位平台编译32位汇编的方法如下

gcc -m32 -S test.c

主要区别

区别cdeclstdcallfastcall
寄存器ECX, EDX
入栈全部参数全部参数从第三个参数开始
调用者清理
被调用者清理

通过具体汇编代码进行说明

以下汇编均通过 Compiler Explorer 生成。使用 x86-64 gcc 11.1

cdecl

cdecl 是 x86 程序和库的默认调用约定。

int callee(int,int,int);
int caller(void) 
{
    return callee(1,2,3) + 5;
}
  • 参数从右到左进行压栈
  • 调用者清理参数
caller:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp		; reserves 8 bytes for local variables
        subl    $4, %esp		; for alignment
        pushl   $3
        pushl   $2
        pushl   $1
        call    callee
        addl    $16, %esp		; clean parameters (3 paras + 1 alignment)
        addl    $5, %eax
        leave
        ret

stdcall

stdcall 主要用于 32位的 windows。

__attribute__((stdcall)) int callee(int,int,int);
int caller(void) 
{
    return callee(1,2,3) + 5;
}
  • 参数从右到左进行压栈
  • 被调用者清理参数
caller:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $4, %esp		; for alignment
        pushl   $3
        pushl   $2
        pushl   $1
        call    callee
        addl    $4, %esp		; only clean the alignment
        addl    $5, %eax
        leave
        ret

fastcall

__attribute__((fastcall)) int callee(int,int,int);
int caller(void) 
{
    return callee(1,2,3) + 5;
}
  • 前两个参数由 ECX, EDX 进行传递
  • 后面到参数自右向左进行压栈
  • 被调用者清理
caller:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $12, %esp		; for alignment
        pushl   $3
        movl    $2, %edx		; second parameter
        movl    $1, %ecx		; first parameter
        call    callee
        addl    $12, %esp		; only clean the alignment
        addl    $5, %eax
        leave
        ret

x86-64 调用约定

x86-64 调用约定主要使用更多的寄存器来传递参数。

int callee(int,int,int,int,int,int,int);
int caller(void)
{
    return callee(1,2,3,4,5,6,7) + 8;
}

Microsoft x64 calling convention

微软x64调用约定

  • 使用RCX, RDX, R8, R9四个寄存器用于存储函数调用时的4个参数(从左到右),
  • 使用XMM0, XMM1, XMM2, XMM3来传递浮点变量
  • 其他的参数直接入栈(从右至左)
  • 整型返回值放置在RAX中,浮点返回值在XMM0中。

汇编

使用 x64 msvc v19.10 (WINE)

caller  PROC
$LN3:
       sub     rsp, 72                             ; 00000048H
       mov     DWORD PTR [rsp+48], 7
       mov     DWORD PTR [rsp+40], 6
       mov     DWORD PTR [rsp+32], 5		; other paras on stack
       mov     r9d, 4
       mov     r8d, 3
       mov     edx, 2
       mov     ecx, 1
       call    callee
       add     eax, 8
       add     rsp, 72                             ; 00000048H
       ret     0
caller  ENDP

System V AMD64 ABI

此约定主要在Solaris,GNU/Linux,FreeBSD和其他非微软OS上使用

  • 使用寄存器RDI, RSI, RDX, RCX, R8和R9存储函数调用时的前6个参数(从左到右)
  • 使用XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 和 XMM7 用来放置浮点变量
  • 其他的参数直接入栈(从右至左)
  • 整型返回值放置在RAX中,浮点返回值在XMM0中。

汇编

使用 x86-64 gcc 11.1

caller:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $8, %rsp
        pushq   $7				; other paras on stack
        movl    $6, %r9d
        movl    $5, %r8d
        movl    $4, %ecx
        movl    $3, %edx
        movl    $2, %esi
        movl    $1, %edi
        call    callee
        addq    $16, %rsp
        addl    $8, %eax
        leave
        ret

参考:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: C语言程序结构是学习C语言的基础,它是一种编程语言,它可以提供一种简单而坚实的框架,帮助程序员更轻松地编写程序。C语言程序结构是由三个基本部分组成的,即:程序文本,变量定义和函数定义。程序文本描述了程序的执行流程,变量定义用来存储数据,而函数定义用来实现程序功能。C语言程序结构可以帮助程序员更好地理解他们的程序,并有助于避免程序中的错误。 ### 回答2: C语言是一种通用的高级编程语言,其程序的基本结构由函数、语句和控制流程组成。在编写C语言程序时,遵循正确的程序结构可以使程序逻辑更清晰、简洁,提高代码的可读性和可维护性。 首先,C语言程序的基本单位是函数。每个C程序都至少包含一个main函数,它是程序的入口。函数是程序的一个独立单元,由一组语句构成。通过定义和调用函数,可以实现代码的重用,提高程序的模块化和可扩展性。在函数中,可以进行变量的声明和赋值,以及其他语句和控制结构的使用。 其次,C语言程序的执行是从main函数开始的,通过按照语句的先后顺序逐行执行,直到遇到return语句或程序结束。C语言提供了多种类型的语句,包括赋值语句、条件语句、循环语句等,可以根据具体的需求选择合适的语句来实现所需的功能。语句的执行结果可以通过变量的值来存储和处理。 控制流程是C语言程序中至关重要的一环,它通过条件语句和循环语句来控制程序的执行流程。条件语句如if-else语句可以根据某个条件的真假选择不同的执行路径。循环语句如for循环、while循环和do-while循环可以重复执行一段代码,直到满足退出条件。控制流程的灵活运用可以实现程序的流程控制和逻辑判断。 此外,为了使程序编写更加规范和易于理解,我们需要按照一定的规则和约定来组织代码。这包括缩进、代码注释、命名规范等。合理的缩进可以使代码的层次结构清晰、易读。注释可以对程序的功能进行说明,方便其他人理解和维护代码。命名规范可以使变量名、函数名等具有一定的语义,增加程序的可读性。 总结来说,C语言程序的结构由函数、语句和控制流程组成。通过合理的程序结构,可以使程序具有清晰的逻辑和良好的可读性,提高程序的可维护性和可扩展性。因此,在编写C语言程序时,我们应该充分理解并遵循良好的程序结构原则,以便更好地实现自己的编程目标。 ### 回答3: C语言是一种高级计算机编程语言,它的程序结构是由一系列的代码组成的。在C语言中,程序的结构是非常重要的,它决定了代码的组织形式和执行顺序。下面我将介绍C语言程序结构的几个重要概念。 首先是C语言程序的入口函数。每个C语言程序必须包含一个名为"main"的函数,它是程序的入口点。程序的执行会从"main"函数开始,并按照顺序执行其中的语句。"main"函数的格式如下: ```c int main() { // 在这里编写代码 return 0; } ``` 接下来是C语言程序的语句。C语言中的语句是用来执行特定操作的。常见的语句包括赋值语句、条件语句和循环语句。赋值语句用于给变量赋值,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码。例如: ```c int x = 10; // 赋值语句 if (x > 0) // 条件语句 { printf("x是正数\n"); } for (int i = 0; i < 10; i++) // 循环语句 { printf("%d\n", i); } ``` 此外,C语言程序还可以包含函数。函数是一段封装了特定功能的代码块,它接受输入参数并返回一个值。通过使用函数,我们可以将代码模块化,提高代码的可读性和复用性。函数的定义和调用如下所示: ```c int add(int a, int b) // 函数定义 { return a + b; } int result = add(2, 3); // 函数调用 printf("结果:%d\n", result); ``` 最后要提到的是C语言的注释。注释用于向其他开发者或自己解释代码的作用,它不会被编译器执行。C语言支持两种注释形式:单行注释和多行注释。示例如下: ```c // 这是一个单行注释 /* 这是一个 多行注释 */ ``` 以上就是关于C语言程序结构的简要介绍。通过合理的程序结构,我们可以更好地组织代码,提高代码质量和可读性。在编写C语言程序时,务必注意良好的程序结构,这将有助于代码的开发和维护。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lylhw13_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值