可变参数源码的剖析和自己的理解

来源:互联网 发布:java sort 编辑:程序博客网 时间:2024/06/05 02:29

在日常写代码时,经常会用到“printf”函数,而printf函数可以传递多个参数,可以1个,2个,多个。而我们写函数时,会注意到自己定义的形参数量是已经定义好的,对比printf函就会发现该函数参数数量却是传递任意个数都可以输出的。如:

    printf("%d\n", 2017);    printf("%s\n","I'm fine.");    printf("%d %s\n", 2017,"I'm fine.");

查阅一下printf函数原型:

int printf ( const char * format, ... );

这时,我们发现参数列表里里有“…”。因此,我们就要讨论一下可变参数,注意:可变参数中参数值至少为1。 而要使用可变参数,就要先了解这些宏:

    va_list arg;     void va_start( va_list arg_ptr, prev_param );    type va_arg( va_list arg_ptr, type );   void va_end( va_list arg_ptr );

而这些宏应该怎么用呢,我就用一个简单的求最大值算法,在代码中注释来给大家解释下。本来顺手打了讲解的,想想自己还是一只小菜鸡呢,赶紧改成解释了呢……

#define _CRT_SECURE_NO_WARNINGS//此处用于在VS2015中防止编译警告#include<stdarg.h>//调用上述宏所需要声明的头文件#include<stdio.h>//输入、输出函数所需要声明的头文件int CalculateMax(int n, ...){    int max = 0;    int i = 0;    int middle = 0;//存放表达式:va_arg(arg,int)的值    va_list arg;// 定义一个名称为arg的指针    va_start(arg, n);//用于指明arg是指针名,n代表第一个参数,用于表示起始端    max = va_arg(arg, int);    while (i < n - 1)    {        if (max < (middle = va_arg(arg, int)))//如果middle = va_arg(arg, int)的值小于max,进入判断        {            max = middle;        }        i++;    }    va_end(arg);    return max;}int main(){    int result = CalculateMax(5, -5, -9, -8, -3, -6);    printf("The biggest number is %d\n", result);    return 0;}
  • 详细解释开始咧。。。。。。

va_list arg; 转到定义:typedef char* va_list;重命名char* 为va_list。此处va_list就相当于 char*了,可以定义指针。


va_arg(arg, int);转到定义:#define va_arg __crt_va_arg,__crt_va_arg。
转到定义: #define __crt_va_arg(ap, t) ((t)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) 。
ap表示可变参数指针,而t表示数据类型。右边语句_INTSIZEOF(t)是什么意思呢?
继续查看 #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))。这又是什么意思呢?
别急,一步一步来,从右向左看。
(sizeof(int) - 1),There is no doubt that result is three。不装13了,该表达式结果是3,二进制位为0000 0000 0000 0000 0000 0000 0000 0011。加取反操作~结果是1111 1111 1111 1111 1111 1111 1111 1100 。 当参数t为char时,’&’前面的表达式结果取最小值4,二进制结果是0000 0000 0000 0000 0000 0000 0000 0100。因此,两边表达式运算结果是4。
那为什么要搞这么复杂的表达式呢?用sizeof关键字就好了咩。一般数据类型确实看上去求算有点复杂了,但是如果是结构体呢?结构体内有多种类型变量,如果继续使用简单的sizeof关键字求解的话,未免有些不太适合了。使用 ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)),传入char,float,double等小于4字节的类型,都会返回4。相应的传入如果类型大小是,5,6,7的话,则返回8。即返回当前一组数中靠近4的倍数的值。当然5,6,7这几个值取的有点不太好,只是为了表达这个意思,勿深究。


说了那么多再来看看 (* (t*)( (ap += _INTSIZEOF(t) ) - _INTSIZEOF(t) ) )。注意括号套着哪个括号。该表达式先让指针ap加上4字节的大小,再把减去4字节大小处所对应的值返回。用t强制类型转换,再解引用。注意此处t是传入参数的数据类型。


最后谈谈va_end(arg);转到定义:#define va_end __crt_va_end,继续转到定义:#define __crt_va_end(ap) ((void)(ap = (va_list)0)),由此看出,该语句把整型0强制转换为字符0,然后传给指针ap,由此可知将ap清空。

原创粉丝点击