天天看点

可变参数列表源码剖析及实例解析

一、源码剖析

在介绍可变参数列表之前,先补充两个宏

1._ADDRESSOF(v)

源码 :

作用:对变量v取地址

2._INTSIZEOF(n)

源码:

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
           

作用:将n的长度化为int长度的整数倍

原理:上式等于(sizeof(n)+3)&~(3)

其中~(3)=11111111 11111111 11111111 11111100

任何数&~(3)之后的二进制最后两位一定为00

则:如果4*i< num< 4 * (i+1) (i>=0,i∈Z) ,那么 num&~3=4 *(i+1);如果num等于4的整数倍,num&~3=num。

又因为(sizeof(n)+3)>=4;所以将保证把n的长度提升到4的倍数,即化为int长度的整数倍

读懂上面两个宏之后可以继续了解可变参数列表,可变参数列表包括:

1.va_list

源码:

typedef char *  va_list;
           

也就是说va_list是个char类型指针

2.va_arg

源码:

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

意思:将指针ap指向当前位置移动_INTSIZEOF(t)个字节后的位置,然后取现在的ap所在位置的前 _INTSIZEOF(t)个字节的位置(ap移动前的原位置),并将其强制转换成t类型,然后对其解引用,取出这个地址的内容。

作用:将ap移位_INTSIZEOF(t)个字节后,再取出ap移位前的位置对应的数据

如图:

可变参数列表源码剖析及实例解析

3.va_copy(暂不做解释,后期补充)

源码:

void _CRTIMP __cdecl _vacopy(_Out_ va_list *, _In_ va_list);
#define va_copy(apd, aps) _vacopy(&(apd), aps)
           

4.va_start

源码:

#define va_start _crt_va_start
#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
           

作用:对v取地址,并强制转换成va_list即char *类型,再将其移动 _INTSIZEOF(v)个字节后的地址赋给ap

5.va_end

源码:

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

作用:将0强制转换成va_list即char *类型,赋给ap,即将ap置空

二、实例解析

#include <stdio.h>
#include <stdarg.h>
#include <windows.h>

int average(int n,...)//n代表参数个数
{
    int i = ;
    int sum = ;
    va_list arg;
    va_start(arg, n);
    for (i = ; i < n;i++)
    {
        sum += va_arg(arg,int);
    }
    va_end(arg);
    return sum/n;
}

int main()
{
    int ret = average(,,,);
    printf("ret= %d\n",ret);
    system("pause");
    return ;
}
           

将上面代码转换成我们能看得懂的代码,如下:

#include <stdio.h>
#include <stdarg.h>
#include <windows.h>

int average(int n, ...)//n代表参数个数
{
    int i = ;
    int sum = ;

    //va_list arg;
    char *arg;

    //va_start(arg, n);
    //arg = (char *)_ADDRESSOF(n) + _INTSIZEOF(n);
    //arg = (char *)&n + ((sizeof(n)+3) & ~3);
    arg = (char *)&n +;

    for (i = ; i < n; i++)
    {
        //sum += va_arg(arg, int);
        //sum += *(int *)((arg += _INTSIZEOF(int)) - _INTSIZEOF(int));
        //sum += *(int *)(arg+=((sizeof(int)+3) & ~3) - ((sizeof(int)+3) & ~3));
        sum += *(int *)(arg+= - );

    }
    //va_end(arg);
    arg = (char *);

    return sum / n;
}

int main()
{
    int ret = average(,,,);
    printf("ret= %d\n",ret);
    system("pause");
    return ;
}
           

代码的功能如下图所示:

可变参数列表源码剖析及实例解析