形式及用法
C语言可以实现可变参函数,可以输入任意个参数个数。打印函数为了实现格式化输出常常都是可变参函数或可变参宏函数。
可变参函数格式:int vaExample (char *fmt, ...)
可变参宏函数格式:#define VATEST(fmt, ...) vaExample(fmt, ##__VA_ARGS__)
- 三个点(三个连续的英文格式的句号)表示0到n个参数,参数间用逗号隔开。
- 宏函数中的
##__VA_ARGS__
用于替换三个点。 - 对于宏函数变参只能是最后一个参数,且函数至少要有一个定参。
- 变参的实现一般是通过栈实现,所有参数必须通过栈来传递否则会出错。
- 变参的实现需要编译器的参与,不同编译器间实现方式会有些区别。
- 变参的实现又标准C库
stdarg.h
提供接口。 - 紧挨变参左侧的定参为变参提供栈地址参考,大多数情况下还用于确定变参个数。
实现方法
通常情况下, 不确定参数个数的参数传递都使用堆栈, RISC 与 CISC 相同. 确定第一个参数的位置是通过最后一个确定的参数在参数堆栈中的位置。
例如: printf(fmt, …); 这个函数通过 fmt 参数在堆栈中的位置, 然后通过某种算法偏移量来确定后面各个参数的位置. 但是, 前提条件是: fmt 不能是寄存器传参, 也就是说 fmt 必须使用堆栈传参, 这个算法才能生效. 当然, 在绝大多数编译器上, 对待变参数数量传参都是用堆栈, 类似的方法都可以处理。
但是 GCC 在 -O3 优化等级时使用了 -finline-function 优化选项. 此选项对使用频率较高的函数进行了内联处理, 很多参数变成了寄存器直接传参, 对于可变个数参数传递, 作为基准参数(上例中的 fmt)如果是寄存器类型, 后面的参数将会有灾难性的后果. 2009.06.30 GCC -O3 测试 ioctl 时, 产生了此类问题. ioctl 的第二个参数为寄存器变量, 导致第三个参数直接错误。
所以强烈推荐使用 GCC 编译 SylixOS 时, 使用 GGC 内建的 stdarg.h 头文件。
实现变参最重要的就是一个类型,三个宏函数:
va_list
:变参列表类型。
va_start
:初始化变参列表对象,获取可变参数列表的第一个参数的地址。
va_arg
:获取可变参数的当前参数,返回指定类型并将指针指向下一参数。
va_end
:清空va_list可变参数列表,需要和va_start配对使用。
SylixOS中stdarg.h 头文件关键代码。
/*********************************************************************************************************
GCC 这里直接使用编译器内建函数, 否则 -O3 (-finline-function) 优化时可能出现错误
*********************************************************************************************************/
#elif defined(__GNUC__)
#ifndef va_start
#define va_start(v,l) __builtin_va_start(v,l)
#endif /* va_start */
#ifndef va_end
#define va_end(v) __builtin_va_end(v)
#endif /* va_end */
#ifndef va_arg
#define va_arg(v,l) __builtin_va_arg(v,l)
#endif /* va_arg */
#ifndef _VA_LIST_DEFINED
#ifndef _VA_LIST
#ifndef _VA_LIST_T_H
#ifndef __va_list__
typedef __builtin_va_list va_list;
#endif /* __va_list__ */
#endif /* _VA_LIST_T_H */
#endif /* _VA_LIST */
#endif /* _VA_LIST_DEFINED */
#ifndef _VA_LIST
#define _VA_LIST
#endif /* _VA_LIST */
#ifndef _VA_LIST_DEFINED
#define _VA_LIST_DEFINED
#endif /* _VA_LIST_DEFINED */
#ifndef _VA_LIST_T_H
#define _VA_LIST_T_H
#endif /* _VA_LIST_T_H */
#ifndef __va_list__
#define __va_list__
#endif /* __va_list__ */
#if !defined(__STRICT_ANSI__) || \
(defined(__STDC_VERSION__) && (__STDC_VERSION__ + 0 >= 199900L)) || \
defined(__GXX_EXPERIMENTAL_CXX0X__)
#ifndef va_copy
#define va_copy(d,s) __builtin_va_copy(d,s)
#endif /* va_copy */
#endif /* __STRICT_ANSI__ ... */
#ifndef __va_copy
#define __va_copy(d,s) __builtin_va_copy(d,s)
#endif /* __va_copy */
应用举例
/*********************************************************************************************************
**
** 中国软件开源组织
**
** 嵌入式实时操作系统
**
** SylixOS(TM) LW : long wing
**
** Copyright All Rights Reserved
**
**--------------文件信息--------------------------------------------------------------------------------
**
** 文 件 名: vaExample.c
**
** 创 建 人: Hou.JinYu (侯进宇)
**
** 文件创建日期: 2017 年 12 月 20 日
**
** 描 述: 可变参操作例程
*********************************************************************************************************/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
/*********************************************************************************************************
宏函数
*********************************************************************************************************/
#define VATEST(fmt, ...) vaExample(fmt, ##__VA_ARGS__)
/*********************************************************************************************************
** 函数名称: vaAnalysis
** 功能描述: 变参解析
** 输 入 : fmt 格式字串
** valist 变参对象
** 输 出 : 错误号
*********************************************************************************************************/
static int vaAnalysis (char *fmt, va_list valist)
{
int i = 0;
while (1) {
if (i > (strlen(fmt) - 1)) {
break;
}
if (fmt[i] != '%') {
break;
}
switch (fmt[i+1]) {
case 'c':
printf("[%d] 'c' = %c\n", i, va_arg(valist, int));
break;
case 'd':
printf("[%d] 'd' = %d\n", i, va_arg(valist, int));
break;
case 'u':
printf("[%d] 'u' = %d\n", i, va_arg(valist, uint_t));
break;
case 's':
printf("[%d] 's' = %s\n", i, va_arg(valist, char *));
break;
default:
printf("error, fmt[i+1] = %02x\n", fmt[i+1]);
break;
}
i += 2;
}
return 0;
}
/*********************************************************************************************************
** 函数名称: vaExample
** 功能描述: 例程入口函数
** 输 入 : fmt 格式字串
** ... 变参对象
** 输 出 : 错误号
*********************************************************************************************************/
static int vaExample (char *fmt, ...)
{
va_list valist;
printf("vaExample\n");
va_start(valist, fmt);
vaAnalysis(fmt, valist);
va_end(valist);
return 0;
}
/*********************************************************************************************************
** 函数名称: main
** 功能描述: 例程入口函数
** 输 入 : argc 参数个数
** argv 参数值数组
** 输 出 : 错误号
*********************************************************************************************************/
int main (int argc, char **argv)
{
VATEST("%c%u%s%d", 'a', 123, "abcsef", -789);
return (0);
}
/*********************************************************************************************************
END
*********************************************************************************************************/
执行结果:
[root@sylixos:/root]# /apps/sylixosAppExample/srcSylixos/vaExample
vaExample
[0] 'c' = a
[2] 'u' = 123
[4] 's' = abcsef
[6] 'd' = -789
[root@sylixos:/root]#
[root@sylixos:/root]#
[root@sylixos:/root]#