zoukankan      html  css  js  c++  java
  • 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 = 10;
        double d = 23.1;
        char str[] = "HelloWorld";
        myPrint("this is a intVal:%d
    ",i);
        myPrint("this is a doubleVal:%f
    ",d);
        myPrint("this is a intVal:%d
    ,this is a char *info:%s
    ",i,str);
        myPrint("this is a test
    ");
        return 0;
    }
    void myPrint(char *fmt,...)
    {
        //使用va_list类型声明变量  它会依次引用各个参数
        va_list ap;
        char *p,*sval;
        int ival;
        double dval;
        int count = 0;
        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,其中还缺乏很多功能呢。比如宽度控制、对齐控制以及更多种类型的输出等等都有待完善。

  • 相关阅读:
    thinkphp3.2.3版本在windows本地apache环境运行正常,上传到centos服务器apache环境中出现:thinkphp 上传根目录不存在!请尝试手动创建:uploads/
    [POI2013]LUK-Triumphal arch
    【背包问题】
    2016 acm香港网络赛 A题. A+B Problem (FFT)
    tomcat部署项目的三种方式
    仿照ArrayList自己生成的MyList对象
    使用回调函数实现回文判断
    关于angularjs的model的一些问题
    关于使用Tomcat服务器出现413错误的解决办法(Request Entity Too Large)
    关于angularjs+typeahead的整合
  • 原文地址:https://www.cnblogs.com/YaLi/p/9801316.html
Copyright © 2011-2022 走看看