我们在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语言不定型参数函数定义的更多相关文章

  1. [11 Go语言基础-可变参数函数]

    [11 Go语言基础-可变参数函数] 可变参数函数 什么是可变参数函数 可变参数函数是一种参数个数可变的函数. 语法 如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最 ...

  2. C语言中可变参数函数实现原理

    C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...

  3. 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...

  4. C语言 命令行参数 函数指针 gdb调试

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...

  5. python 可变参数函数定义* args和**kwargs的用法

    python函数可变参数 (Variable Argument) 的方法:使用*args和**kwargs语法.其中,*args是可变的positional arguments列表,**kwargs是 ...

  6. JavaScript 带参数函数定义

    函数的参数parameters在函数中充当占位符(也叫形参)的作用,参数可以为一个或多个.调用一个函数时所传入的参数为实参,实参决定着形参真正的值. 这是带有两个参数的函数, param1 和 par ...

  7. C语言可变参数函数实现原理

    一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本 ...

  8. c语言可变参数函数

    c语言支持可变参数函数.这里的可变指,函数的参数个数可变. 其原理是,一般情况下,函数参数传递时,其压栈顺序是从右向左,栈在虚拟内存中的增长方向是从上往下.所以,对于一个函数调用 func(int a ...

  9. Python函数定义和使用

    函数是一段可以重复多次调用的代码,通过输入的参数值,返回需要的结果.通过使用函数,可以提高代码的重复利用率.本文主要介绍Python函数的定义.调用和函数参数设置方法. 函数的定义 Python函数定 ...

随机推荐

  1. [转发]CPU个数、CPU核心数、CPU线程数

    我们在选购电脑的时候,CPU是一个需要考虑到核心因素,因为它决定了电脑的性能等级.CPU从早期的单核,发展到现在的双核,多核.CPU除了核心数之外,还有线程数之说,下面文本就来解释一下CPU的核心数与 ...

  2. Python实例---抽屉后台框架分析

    1.1. 抽屉框架分析 --登陆注册分析 1.2. 前台获取form表单补充知识: <!DOCTYPE html> <html lang="en"> < ...

  3. list 去重并保持原来排序

    public <T> List<T> removeDuplicateKeepOrder(List<T> list){ /* Set set = new HashSe ...

  4. jq实现鼠标经过出现上拉菜单

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  5. PostgreSQL 连接的问题

    一.在postgresql的安装文件夹\8.3\data\pg_hba.conf里面(或者在开始菜单程序下面的postgresql的配置文档)找到“# IPv4 local connections:” ...

  6. P2585 [ZJOI2006]三色二叉树

    题目描述 输入输出格式 输入格式: 输入文件名:TRO.IN 输入文件仅有一行,不超过500000个字符,表示一个二叉树序列. 输出格式: 输出文件名:TRO.OUT 输出文件也只有一行,包含两个数, ...

  7. POJ1039 Pipe

    嘟嘟嘟 大致题意:按顺序给出\(n\)个拐点表示一个管道,注意这些点是管道的上端点,下端点是对应的\((x_i, y_i - 1)\).从管道口射进一束光,问能达到最远的位置的横坐标.若穿过管道,输出 ...

  8. xss练习平台及writeup

    今天玩了一天的xss. 分享几个xss game https://xss.haozi.me/#/0x00 http://47.94.13.75/test/  writeup:http://www.cn ...

  9. 2、Spring Cloud - 入门概述

    前言: Spring Cloud是什么: 官网说明 SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心, 全链路监控,服务网关,负载均衡,熔断器等组 ...

  10. 8、RabbitMQ-消息的确认机制(生产者)

    RabbitMQ 之消息确认机制(事务+Confirm) https://blog.csdn.net/u013256816/article/details/55515234 概述: 在 Rabbitm ...