可变参数列表解析及模拟实现printf函数

可变参数列表解析及模拟实现printf函数

我们学过很多函数,他们都有参数,而且参数的个数都是固定的。但是用的时候我们设计函数函数时却血药参数不是固定为好。例如:我们设计一个求平均数的函数,当求两个平均数时我们会传2个参数,三个数的平均数传3个参数………如果要在一个函数中实现n个数的平均值,这里我们就得靠可变参数列表来实现。
下来先看一个简单的例子,使用可变参数,实现函数求未知参数部分n个数的平均值
代码如下:

#include <stdio.h>
#include<stdarg.h>
int average(int n, ...)
{
    va_list arg;
    int i = 0;
    int sum = 0;
    va_start(arg, n);
    for (i = 0; i < n; i++)
    {
        sum += va_arg(arg, int);
    }
    return sum / n;
    va_end(arg);
}
int main()
{
    printf("%d\n", average(3, 5, 6, 7));
    printf("%d\n", average(6, 1, 2, 3, 4,5,9));
    return 0;
}

运行结果:
这里写图片描述
1.va_list定义了一个arg变量,它用于访问参数列表未确定的部分。
2.va_start对其进行初始化,其第一个参数是va_list变量的名字,第二个参数是省略号前第一个有名字的参数。在初始化的过程,arg被设置为指向可变参数部分第一个参数。
3.为了访问访问参数,我们需要使用va_arg。va_arg这个宏接受两个参数,va_list定义的参数和列表中下一个参数的类型。va_arg返回这个参数的值,并且指向下一个可变参数。
4.最后,访问完最后一个参数时,需要调用va_end将指针置为无效。

va_start宏的定义如下

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )**
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

_INTSIZEOF(n)作用:将n的长度化为int长度的整数倍。

va_arg宏定义如下

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

va_arg作用:返回参数的值,并使arg指向下一个参数。

va_end定义如下

#define va_end(ap) ( ap = (va_list)0 )

va_end作用:指针置为无效。

注意:

  • 可变参数列表必须按照顺序从头到尾逐个访问,当你在访问几个可变参数后想半途中止,这是可以的。但是如果你一开始就想访问中间的参数,那是不行的。
  • 参数列表中至少要有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。
  • 这些宏无法直接判断实际存在参数的数量。
  • 这些宏无法判断每个参数的类型。
  • 如果va_arg中指定了错误的类型,那么后果是不可预测的。

    printf函数的实现
    代码如下

#include "stdio.h"
#include "stdarg.h"
void show(int n)
{
    if (n > 9)
    {
        show(n / 10);
    }
    putchar(n % 10 + '0');
}
void print(const char *format, ...)
{
    va_list arg;
    va_start(arg, format);
    while (*format)
    {
        switch (*format)
        {
        case 's':
        {
            char* ret = va_arg(arg, char*);
            while (*ret)
            {
                putchar(*ret);
                ret++;
            }
            break;
        }
        case 'c':
        {
            int ch = va_arg(arg, char);
            putchar(ch);
        }
        break;
        case 'd':
        {
            int ret = va_arg(arg, int);
            show(ret);
        }
        break;
        default:
            putchar(*format);
            break;
        }
        format++;
    }
}

int main()
{
    print("s ccc d.\n", "hello", 'b', 'i', 't', 100);
    return 0;
}

运行结果:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/yulong__li/article/details/81212550