函数调用和变长参数

函数调用

  1. 函数参数传入寄存器(保护现场过程省略)或压栈
  2. 调用函数代码

函数中按预设的入栈顺序使用参数

调用惯例

基于函数调用过程中参数传递原理,函数调用方与被调用方对于传递和使用参数需要有一致的理解。因此需要调用惯例。

调用惯例:

  • 函数参数的传递顺序和方式
  • 栈的维护方式:函数调用时将所有函数参数压栈,在调用完成后需将所有参数出栈。出栈的工作可由函数的调用方完成,也可由函数本身(被调用方)完成。
  • 名字修饰的策略:在原函数名基础上按设定规则加上修饰,汇编代码中可查看

C语言默认调用惯例cdecl

c 复制代码
// 选择调用惯例
// 定义
__attribute__((cdecl)) void foo(int a, int b)
{
    printf("a=%d, b=%d\n", a, b);
}
// 声明
__attribute__((cdecl)) void foo(int, int);

x86-64 有统一的调用约定(System V AMD64 ABI), 不能自定义

变长参数函数

变长参数的标准库头文件:stdarg.h

实现不定参数的步骤:

  1. 定义函数:函数的最后一个参数使用省略号(...),省略号前可以有固定参数。
  2. 函数内创建va_list变量:这是一个在stdarg.h中定义的类型,用于访问参数列表。
  3. 初始化参数列表:使用va_start宏和最后一个固定参数来初始化va_list变量。
  4. 访问参数:使用va_arg宏和va_list变量来访问每个参数。
  5. 结束参数列表访问:使用va_end宏清理va_list变量。
c 复制代码
#include <stdarg.h>
#include <stdio.h>
// 变长参数函数示例
 
int func_name(int arg1, ...)
{
	va_list ap;
	va_start(ap, arg1);
    int arg2 = va_arg(ap, int);
    double arg3 = va_arg(ap, double);
    char *arg4 = va_arg(ap, char *);
    printf("%d %lf %s\n", arg2, arg3, arg4);
    va_end(ap);
    return arg1;
}
int main() {
    func_name(3, 1, 1.5, "abc");
    return 0;
}

arg1是函数的第一个参数,省略号(...)表示后续的参数数量是可变的。

要使用这种机制,需要包含stdarg.h头文件,它提供了处理可变参数的宏和函数。

可见可变参数的函数的参数分为两种类型,固定参数(只表意)和不定参数,固定参数在前不定参数在后。

变长参数宏

##为宏字符串连接

c 复制代码
// 1.gcc编译下的定义
#define mprintf(arg...) fprintf(stdout, ##arg)
// 2.MSVC下的定义
#define mprintf(...) fprintf(stdout, __VA_ARGS__)
// 使用
mprintf("%d %lf %s\n", 1, 1.5, "abc");
相关推荐
白山编程大哥3 小时前
Lua 中使用 C 语言的用户自定义类型——userdata
c语言·junit·lua
无敌贵点大王4 小时前
RTThread学习记录13——关于要使用一个外设,RTThread与cubemx到底要怎么配合?
c语言·stm32·学习·rtthread
phltxy4 小时前
C 语言动态内存管理:从申请空间到安全释放
c语言·开发语言·ui
上位机妹子5 小时前
C 语言 数组删除指定元素(快慢指针法)
c语言·数据结构·算法
心中有你02145 小时前
【路径规划】A*寻路算法最通俗易懂讲解(C语言完整实现+详细注释)
c语言·开发语言·算法
大圣编蚕6 小时前
C语言入门:基本语法汇总
c语言·开发语言
en.en..7 小时前
C语言---宏
c语言·开发语言
白山编程大哥7 小时前
适合初学者练习的 C/C++ 开源项目推荐
c语言·c++·开源
小灰灰搞电子8 小时前
C 语言 <stdlib.h> 头文件详解
c语言·stdlib.h