C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言可变量参数机制

C语言可变量参数机制的使用小结

作者:学逆向的

本文深入解析C语言可变参数机制,涵盖va_list、va_start、va_arg、va_end四大宏的使用规范与底层原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

函数参数分类

  1. 基本类型参数:int、float、char等,传递时传递值拷贝,函数内部修改不会影响外部原变量
  2. 复杂类型参数:数组、结构体、指针
    ○ 数组:传参时自动退化为指针,函数内拿到的是数组首地址
    ○ 结构体:可以传值(拷贝整个结构体),也可以传结构体指针(推荐,节省拷贝开销)
    基本类型参数定义时必须写类型,void func(int a),不能省略类型

可变参数(不定量参数)基础

  1. 关键字:… 省略号,表示可变参数列表
  2. 头文件:<stdarg.h>,提供va_list、va_start、va_arg、va_end这一组宏
  3. 语法规则:可变参数…必须放在参数列表最后面,前面至少要有 1 个固定参数
int Sum(int count, ...);// ✅合法
int Sum(...);           // ❌错误,...不能放前面

四大可变参数宏

宏作用
va_list ap;定义一个可变参数迭代器变量 ap,本质是指针
va_start(ap, 固定参数名)初始化 ap,让 ap 指向第一个可变参数;必须指定最后一个固定参数
va_arg(ap,类型)取出当前 ap 指向的参数,并且 ap 自动移动到下一个参数;必须指定正确类型
va_end(ap)清理 ap,结束可变参数读取,防止内存问题
#include <stdio.h>
#include <stdarg.h>
// count:传入参数个数,后面是可变参数
int Sum(int count, ...)
{
    va_list ap;
    va_start(ap, count); // ap指向第一个可变参数
    int res = 0;
    for(int i = 0; i < count; i++)
    {
        res += va_arg(ap, int); // 取出int类型参数
    }
    va_end(ap); // 收尾
    return res;
}
int main(void)
{
    printf("%d\n", Sum(3,10,20,30)); // 3个参数:10+20+30
    return 0;
}

printf 原理

int printf(const char *format, ...);
  1. format是固定参数(格式化字符串),后面…接收任意数量、任意类型参数
  2. 通过解析format字符串里的%d %f %s,判断后面可变参数的类型,再调用va_arg取出对应参数
    ⚠️坑:格式符和实际参数类型不匹配,会直接得到乱码(未定义行为),va_arg不会自动识别类型,你写什么类型它就取多少字节

底层 & 反汇编视角

va_start 的底层:拿到栈上可变参数区域的起始地址,ap 本质就是栈指针

  1. 可变参数本质:调用方把所有参数按调用约定放到寄存器 / 栈上;被调用函数靠 va 系列宏,手动遍历这片内存
  2. 没有任何机制帮你记录:可变参数一共有多少个、每个是什么类型!必须靠固定参数传递信息(printf 靠字符串标记、Sum 靠 count)
  3. 调试:断点进可变参数函数,查看ap的值,内存窗口跟着 ap 查看栈上参数;反汇编能看到 va_start/va_arg 展开后的内存读写指令

可变参数常见坑

  1. 不能自动识别参数数量、类型,类型写错直接读错内存
  2. va_arg类型不能写char/short,可变参数会默认整型提升,char 会被提升为 int
  3. va_start、va_end必须成对使用
  4. …只能放在参数末尾

到此这篇关于C语言可变量参数机制的使用小结的文章就介绍到这了,更多相关C语言可变量参数机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文