void log( int log_level,  const char* file_name, int line_num, const char* format, ... )
{
.............
..............
va_list arg_list;
va_start( arg_list, format );
memset( arg_buffer, '\0', LOG_BUFFER_SIZE );
vsnprintf( arg_buffer, LOG_BUFFER_SIZE - , format, arg_list );
printf( "%s\n", arg_buffer );
fflush( stdout );
va_end( arg_list );

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; 
}

这个代码 有bug  但是想表达的意识还是很容易看懂的啊!!!!!

输出结果 不对啊 !!!!!!!!!

Output:: 



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); 
}
4.演示如何使用参数个数可变的函数,采用ANSI标准形式 

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

Parameter #0 is: This
Parameter #1 is: is
Parameter #2 is: a
Parameter #3 is: demo!


 
函数原型:int vsnprintf(char *str, size_t size, const char *format, va_list ap);

#include <stdio.h>
#include <stdarg.h> void SYSTEM(const char * format, ...)
{
char buff[];
va_list list;
va_start(list, format);
vsnprintf(buff, , format, list);
va_end(list);
printf("%s\n", buff);
} void main()
{
SYSTEM("%d_%s", , "abc");
} 输出:
6_abc
 
 
 

va_list arg_list va_start(arg_list, format) va_end( arg_list ) 原理的理解的更多相关文章

  1. C++可变参数列表处理宏va_list、va_start、va_end的使用

      VA_LIST是在C语言中解决变参问题的一组宏他有这么几个成员: 1)va_list型变量: #ifdef     _M_ALPHA typedef    struct{ char* a0; /* ...

  2. 【转】C++可变参数列表处理宏va_list、va_start、va_end的使用

    VA_LIST是在C语言中解决变参问题的一组宏他有这么几个成员: 1)va_list型变量: #ifdef     _M_ALPHA typedef    struct{ char* a0; /*po ...

  3. va_list 、va_start、 va_arg、 va_end 使用说明【转】

    转自:https://blog.csdn.net/f110300641/article/details/83822290 在ANSI C中,这些宏的定义位于stdarg.h中: typedef cha ...

  4. 对C语言中va_list,va_start,va_arg和va_end的一点理解

    这几个函数和变量是针对可变参数函数的,什么是可变参数函数呢,最经典的莫过于printf和scanf,这两个函数的声明如下: int printf(const char *format, ...); i ...

  5. va_list、va_start、va_arg、va_end

    转载:https://www.cnblogs.com/bwangel23/p/4700496.html 这几个函数和变量是针对可变参数函数的,什么是可变参数函数呢,最经典的莫过于printf和scan ...

  6. va_list、va_start和va_end使用

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

  7. C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数

    在定义可变参数的函数之前,先来理解一下函数参数的传递原理: 1.函数参数是以栈这种数据结构来存取的,在函数参数列表中,从右至左依次入栈. 2.参数的内存存放格式:参数的内存地址存放在内存的堆栈段中,在 ...

  8. C语言利用va_list、va_start、va_end、va_arg宏定义可变參数的函数

    在定义可变參数的函数之前,先来理解一下函数參数的传递原理: 1.函数參数是以栈这样的数据结构来存取的,在函数參数列表中,从右至左依次入栈. 2.參数的内存存放格式:參数的内存地址存放在内存的堆栈段中, ...

  9. ios 可变参数(va_list,va_start,va_end)

    例如:UIAlertView的init方法中的otherButtonTitles:(NSString *)otherButtonTitles, ...等多个可变参数. ios实现传递不定长的多个参数的 ...

随机推荐

  1. DesertWind TopCoder - 1570

    传送门 分析 首先我们要知道为了情况最坏,无论你到哪,一定会在你前往绿洲的最短路上的那片沙子上刮风,所以这个点到绿洲的最短路即为他相连的点中到绿洲距离最短的距离+3和次短的距离+1的最小值,具体实现见 ...

  2. WordCount 编码与测试

    word count github 项目地址:https://github.com/liuqiang666/wordCount PSP表格 PSP2.1  PSP阶段  预估耗时(小时)  实际耗时( ...

  3. js/jq基础(日常整理记录)-4-一个简单的自定义tree插件

    一.一个简单的自定义tree插件 上一篇是之前自定义的table插件,这一篇也是之前同期尝试做的一个tree插件. 话不多说,先看看长什么样子哈! 现在来看确实不好看,以后在优化吧! 数据源:ajax ...

  4. const与define的区别

    const与#define最大的差别,Const在堆栈分配了空间,而#define只是把具体数值 直接传递到目标变量罢了.或者说,const的常量是一个Run-Time的概念,他在程 序中确确实实的存 ...

  5. cross validation

    k-folder cross-validation:k个子集,每个子集均做一次测试集,其余的作为训练集.交叉验证重复k次,每次选择一个子集作为测试集,并将k次的平均交叉验证识别正确率作为结果.优点:所 ...

  6. angular 分页

    http://jsfiddle.net/SAWsA/11/# <html xmlns:ng="http://angularjs.org" ng-app lang=" ...

  7. 关于Linq对DataTable和List各自的两个集合筛选的相关操作技巧

    项目中用到了对两个集合的帅选等操作,简单总结下 1.Linq操作多个Datable 可以通过AsEnumerable()方法对DataTable进行Linq操作 //获取数据表所有数据 DataTab ...

  8. C 语言 clock() 函数,例:计算多项式值

    C 语言 clock() 函数,例:计算多项式值 /** * clock(): 捕捉从程序开始运行到 clock() 被调用时所耗费的时间. * 这个时间单位是 clock tick, 即" ...

  9. Android源码树中C代码的编译

    侯 Sir说:“源码之下,了无秘密.” 但有些秘密还是要搞起来了看得更真切,仅从静态代码的体位很难体会到运动时的妙处.因此环境搭好了,下一步就是调试.gdbserver搭配gdb的调试环境走得很顺利, ...

  10. python的发音

    我一直读的是:拍方(可能是受有道词典发音的影响了~),可是别人都听不懂,他们大多是读的拍森. 来看看下面这个小伙伴的解释,感觉他说的挺好: “θ”这个字符的发音,有 80% 的中国人(学英文的人)都读 ...