自定义可变参函数

文章目录

一、可变参函数

  在C语言编程中,我们经常会遇到一些参数个数可变的函数,如:int printf(const char *format, ...);scanf()等等。它除了第一个参数format固定以外,后面跟着的参数的个数和类型是可变的,其中...称作参数占位符。
  可是上述中的可变参函数是如何对变化的参数进行处理的呢?我们又是否可以编写属于自己的可变参函数呢?当然可以,不过需要首先来认识一下va_list的成员和功能。

1、va_list简介

  va_list是C语言中用于解决变参问题的一组宏,它定义于标准库stdarg.h中,通过命令$ man va_arg也可以从手册中查询到va_list的用法,下面将详细介绍va_list各成员的功能和实现原理。

2、va_list成员

成员 原型 描述 版本
va_start void va_start(va_list ap, last); 初始化va_list类型的变量ap C89
va_arg type va_arg(va_list ap, type); 返回可变参数中类型为type的参数 C89
va_end void va_end(va_list ap); 释放指定的va_list C89
va_copy void va_copy(va_list dest, va_list src); 拷贝va_list的内容 C99

3、va_list原理

  由man手册可知,va_list相关成员定义在stdarg.h文件中,经过查找,该头文件的路径为:/usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h。当前PC使用的GCC版本是gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11)

#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)
#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L || defined(__GXX_EXPERIMENTAL_CXX0X__)
#define va_copy(d,s)    __builtin_va_copy(d,s)
#endif
#define __va_copy(d,s)  __builtin_va_copy(d,s)

  从上述定义中,可以发现va_list成员都是通过GCC内建函数实现的。这部分对于非编译器研究者的我来说,实在有些晦涩,难以跟踪。因此暂时就先不继续探究了。

4、va_list用法

1)printk函数

  printk()是内核空间的打印函数,其实现中也借助了va_list处理可变参数。printk()原码如下所示:

asmlinkage __visible int printk(const char *fmt, ...)
{
		va_list args;
		int r;
	
		va_start(args, fmt);
		r = vprintk_func(fmt, args);
		va_end(args);
	
		return r;
}
EXPORT_SYMBOL(printk);

具体实现就不再此处分析了,后面会单独分析Linux printk() 实现的原理。

2)自定义函数

/* @file: a.c */
#include <stdio.h>
#include <stdarg.h>

static void show_numbers(int num, ...)
{
        va_list va; 
        va_start(va, num);
        while(num--) {
                printf("%d\n", va_arg(va, int));
        }   
        va_end(va);
}

int main(void)
{
        show_numbers(3, 110, 120, 114);
        return 0;
}

以下是程序运行结果:
自定义可变参函数

上一篇:codeforces757F Team Rocket Rises Again【支配树+倍增+拓扑+spfa】


下一篇:C语言可变参数 "..."