• C语言回顾(可变参数篇)


    可变参数

    C 语言允许定义参数数量可变的函数,这称为可变参数函数。这种函数需要固定数量的强制参数,后面是数量可变的可选参数。

    常见的可变参数函数
    int printf(const char* format,…)
    int scanf(const char *format,…)
    固定参数 format ,可选参数用“...”最为参数占用符
    
    • 1
    • 2
    • 3
    • 4

    任何一个可变参数的函数都可以分为两部分:固定参数和可选参数。至少要有一个固定参数,其声明与普通函数参数声明相同;可选参数由于数目不定(0个或以上),声明时用"…"表示。固定参数和可选参数共同构成可变参数函数的参数列表。

    可变参数获取对象
    // 函数add() 计算可选参数之和
    // 参数:第一个强制参数指定了可选参数的数量,可选参数为double类型
    // 返回值:和值,double类型
    double add( int n, ... )
    {
      int i = 0;
      double sum = 0.0;
      va_list argptr;
      va_start( argptr, n );   // 初始化argptr
      for ( i = 0; i < n; ++i )  // 对每个可选参数,读取类型为double的参数,
        sum += va_arg( argptr, double ); // 然后累加到sum中
      va_end( argptr );
      return sum;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在上面的例子中,用到了va_list ,va_start,va_arg,va_end 包含头文件

    va_list :其实就是通过typedef 定义的一个指针类型,它用来定义一个指针。

    va_start:其实就是为了获取可选参数的地址,然后保存在第一个参数中。它有两个参数,一个参数是va_list 类型的,用来存放第一个可选参数的地址。另一个参数是与可选参数相邻的那个强制参数。其实在程序的栈中,函数的所有参数都是在内存中连续分布的,所以可以通过强制参数的地址来获取可选参数的地址,然后保存在第一个参数中

    va_arg:这个宏取得 type 类型的可变参数值。该函数有两个参数,第一个参数是当前可变参数的地址,第二个参数是当前可变参数的类型。通过地址和类型来确定可变参数的值,然后返回。这个函数调用过后,第一个参数的值会变化,它会指向参数列表中下一个参数。

    va_end:必须和va_start 配合使用,使用 va_start 宏的函数中退出之前,必须调用一次 va_end 宏。相当于释放了内存。

    __VA_ARGS__

    个可变参数的宏,它使得可以在宏定义中使用可变参数列表,可变参数的个数最少为1,否则编译会出错。

    //用法如下,在宏定义中替代 ... :
    #define my_print2(fmt,...)  printf(fmt,__VA_ARGS__)
    
    
    • 1
    • 2
    • 3

    此外还有一个 ##__VA_ARGS__ ,它可以允许可变参数的个数为0个。

    #define my_print2(fmt,...)  printf(fmt,__VA_ARGS__)
    my_print2("i=%d,j=%d\n",i,j) //正确打印
    my_print2("iiiiiii\n") //编译失败 只有一个参数,可变参数的个数为0
    //改为:
    #define my_print2(fmt,...)  printf(fmt,##__VA_ARGS__)
    my_print2("iiiiiii\n")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    vsprintf与vsnprintf

    vsprintf与vsnprintf的用法差不多,区别在于vsnprintf限制了最大的转换长度,可以防止越界。一般选用vsnprintf。

    int vsprintf (char * s, const char * format, va_list arg );
    将可变参数列表的格式化数据写入字符数组s中
    s:这是指向一个字符数组的指针,该数组存储了字符串
    format:要格式化输出的字符串
    arg:可变参数列表
    返回值:如果成功,则返回写入的字符总数,否则返回一个负数
    用法:
    int vspfunc(char *format, ...)
    {
       va_list aptr;
       int ret;
    
       va_start(aptr, format);
       ret = vsprintf(buffer, format, aptr);
       va_end(aptr);  
       return(ret);
    }
    int main()
    {
       int i = 5;
       float f = 27.0;
       char str[50] = "runoob.com";
       vspfunc("%d %f %s", i, f, str);
       printf("%s\n", buffer);  
       return(0);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    int vsnprintf(char *s, size_t n, const char *format, va_list arg)
    s:这是指向一个字符数组的指针,该数组用来存储字符串
    n:在缓冲区中使用的最大字节数。生成的字符串的长度至多为n-1,为额外的终止空字符留下空间
    format:要格式化输出的字符串
    arg:可变参数列表
    返回值:如果成功,则返回写入的字符总数,不包括终止空字符,否则返回一个负数
    用法:
    void test(const char * format, ...)
    {
         char buf[4069];
    
         va_list list;
    
     
         va_start(list, format);
     
         vsnprintf(buf, 4069, format, list);
     
         va_end(list);
     
         printf("%s\n", buf);
     } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 相关阅读:
    C++ 一维数组初始化
    ES6学习24~47
    安卓开发Android studio学习笔记15:关于如何使用Okhttp框架的网络请求(调用API接口)
    Spring Cloud Gateway转发Spring WebSocket
    [Tools: cntk] Linux下cntk-gpu安装
    小程序毕设作品之微信美食菜谱小程序毕业设计成品(3)后台功能
    软件测试的行业现状,我们的未来在哪里?
    操作系统之《PV操作》【知识点+详细解题过程】
    Caputo 分数阶一维问题基于 L1 逼近的快速差分方法(附Matlab代码)
    JS中Class类的静态属性和静态方法
  • 原文地址:https://blog.csdn.net/g360250466/article/details/125991865