esp32 c3 开发板乐鑫8089芯片代理商嵌入式函数调用规范,嵌入式软件工程(project)中可以包括多个源文件和二进制库文件。函数调用范保证不同文件之间函数的正确调用。常用的丽数调用规范有 stdcall、cdecl、fastcall 和 thiscall 等。 stdcall 函数调用式又称为 Pascal 调用,应用于 Microsoft 的 C/C++编译器。cdecl 调用方式又称为(调用方式,是C语言缺省的函数调用方式。fastcall 是一种快速调用方式,在 x86台上利用ecx和edx寄存器传递函数的第 1 个和第 2个32 位参数。thiscall是C++类成员函数缺省的调用方式。
ARM处理器过程调用规范(ARM Procedure Call Standard,APCS)定义ARM 处理器应用程序中的函数调用规则。使用多个通用寄存器传递参数,返回数结果,提高函数调用过程的速度。RISC-V gcc 编译工具使用 RISC-V 应用程序二进制接口(Application BinarInterface,ABI)调用规范。与其他调用规范相比,esp32 c3 开发板乐鑫8089芯片代理商RISC-V ABI规范使用更多寄存器传递参数,调用过程更加快捷。采用 ABI规范可以实现汇编语言程序与 C 语言程序之间、汇编语言程序与C++程序之间、源程序与二进制库文件之间函数的相互调用。
函数调用过程
示例5.7中包括函数 int max(int a,int b)和主程序 int main()。主程序 main中调用函数 max和 printf,main 是调用者,函数 max 和 printf 是被调用者。
示例 5.7: 函数调用。
1 # include <stdio.h>
2 int max(int a,int b);
3 int main() //主程序
4 {int maxvalue=0;
5 int a = 50,b = 150;
6 maxvalue = max(a,b); //调用源文件的函数
7 printf(“max=%d”,maxvalue); //调用二进制库中的函数
8 return 1;
9 }
10 int max(int a,int b) //函数,返回大值
11 {
12 if(a>=b)
13 return a;
14 else
15 return b;
16 }
如图 5.7 所示为函数调用过程,esp32 c3 开发板乐鑫8089芯片代理商整个过程可分为 7 个阶段:
1、将参数存储到函数能够访问到的数据栈,数据栈是内存或者寄存器;
2、在 RISC-V RV32I指令集中,用jal或 jalr 指令跳转到函数开始位置;
3、函数获取局部资源,从数据栈中提取参数,并备份函数中需要使用的寄存器;
4、执行函数中的指令;
5、将返回值存储到调用者能够访问到的数据栈,恢复寄存器,释放局部存储资源;
6、从函数中返回,用 ret 指令回到主程序;
7、主程序从数据栈中读取返回数据。
函数声明和实现时必须符合调用约定,以保证函数之间的正确调用。
函数声明
函数定义包括名称、形式参数列表、函数主体和返回类型 4 个部分。
名称:每个函数都必须拥有的名字。
形式参数列表:保存传递给函数值的变量列表。如果不需要传输参数,则形式参数列表为空。
主体:函数中运算或处理事务的一组语句。
返回类型:函数返回值的数据类型。
链接过程中,如果链接器扫描到新的函数名称,则在同一个目标文件中、同一工程中的其他目标文件中,或者在与当前工程中相关的二进制库文件中寻找同名的函数实体,然后链接起来。
1.文件内调用
示例 5.7 中,调用者 main 和被调用函数 max 在同一文件中。源序中第 3~9行是 main 函数体,第 10~16行是 max 函数体源序第 2行定 max 数原型。接时,链接器在当前文件中直接查找函数体。
对于 C/C++语言程序,如果调用者和被调用者(函数)在同一源程序文件中,则只需要在调用语句之前声明函数原型,调用者就可以直接调用该函数了。
对于 RISC-V 汇编语言程序,esp32 c3 开发板乐鑫8089芯片代理商同一源文件内的函数调用更加简单,不需要声明函数原型,甚至不需要声明函数原型,甚至不需要遵守调用规范,编译器可以直接查找函数的名称(标签)。在下列帮序中,主程序直接调用函数_fini。
1 la tl,_fini //获取函数标签地址
2 jalr tl //调用函数
3 _fini: //函数体
4 ret //返回
2.文件之间的函数调用
如果调用者和被调用者不在同一个文件中,则需要在源程序中对函数进行声明告诉链接器,文件中哪些函数的函数体在文件外部,以及文件内哪些函数体可以被外部调用。
在 RISC-V gas 汇编语言中,用命令“.global”声明全局函数或全局符号。汇编语言文件中,先声明文件中的外部函数和可以被外部调用的函数。在示例 5.8中,_muldf3 是外部函数,delay_lms 是可以被外部调用的函数体。
示例 5.8:声明全局函数和符号。
1 .text //声明外部函数全局标签
2 .global _muldf3 //声明为全局标签
3 .globaldelay_lms //声明函数类型
4 . type delay_lms,@function //函数标签
5 delay_lms:
6 .......
7 call _muldf3 //调用外部函数
8 .......
9 .end
在C语言中,如果需要调用外部文件中的雨数,则必须在 C 语言程序的开始部分进行声明。
如果所调用函数的函数体在同工程中的另外一个源程序文件中,则可以在调用者所在的源程序文件中添加被调用的函数原型声明。esp32 c3 开发板乐鑫8089芯片代理商添加函数原型声明的方式有两种:一,在源文件中添加声明函数原型的语句;第二,在源文件中包含有函数原型声明的头文件。
例如,在源文件 sourcel.c 中调用源文件 source2.c 中的函数 delay 1s,则可以在 sourcel.c 文件中添加函数原型语句:
void delay_lms(int mstimes);
或者,把声明函数原型的语句写到 source2.h 文件中,然后在 sourcel.c 文件前添加语句:
#include“source2.h”
如果被调用的函数体在汇编语言程序中,或者在二进制目标文件中,则在调用函数的C源程序中添加函数原型声明语句,esp32 c3 开发板乐鑫8089芯片代理商且在函数原型前添加关键字“extern”说明是外部函数。例如,示例 5.9 的第 1行中,delay_lms 是在汇编语言中定义的函数。
示例 5.9:C语言外部函数声明
1extern void delay_lms(unit32_t count); //外部函数原型
2 int main(void)
3 {
4 ......
5 delay_lms(50); //调用外部函数
6 ......
7 }