C语言不定型参数函数定义
我们在C语言中定义一个函数,通常都是需要在函数原型中规定这个函数需要提供什么类型的参数以及需要提供多少个。也就是,你的参数必须明确。但是我们调用函数库中的printf和scanf函数会发现,它们似乎是可以根据我们自己任给的参数类型与参数个数来操作,那它们是怎么实现的呢?
在《C程序设计语言》中我找到了相关的描述,内容位于第七章输入与输出中的7.3节:可变参数表。
书中指出:我们想使用可变参数的函数时,首先应当在函数声明中用三个‘.’来代替将来使用可能会出现的参数,且省略号必须位于参数表的尾部。如:
int Test(char *format,...);
而非是
int Test(...,char *format);
当然,除了格式控制符,你还可以提供更多的参数表,以完成你需要的操作。
上面的操作我们只是完成了函数的声明,而函数体的完成,我们需要引入标准头文件<stdarg.h>。里面提供的一些宏可以帮助我们实现可变参数表的遍历。
首先,它提供了一种参数指针的类型:va_list。我们可以使用它来定义一个指针遍历参数表。如
va_list ap;
在我们指针正式工作前,还必须使用宏va_start对ap进行初始化,结果是使它指向第一个无名参数。(事实上更准确的描述应该是:以最后一个有名参数为起点,因为事实上是可以不传入无名参的。) 我们需要遍历参数表时,使用宏va_arg来实现。它会将指针在参数表往后挪一个,并给你返回一个值。它需要你自己提供一个参数类型,并把指的这个参数内容按你提供的类型解析。这个参数类型需要你自己对format进行解析得到。当我们完成遍历工作后,还需要使用宏va_end来完成系列清理工作。注意:va_start、va_arg和va_end在头文件中都是采用宏的方式,不信你#undef试试。
好啦,下面就是我们自写的一个myPrintf函数。
#include<stdio.h>
#include<stdarg.h>
//#undef va_end
void myPrint(char *fmt,...);
int main(void)
{
int i = ;
double d = 23.1;
char str[] = "HelloWorld";
myPrint("this is a intVal:%d\n",i);
myPrint("this is a doubleVal:%f\n",d);
myPrint("this is a intVal:%d\n,this is a char *info:%s\n",i,str);
myPrint("this is a test\n");
return ;
}
void myPrint(char *fmt,...)
{
//使用va_list类型声明变量 它会依次引用各个参数
va_list ap;
char *p,*sval;
int ival;
double dval;
int count = ;
va_start(ap,fmt); //宏va_start将ap指向第一个无名参数 在使用ap前该宏必须被调用一次 参数表至少包含一个有名参数
for(p = fmt;*p;p++)
{
if(*p != '%')
{
putchar(*p);
continue;
}
count++;
//如果是碰到了% 则判断后一个字符是什么
switch(*++p)
{
case 'd':
ival = va_arg(ap,int);
printf("%d %d",count,ival);
break;
case 'f':
dval = va_arg(ap,double);
printf("%d %f",count,dval);
break;
case 's':
printf("%d ",count);
//先把ap中的对应信息取出来放这里:s_val 然后对这个s_val遍历输出
for(sval = va_arg(ap,char *);*sval;++sval)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap); //结束需要清理工作
}
当然,我们只是实现了阉割版的printf,其中还缺乏很多功能呢。比如宽度控制、对齐控制以及更多种类型的输出等等都有待完善。
C语言不定型参数函数定义的更多相关文章
- [11 Go语言基础-可变参数函数]
[11 Go语言基础-可变参数函数] 可变参数函数 什么是可变参数函数 可变参数函数是一种参数个数可变的函数. 语法 如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最 ...
- C语言中可变参数函数实现原理
C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...
- 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试
. 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...
- C语言 命令行参数 函数指针 gdb调试
. 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...
- python 可变参数函数定义* args和**kwargs的用法
python函数可变参数 (Variable Argument) 的方法:使用*args和**kwargs语法.其中,*args是可变的positional arguments列表,**kwargs是 ...
- JavaScript 带参数函数定义
函数的参数parameters在函数中充当占位符(也叫形参)的作用,参数可以为一个或多个.调用一个函数时所传入的参数为实参,实参决定着形参真正的值. 这是带有两个参数的函数, param1 和 par ...
- C语言可变参数函数实现原理
一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本 ...
- c语言可变参数函数
c语言支持可变参数函数.这里的可变指,函数的参数个数可变. 其原理是,一般情况下,函数参数传递时,其压栈顺序是从右向左,栈在虚拟内存中的增长方向是从上往下.所以,对于一个函数调用 func(int a ...
- Python函数定义和使用
函数是一段可以重复多次调用的代码,通过输入的参数值,返回需要的结果.通过使用函数,可以提高代码的重复利用率.本文主要介绍Python函数的定义.调用和函数参数设置方法. 函数的定义 Python函数定 ...
随机推荐
- Docker相关连接
docker-compose文档:https://docs.docker.com/compose/compose-file/ dockerfile文档:https://docs.docker.com/ ...
- mysql资源地址
http://ftp.ntu.edu.tw/MySQL/Downloads/MySQL-5.7/
- 无线wifi
802.11n 802.11n是基于OFDM(正交频分复用Orthogonal Frequency Division Multiplexing)技术下的2.4G频段的协议,也可以工作在5G频段下. - ...
- mysql DML语句学习1
DML 操作是指对数据库中表记录的操作,主要包括表记录插入(insert).更新(update).删除(delete)和查询(select) 1. 插入记录 表创建好后,就可以往里插入记录,基本语句如 ...
- python接口测试:自动保存cookies
接口测试中遇到上一个请求返回响应包含cookie(如下图登录请求的响应结果).需将cookies保存下来,后续请求自动带入,否则会提示未登录. python requests的cookie类型是< ...
- visual stdio 安装OpenGL库文件
1.将下载的压缩包解开.将得到5个文件 1. 将glut解压出来,将当中的glut.h拷贝到C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC ...
- myeclipse解决JSP文件里script背景颜色的调整
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/UP19910522/article/details/27971401 1导入MyEclipse的主题 ...
- HTML data-* 自定义属性
HTML data-* 自定义属性 data-*是HTML5新添加的全局属性,通过它我们可以自定义属性,就像id,class等属性一样.我们可以使用JavaScript来操作这些属性. <div ...
- Elasticsearch + Elasticsearch-head搭建
Elasticsearch搭建: [root@hdoop3 elk]# tar -xvf elasticsearch-6.2.4.tar [root@hdoop3 elk]# cd elasticse ...
- 启动 NFS 守护进程:rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused)
启动 NFS 守护进程:rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused) rpc.nfsd: unable t ...