可变参数列表的解析及应用

xiaoxiao2021-02-28  59

在C语言中,通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数(不固定)。

我们来看一个例子,并且通过剖析这个例子来了解可变参数列表

编译器:vs2013

//使用可变参数,实现函数,求函数参数的平均值 #include <stdio.h> #include <stdarg.h> #include <windows.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); } va_end(arg); return sum / n; } int main() { int avg = average(4, 1, 2, 3, 5); printf("%d\n", avg); system("pause"); return 0; }

首先,我们来分析部分代码

1.

int average(int n, ...) // ... 未知参数列表,n为未知参数列表个数 //将部分参数(1, 2, 3, 5)传给了未知参数列表

2.

代码
va_list arg; //char *arg //创建一个指向未知列表的指针
转到定义
typedef char * va_list; //右击鼠标,转到定义,可以观察到char *类型被重命名为 va_list

3.

代码
va_start(arg, n); //arg = (char *)(&(n)) + 4 //初始化arg为未知参数列表的第一个参数的地址
转到定义
#define va_start _crt_va_start //宏,_crt_va_start被重命名为va_start #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) //宏,( ap = (va_list)( &(v) ) + 4 ) #define _ADDRESSOF(v) ( &(v) ) //宏,取v的地址 #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) //宏,当n所占的字节为1,2,3,4时,宏返回4;当n所占的字节为5,6,7,8时,宏返回8 //n所占的字节为4,宏返回4

4.

代码
va_arg(arg, int); //*(int *)((arg += 4) - 4) //根据第二个参数在未知参数列表中获取一个参数,在获取一个参数之后,让arg向后移动指向下一个未知参数
转到定义
#define va_arg _crt_va_arg //宏,_crt_va_arg被重命名为va_arg #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //宏,( *(t *)((ap += 4) - 4) ) #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) //宏,当n所占的字节为1,2,3,4时,宏返回4;当n所占的字节为5,6,7,8时,宏返回8 //n所占的字节为4,宏返回4

5.

代码
va_end(arg); //arg = (char *)0 //把arg初始化为NULL
转到定义
#define va_end _crt_va_end //宏,_crt_va_end被重命名为 va_end #define _crt_va_end(ap) ( ap = (va_list)0 ) //宏,( ap = (va_list)0 )

之后,我们对average函数的部分代码进行替换

int average(int n, ...) { char *arg; //va_list arg; int i = 0; int sum = 0; arg = (char *)(&(n)) + 4; //va_start(arg, n); for (i = 0; i < n; i++) { sum += *(int *)((arg += 4) - 4); //sum += va_arg(arg, int); } arg = (char *)0; //va_end(arg); return sum / n; }

综上所述,我们可以了解到

声明一个va_list类型的变量,用于访问参数列表的未确定部分。这个变量是用va_start来初始化的,它的第一个参数是va_list的变量名,第二个参数是省略号前最后一个有名字的参数。初始化过程把arg变量设置为指向可变参数部分的第一个参数。为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数的类型。在上述的例子中,所有可变参数都是整型。va_arg返回这个参数的值,并使va_arg指向下一个可变参数。最后,我们访问完毕最后一个参数之后,需要调用va_end。

可变参数列表的应用

1.使用可变参数,实现函数,求函数参数的最大值

#include <stdio.h> #include <stdarg.h> #include <windows.h> int Max(int n, ...) { va_list arg;//创建一个指向未知列表的指针 int i; int max = 0; va_start(arg, n);//初始化arg为未知参数列表的第一个参数的地址 max = va_arg(arg, int); //获取第一个参数赋给max,之后,并且让arg向后移动指向下一个未知参数 for (i = 1; i < n; i++) { int tmp = va_arg(arg, int);//获取一个参数赋给max,之后,并且让arg向后移动指向下一个未知参数 if (max < tmp) max = tmp; } va_end(arg); //把arg初始化为NULL return max; } int main() { int ret = Max(4, 1, 2, 3, 4); printf("%d\n", ret); system("pause"); return 0; }

模拟实现简单的printf函数

模拟实现简单的printf函数,需要利用传入的第一个参数,对字符串经行遍历,若遇到字符为d,c,s等等(模拟%d,%c,%s),则利用va_age获取并输出后面所对应的未知参数列表中的参数,若没有遇到,则原样输出。

#include <stdio.h> #include <stdarg.h> #include <math.h> #include <windows.h> void print(const char *format, ...) { char *str = (char *)format; //保存 va_list arg; //创建指向未知参数列表的指针 va_start(arg, format);//初始化arg为可变参数列表的第一个参数的地址 while (*str != '\0') { int ch; char *s; switch (*str) { case 'd': //查到数字 print("%d\n", d); ch = va_arg(arg, int); while (ch > 10) { int d = 0; int i = 1; d = ch / 10; while (d > 10)//每次都得到ch第一位 { d = d / 10; i++; } d = d + 48; //转化为字符 putchar(d); i = (int)pow(10, i); ch = ch % i; //得到ch的后几位 } ch = ch + 48; putchar(ch); //输出ch的最后一位 str++; break; case 'c': //查到单字符 print("%c\n", 'c'); putchar(va_arg(arg, char)); str++; break; case 's': //查到字符串 print("%s\n", "s");, s = va_arg(arg, char*);//在未知参数列表中获取所对应字符串首字符的地址 while (*s != '\0') { putchar(*s); s++; } str++; break; default: //查到其它时原样输出 putchar(*str); str++; break; } } } int main() { print("s ccc d.\n", "hello", 'b', 'i', 't', 1234); system("pause"); return 0; }
转载请注明原文地址: https://www.6miu.com/read-2620506.html

最新回复(0)