每一个程序员写的第一个程序大概鼎鼎大名的hello world了,一个简简单单,就那么几行的小程序开启了另一个世界的大门,从此,我们发现了更广阔的世界。而每一个C程序员接触到的第一个函数(除去main函数不算)大概就是printf函数了。这个函数用法很简单灵活,然而里面暗藏玄机,包含了C语言诸多的内容。
  不知道大家发现没有,printf函数的参数个数是可变的。下面这三个函数都可以完美运行。

  printf("hello,world\n");
  printf("this is a int num %d\n",a);
  printf("this is a string %s,and a num %d\n",s,a);

  上面这三个函数分别有一个、两个、三个参数。而我们学的C语言函数参数的个数都是固定的,比如下面这个swap()函数,必须传入两个int型指针才能运行。

void swap(int *a,int *b)
{
    int tmp=*a;
    *a=*b;
    *b=tmp;
}

  而且C语言里面没有类似C++当中类似默认构造函数的机制,那么printf函数参数个数可变是怎么实现的呢?
  printf()函数的声明形式是这样:
  

int printf(char *fmt,...)

  第一个参数是char型指针,第二个参数是”…”???正是这个“…”实现了printf的变长参数表。
  
  标准头文件stddrg.h当中包含了一组宏定义,它们对如何遍历参数表进行了定义。
  va_list 类型用于声明一个变量,该变量将依次引用各参数。我们将该变量称为ap,意思是“参数指针”。宏va_start将ap初始化为指向第一个无名参数的指针。在使用ap 之前,该宏必须被调用一次。参数表必须至少包括一个有名参数,va_start将最后一个有名参数作为起点。
  每次调用va_arg,该函数都将返回一个参数,并将ap 指向下一个参数。va_arg 使用一个类型名来决定返回的对象类型、指针移动的步长。最后,必须在函数返回之前调用va_end,以完成一些必要的清理工作。

下面使用变长参数表实现printf的两个简单功能,打印整数和字符串。

//main.c文件内容
#include<stdio.h>
#include "print_dec.h"
#include<stdarg.h>
void print(char *fmt,...);
int main()
{
    int i;
    char s[100];
    scanf("%d",&i);
    scanf("%s",s);
    print("this is a int: %d\n",i);
    print("this is a string:\n%s\n",s);
    return 0;
}
void print(char *fmt,...)
{
    int i;
    char *m;
    va_list p;
    va_start(p,fmt);
    //将有名参数fmt作为起点,p此时指向第一个无名参数
    char *tmp;

    for(tmp = fmt;*tmp;tmp++)
    {
        if(*tmp!='%')
        {
            putchar(*tmp);
            continue;
        }
        switch(*++tmp)
        {
            case 'd':
                i = va_arg(p,int);
                //获得一个无名参数,同时p指向下一个无名参数
                print_dec(i);//打印数字i
                break;
            case 's':
                m = va_arg(p,char *);
                //获得一个无名参数,同时p指向下一个无名参数
                for(;*m;m++)
                    putchar(*m);
        }   
    }
    va_end(p);//结束时的清理工作
}
//print_dec.c文件内容
#include<stdio.h>
void print_dec_(int i)//这个函数可以实现打印正整数
{
    if(i == 0)
        putchar(i);
    else 
    {
        print_dec_(i/10);
        int m = i%10 + '0';
        putchar(m);
    }
}
void print_dec(int i)//实现打印0和正整数
{
    if(i==0)
        putchar('0');
    else
        print_dec_(i);
}
//print_dec.h文件内容
void print_dec(int i);
void print_dec_(int i);
//makefile文件内容
TAG=print.out
SRC=main.c print_dec.c print_dec.h
${TAG}:${SRC}
    gcc -o ${TAG} ${SRC}
clean:
    rm -rf *.out

程序执行效果: