可以参考:C 标准库 - <stdarg.h>

1. 在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表

void foo(...);
void foo(parm_list,...);

这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。

2. 函数参数的传递原理

函数参数是以数据结构:栈的形式存取,从右至左入栈。

首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:

void func(int x, float y, char z);

那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。

下面是 <stdarg.h> 里面重要的几个宏定义如下:

typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );

va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。

  • <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
  • <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“…”之前的那个参数;
  • <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
  • <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。

例如 int max(int n, …); 其函数内部应该如此实现:

#include <iostream.h>
void fun(int a, ...)
{int *temp = &a;temp++;for (int i = 0; i < a; ++i){cout << *temp << endl;temp++;}
}
int main()
{int a = 1;int b = 2;int c = 3;int d = 4;fun(4, a, b, c, d);system("pause");return 0;
}

Output:
1
2
3
4

3. 获取省略号指定的参数

在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:

void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
{va_list args;va_start(args, pszFormat); //一定要“...”之前的那个参数_vsnprintf(pszDest, DestLen, pszFormat, args);va_end(args);
}

头文件:

#include <stdarg.h>

函数声明:

int _vsnprintf(char* str, size_t size, const char* format, va_list ap);

参数说明:

  • char *str [out],把生成的格式化的字符串存放在这里.
  • size_t size [in], str可接受的最大字符数 (非字节数,UNICODE一个字符两个字节),防止产生数组越界.
  • const char *format [in], 指定输出格式的字符串,它决定了你需要提供的可变参数的类型、个数和顺序。
  • va_list ap [in], va_list变量. va:variable-argument:可变参数

函数功能:将可变参数格式化输出到一个字符数组。

4.演示如何使用参数个数可变的函数,采用ANSI标准形式

#include 〈stdio.h〉
#include 〈string.h〉
#include 〈stdarg.h〉/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
int demo( char, ... );
void main( void )
{demo("DEMO", "This", "is", "a", "demo!", "");
}/*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
int demo( char msg, ... )
{/*定义保存函数参数的结构*/va_list argp;int argno = 0;char para;/*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/va_start( argp, msg );while (1){para = va_arg( argp, char);if ( strcmp( para, "") == 0 )break;printf("Parameter #%d is: %s\n", argno, para);argno++;
}
va_end( argp );
/*将argp置为NULL*/
return 0;
}

例子

#define PT_BUFFER (512)
void Print(const s8 *pFmt,...)
{if (NULL == pFmt ){return;}va_list pArgList = NULL;s8 achPtBuf[PT_BUFFER] = {0};//获取可变参数列表的首地址va_start(pArgList , pFmt);//将格式化字符串与可变参数列表按格式输出到 achPtBuf中// 下面vsnprintf_s的第二个参数是 MaxCount 而不是 BufSize // (MaxCount: Maximum number of characters to write (not including the terminating null))// 为了避免 字符串pFmt 超长时 vsnprintf_s 崩溃,// 现将第二个参数由 sizeof(achPtBuf)/sizeof(s8) 改成 sizeof(achPtBuf)/sizeof(s8) - 10// ----- lijian 2020/3/19vsnprintf_s (achPtBuf, sizeof(achPtBuf)/sizeof(s8) - 10, pFmt, pArgList);//清空va_list可变参数列表va_end(pArgList);//======= 打印 =============printf("%s", achPtBuf);
}

va_start 用法相关推荐

  1. va_start用法

    此文转自别人博客,感谢原作者 1:当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表 void foo(...); void foo(parm_list,...); 2:函数参数的传递原 ...

  2. va_list、va_start和va_end的用法

    在项目的代码中看到函数不定参数的使用,现在总结一下va_list.va_start和va_end三个宏的用法. c语言提供了函数的不定长参数使用,比如 void func(int a, -).三个省略 ...

  3. Va_list Va_start va_arg Va_end 的用法

    首先我们先看看它的头文件是怎么描述的 stdarg.h #pragma once#ifndef _INC_STDARG #define _INC_STDARG#if !defined(_WIN32) ...

  4. va_list、va_start和va_end使用

    我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1.硬件平台的不同 2.编译器的不同,所以定义的宏也有所不同. 在ANSI C中,这些宏的定义位于stdar ...

  5. 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法

     参考: http://www.360doc.com/content/12/0309/10/4025635_192940551.shtml http://www.cnblogs.com/Annie ...

  6. _vsnprintf 用法

    _vsnprintf,C语言库函数之一,属于可变参数.用于向字符串中打印数据.数据格式用户自定义. 头文件: #include <stdarg.h> 函数声明: int _vsnprint ...

  7. 深度探索va_start、va_arg、va_end

    采用C语言编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数.但在某些情况下希望函数的参数个数可以根据需要确定.典型的例子有大家熟悉的函数printf().s ...

  8. 可变参数列表(va_list,va_arg,va_copy,va_start,va_end)

    本文转自:http://blog.csdn.net/costa100/article/details/5787068 va_list arg_ptr:定义一个指向个数可变的参数列表指针: va_sta ...

  9. C/C++vsnprintf用法(要配合va_list使用)

    _vsnprintf是C语言库函数之一,属于可变参数.用于向字符串中打印数据.数据格式用户自定义.头文件是#include <stdarg.h>. 参数说明: char *str [out ...

最新文章

  1. Microbiome:首个地球微生物“社会关系”网络在浙大绘制!
  2. 入门学Java,要学哪些开发工具呢?
  3. wxWidgets:wxBitmap类用法
  4. Idea tomcat get 乱码
  5. Python中的快捷键和注释方式
  6. 如何修改linux的 系统语言
  7. 如何用sql语言只获得数据库当前日期,且格式为yyyy-mm-dd?
  8. 黄色量能通达信指标公式!没有未来函数,不加密的副图指标!
  9. 常用计算机操作技巧与方法ppt,相见恨晚的20个常用PPT小技巧
  10. 图片去水印Inpaint5.2汉化破解便携版
  11. 墨刀导出的html可以修改,怎么把墨刀的原型复制出来
  12. 股价大涨61%,趣头条还能维持下一季增长吗?
  13. docker 启动时错误docker: Cannot connect to the Docker daemon
  14. 计算机专业毕业设计(论文)指导pdf,中国计量学院信息工程学院毕业设计论文指导书计算机专业.pdf...
  15. LDAP管理用户数据,用户UserAccountControl属性详解
  16. 计算机图形学 | 欢迎来到图形世界
  17. 【互联网人的英语】背景知识对于理解文章究竟有多重要?
  18. 纳米数据,足球比分,赛事数据接口api,足球数据接口
  19. 演讲稿丨史忠植:探寻大师足迹,一览马文•明斯基学术风采
  20. 抑郁症自我测试皮肤软件,39健康自测_中国最大的在线健康自测平台

热门文章

  1. 简单介绍pytorch中分布式训练DDP使用 (结合实例,快速入门)
  2. Adam和学习率衰减(learning rate decay)
  3. 中国移动业务支撑系统简介(BOSS、BASS、BOMC、4A及VGOP)
  4. 四阶龙格库塔法-实现异步电机模型仿真
  5. 在线教育网站的一些瞎折腾……
  6. 搭配online:抖音企业号数量破400万,奇妙好物节即将开启
  7. 当前最为流行的可视化大屏都是怎么做的?
  8. 【数据结构和算法】图论—克鲁斯卡尔(Kruskal)算法详解
  9. JAVA项目工作经验总结
  10. 30个免费且很棒的公共数据源分享