• fprintf 的封装(vsprintf,va_start(), va_arg(), va_end()可变参数列表)


    首先我们来看一个封装的实例:

    #include <stdio.h>
    #include 
    <stdlib.h>
    #include 
    <stdarg.h>

    #define FPRINT_OUT_FILE_PATH "/dev/pts/2"
    FILE 
    * console_printf_f;

    int My_fprintf(const   char   *form ,...)
    {
        va_list   arg;   
        
    int   done;   
        
    char pbString[256];
      
        va_start(arg,form);  
        vsprintf(pbString,form,arg);           //若此行和下行改为:done = fprintf(console_printf_f,form,arg);则可变
        done 
    = fprintf(console_printf_f,pbString);   //参数的值不能正常显示,原因是参数传递错误,fprintf不能正确处理va_list类型的参数
        
    //done = printf(pbString);
        va_end(arg);   
        
    return   done;   
    }

    int main()
    {
        
    int i;
        console_printf_f
    =fopen(FPRINT_OUT_FILE_PATH, "w" );
        My_fprintf(
    "hello my_fprintf %d %d %d  %s \n",0,1,2,"ni hao ");
        fprintf(console_printf_f,
    "hello fprintf %d %s \n",32 ,"ni hao ");
        fclose(console_printf_f);
        
    return 0;
    }

     1.fprintf()原形:
    #include <stdio.h>
    int fprintf( FILE *stream, const char *format, ... );

    2.vsprintf(), vnsprintf()的原形及使用:

    #include <stdarg.h>  
    int vsprintf(char *str, const char *format, va_list ap);  
    int vsnprintf(char *str, size_t size, const char *format, va_list ap); 

    说明:
    vsprintf() 和 vsnprintf() 基本一样,但后者比前者多了一个字节数的限定。

    vsprintf() 参数说明:
    str : 一般是个字符缓冲区的首地址;
    format : 是带有格式说明的字符串,如同 printf() 中第一个参数;
    ap : va_list 类型,关于 va_list 类型说明见:http://www.groad.net/bbs/read.php?tid-947.html

    应用举例:

    代码
    #include <stdio.h>  
    #include 
    <stdarg.h>  

    void log_msg (const char *text, ...)  
    {  
        
    char buf [256];  
        va_list args;  
        va_start (args, text);  
        vsprintf (buf , text, args);  
        printf (
    "%s", buf);  
        va_end (args);    
    }  

    int main()  
    {  
        
    int year = 2008;  
        
    char *ptr = "china";  
        log_msg (
    "hello %d Beijing and welcome to %s\n", year, ptr);  
        
    return (0);  

    运行与输出:

    [beyes@localhost vsprintf]$ ./vsprintf.exe
    hello 2008 Beijing and welcome to china

    3.可变参数列表:

    1)相关函数:

    #include <stdarg.h>
    void va_start(va_list ap, last);
    type va_arg(va_list ap, type);
    void va_end(va_list ap);
    void va_copy(va_list dest, va_list src);

    2)也许有时会需要不同类型、不同数目的参数来调用函数。<stdarg.h> 声明了一个 va_list 类型,还定义了三个宏来逐步跟踪一个参数列表。对于调用函数来说,它并不会知道这个列表里的参数个数及其类型。
    调用函数必须声明一个 va_list 类型的变量。va_list 类型在宏 va_start(),va_arg(),和 va_end() 中用到。
    va_start()
    va_start() 初始化

    ap 这个参数,这是为了后面的 va_arg() 和 va_end() 会用到此参数。这个宏是必须被第一个调用的。
    last  参数是可变参数列表之前的最近的一个参数的名字,也就是说,这是调用函数 所知道的最近的一个类型。由于这个参数( last )的地址可能会在 va_start() 中用到,所以它不能是一个寄存器变量,或者是一个函数,抑或是一个数组类型。
    va_arg()
    va_arg() 宏展开成一个表达式,这个表达式含有调用函数中下一个参数的值和类型。
    其中,ap 是经由 va_start() 初始化后的 va_list ap 。每次调用 va_arg() 都会改变 ap,这样一来下一次的调用就会返回下一个参数。参数 type 是一个已指定过的类型名(如 int , char ),这样一来,通过增加 * 到 type 中( 如 int *, char *)就可以简单地获得一个已指定类型对象的指针类型。
    在调用 va_start() 后第一次使用 va_arg() 会返回 last 后的参数。连续调用则返回剩下的参数。
    如果已没有下一个参数,或者 type 与下一个参数的实际类型并不兼容,则发生的错误不可预料。
    如果把 ap 传递给一个调用 va_arg(ap, type) 的函数,那么当这个函数返回后,ap 的值为未定义。

    va_end()
    在同一个函数里,每一次调用 va_start() 都必须和 va_end() 相配对。在调用 va_end( ap ) 后,ap 变为未定义。对于多重遍历参数列表,则每次配对使用 va_start() 和 va_end() 是可以的。va_end() 可能是一个宏,或者是一个函数。
    举例:

    代码
    #include <stdio.h>
    #include 
    <stdarg.h>

    void foo(char *fmt, ...)
    {
            va_list ap;
            
    int     d;
            
    char c, *s;

            va_start(ap, fmt);
            
    while (*fmt)
                    
    switch (*fmt++) {
                    
    case 's':
                            s 
    = va_arg(ap, char *);
                            printf(
    "string %s\n", s);
                            
    break;
                    
    case 'd':
                            d 
    = va_arg(ap, int);
                            printf(
    "int %d\n", d);
                            
    break;
                    
    case 'c':
                            c 
    = (char) va_arg(ap, int);
                            printf(
    "char %c\n", c);
                            
    break;
                    }
            va_end(ap);
    }
    int main()
    {
            
    int val1 = 20;
            
    char buf[20= {"hello world"};
            foo(
    "kkkkkk%dxxxxxsxxxxx",val1,buf);
            
    return 0;
    }

    运行及输出
    beyes@linux-beyes:~/C> ./varg.exe
    int 20
    string hello world

    说明
    在 foo() 函数中,使用一个 while 循环遍历 fmt 所指向字符串中的每一个字符,这是为了要找出与 switch 所定制相匹配的字符。假如找到匹配项,那么就调用 va_arg()。在 va_arg() 中,ap 表示了 fmt 指向字符串后面的参数列表。每一次调用 va_arg() 就会读出参数列表中的一个项,假如得到的这个参数列表中的项的实际类型和 va_arg() 中的第二个参数指明的类型一样,那么这个宏调用成功。如果是乱指定一个未知的类型,比如 kk ,那么这在编译时也会给出错误的提示:

    varg.c: In function ‘foo’:
    varg.c:14: error: expected specifier-qualifier-list before ‘kk’

    如果指定一个不兼容的类型,比如把上面的 char * 改写成 int,编译时给出提示:
    varg.c: In function ‘foo’:
    varg.c:14: warning: assignment makes pointer from integer without a cast

    这是个警告信息,意思是没有通过类型强制转换就把一个整数类型变成指针类型。但程序运行后仍然打印出想要的信息。

  • 相关阅读:
    服务器开启超线程
    redhat用kickstart.cfg自动安装后,挂载ISO镜像并从中拷贝文件
    关于网卡特性TSO、UFO、GSO、LRO、GRO
    Linux 网卡特性配置ethtool详解
    SecureCRT设置标签显示标题
    关闭SecureCRT的声音
    SecureCRT设置Vim显示颜色
    centos关闭swap分区
    制作CentOS7.6 自动安装ISO镜像光盘
    带阵列卡的机器打开磁盘cache
  • 原文地址:https://www.cnblogs.com/leaven/p/1767374.html
Copyright © 2020-2023  润新知