博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
可变参数
阅读量:7040 次
发布时间:2019-06-28

本文共 3555 字,大约阅读时间需要 11 分钟。

hot3.png

C语言支持参数可变的函数,例如经常使用的函数printf就是一个典型的参数可变的函数,其原型如下:

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

printf()函数是一个可变参数的函数,其参数数目在函数调用的时候确定。

printf()函数的函数原型中第一个参数format是固定的,后面的参数个数和类型都是可变的。编译器使用3个"..."作为参数的占位符,告知编译器一个个参数的后面还可能有若干的参数。

实现一个简单的可变参数的函数

#include 
#include
/** * 列出所有的参数 */int print_args(int begin, ...){ va_list ap; char *p; int n; va_start(ap, begin); //从可变参数的第一个参数开始遍历 p = va_arg(ap, char *); //得到第一个参数 n = 0; while(p != NULL) { //可变参数以NULL结尾,在遇到NULL结束符之前输出所有参数 n++; //累计采纳数的个数 printf("arg %d : %s\n", n, p); //输出每个参数 p = va_arg(ap, char *); //得到下一个参数 } va_end(ap); //参数处理结束,做一些清理工作 return n; //返回参数个数}int main(void){ int n; n = print_args(-1, "hello", "world", NULL); //第一次调用,使用四个参数 printf("first, without NULL : %d\n", n); n = print_args(-1, "beijing", "shanghai", "shenzhen", NULL); //第二次调用,使用五个参数 printf("first, without NULL : %d\n", n);}

运行结果如下:

对上面的函数做一下解释:

  • 标准头文件stdarg.h:这个头文件中定义了一系列的宏来处理这个可变长度的参数列表。如果需要实现一个可变参数的函数,该头文件是必不可少的。
  • 类型va_list:这个类型定义在stdarg.h头文件中。va_list定义为这样一个数据类型,循环使用且每次指向一个可变的参数。因此该类型的变量代表整个参数的列表。在上例中定义变量ap为va_list类型的变量,所以ap代表整个参数列表。
  • 宏 va_start:va_start宏初始化一个va_list类型的变量(上例中使用变量ap),使其指向第一个可变的参数。经过初始化后,变量ap就可以代表整个参数列表了,因此该宏必须在使用参数列表之前使用。每个可变参数的函数的第一个参数必须固定,否则无法进行初始化,并且将变量ap指向该参数列表。
  • 宏 va_arg:va_arg宏返回一个可变长度参数的值并使ap指向下一个可变的参数,该宏使用一个类型名来确定要返回的类型和指针ap需要移动的字节单位。
  • 宏 va_end:做一些必要的清理工作,需要在程序结束之前使用。

通过分析以上实例的代码,可以总结一个可变参数的函数实现流程如下:

  1. 使用va_start宏初始化va_list类型的变量,使其指向可变参数列表的头。
  2. 使用va_arg宏得到每一个参数并对其进行处理,当遇到一个结束标志时停止处理。
  3. 使用va_end宏做清理工作

流程图如下:

可变参数实例

实现my_printf()函数,即实现与printf()函数类似的功能。

#include 
#include
#include
#define MAX 64/** * 将一个short型变量转换为字符串形式成功返回转换后的字符串首地址,失败返回NULL * i:需要转换的短整型,最大值为65536,不处理附负数的形式 * p:转换后的字符串的首地址。p代表存储该串的数组空间的起始位置 */char * itoa(int i, char *p){ char *q; if(p == NULL) { return NULL; } //将整型转换为字符串,整型的最大不会超过65536且不处理负数 p[0] = (i/10000) + '0'; i = i%10000; p[1] = (i/1000) + '0'; i = i%1000; p[2] = (i/100) + '0'; i = i%100; p[3] = (i/10) + '0'; i = i%10; p[4] = i + '0'; p[5] = '\0'; //下面的操作用于去除多个0,将第一个有效数字作为第一个数字 q = p; while(*q != '\0' && *q == '0') { //找到第一个非0数字 q++; } if(*q != '\0'){ strcpy(p, q); } return p;}/** * 自定义的printf函数,这是一个可变参数的函数。第一个参数固定为字符指针型 * 返回值是实际输出的字符串 */int my_printf(const char *format, ...){ va_list ap; char c, ch; int i; char *p; char buf[MAX]; //保存字符串的缓冲区 int n = 0; //累计输出字符数 va_start(ap, format); //到达可变参数的起始位置 c = *format; while(c != '\0'){ if(c == '%'){ format++; //使用%进行转义,跳过%字符,处理后面的转义字符 c = *format; switch(c){ case 'c': //处理字符 ch = va_arg(ap, int); //取第一个字符参数 putchar(ch); //输出该字符 n++; break; case 'd': //处理整数(short) i = va_arg(ap, int); //取该整数参数 itoa(i, buf); //将整数转换为字符串 n += strlen(buf); //累计输出字符数 fputs(buf, stdout); //输出该整数的字符串形式 break; case 's': //处理字符串 p = va_arg(ap, char *); //取下一个指针参数,保存字符串的首地址 n += strlen(p); fputs(p, stdout); //输出该字符串 } } else { putchar(c); //普通字符,则输出该字符 n++; } format++; //处理下一个字符 c = *format; } va_end(ap); //做一些清理工作 return n; //返回实际输出的字符数}int main(void){ //调用my_printf()函数输出字符、整型和字符串 my_printf("the char is : %c\nthe number is : %d\nthe string is : %s\n", 'a', 100, "hello world!"); return 0;}

运行结果为:

my_printf()函数是一个可变参数的函数,首先使用va_start宏进行初始化,之后处理format字符串所表示的每一个字符。如果遇到'%'字符表示这个字符后面是一个转义字符,需要使用va_arg宏得到一个可变参数,并且对其进行适当的处理。my_printf()函数是一个简单的版本,只处理'c','d','s'的情况。

其中,itoa()函数的执行流程为:

 

转载于:https://my.oschina.net/daowuming/blog/761912

你可能感兴趣的文章
P2P技术如何将实时视频直播带宽降低75%?
查看>>
统计文件夹下文件总数
查看>>
JEPLUS之循环报表—JEPLUS软件快速开发平台
查看>>
从一个线上问题分析binlog与内部XA事务提交过程
查看>>
网页版式设计与平面构图
查看>>
view桌面模板控制usb权限
查看>>
吾日三省吾身
查看>>
【office培训】【王佩丰】Excel2010视频教程第2讲:单元格格式设置
查看>>
android inflate
查看>>
libxml2的编译与安装
查看>>
Spring Boot中使用redis的发布/订阅模式
查看>>
手动清除memcached缓存方法
查看>>
c++数据类型
查看>>
我的友情链接
查看>>
Hadoop2.7实战v1.0之Linux参数调优
查看>>
eclipse mqtt paho client 处理shared topic, 共享主题问题
查看>>
eclipse 初始化
查看>>
Python程序结构与控制流
查看>>
详述Google针对Android平板App发布的十大开发准则
查看>>
CentOS 7安装python3笔记
查看>>