我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:  
   例一: int   printf(   const   char*   format,   ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式:

  printf("%d",i);    
    printf("%s",s); 
    printf("the   number   is   %d   ,string   is:%s",   i,   s);

例二:vfprintf

/*
  vfprintf 函数的原型
  int vfprintf ( FILE * stream, const char * format, va_list arg ); 
 */

void test(char* ch, ...)
{
va_list arg;
va_start(arg, ch); //初始化 arg
vfprintf(stdout, ch, arg);
va_end(arg);
} void main()
{ test("%s,%d\n", "test1", );
test("%s,%s,%d\n", "test1", "test2", );
}

C语言中可变参数函数实现原理:

C函数调用的栈结构

可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则:参数是从右到左,逐一压入栈中的(栈的延伸方向是从高地址到低地址,栈底的占领着最高内存地址,先入栈的参数,其地理位置也就最高)

void fun1(char a, int b, double c, short d) ;

int main()
{
fun1(, 5.40, "hello world");
return ;
}
a = 0x0022FF50
b = 0x0022FF54
c = 0x0022FF5C

因此,函数的所有参数是存储在线性连续的栈空间中的,基于这种存储结构,这样就可以从可变参数函数中必须有的第一个普通参数来寻址后续的所有可变参数的类型及其值。

我们基本可以得出这样一个结论:

c.addr = b.addr + x_sizeof(b);  /*注意:  x_sizeof !=sizeof */
 b.addr = a.addr + x_sizeof(a);

由于内存对齐,编译器在栈上压入参数时,不是一个紧挨着另一个的,编译器会根据变参的类型将其放到满足类型对齐的地址上的,这样栈上参数之间实际上可能会是有空隙的。所以此时的ap计算应该改为:ap =  (char *)ap +sizeof(int) + _INTSIZEOF (n);

为了满足代码的可移植性,C标准库在stdarg.h中提供了诸多便利以供实现变长长度参数时使用:

#define a_list char*        
#define _INTSIZEOF (n) ((sizeof(n)+ sizeof(int )-1) & ~(sizeof(int )-1))            //将 n 与 4 向上对齐
#define va_start (ap,v) (ap = (va_list)&v + _INTSIZEOF(v))      //宏初始化变量ap
#define va_arg (ap,t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))     //va_arg返回可变的参数
#define va_end (ap) (ap = (va_list)0)      //用va_end宏结束可变参数的获取
 
//一个可变参数应用的简单例子
//可变参数
int Avg(int n, ...)
{
a_list arg; //char *arg;
va_start(arg, n );//init
int sum = ;
for (int i = ; i<n; ++i)
{
sum += va_arg(arg,int );
}
va_end(arg); //arg = 0; //野指针
return sum / n ;
}
void main()
{
int avg = Avg(, , , ,,);
printf( "avg = %d\n", avg);
}

欢迎讨论~~

 
 
 

c 中可变参数的实现的更多相关文章

  1. C语言中可变参数的函数(三个点,“...”)

    C语言中可变参数的函数(三个点,“...”) 本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end ...

  2. Java 中可变参数

    可变参数 Java 中可变参数 现在需要编写一个求和的功能,但是不知道有几个参数,在调用的时候才知道有几个参数,请问这如何实现呢? Java 给我们提供了一个 JDK 1.5 的新特性---可变参数 ...

  3. C语言中可变参数函数实现原理

    C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...

  4. 【转】C/C++中可变参数函数的实现

    转自:http://www.cnblogs.com/cylee025/archive/2011/05/23/2054792.html 在C语言的stdarg.h头文件中提供了三个函数va_start, ...

  5. C语言中可变参数的用法

    原文地址: http://blog.csdn.net/wooin/archive/2006/04/29/697106.aspx   我们在C语言编程中会遇到一些参数个数可变的函数,例如printf() ...

  6. 巩固java(六)----java中可变参数方法(非常实用哦)

    java提供了可变参数的方法,即方法的参数个数可以不确定,用"..."定义. import java.util.ArrayList; import java.util.List; ...

  7. C/C++中可变参数函数的实现

    在C语言的stdarg.h头文件中提供了三个函数va_start, va_end,va_arg和一个类型va_list.利用它们,我们可以很容易实现一个可变参数的函数.首先简单介绍一下这三个函数. 假 ...

  8. 18、Java中可变参数

    从JDK 1.5之后,Java允许定义形参可变的参数 例如: public void test(int a,String ... books){ for(String book:books){ Sys ...

  9. C语言中可变参数的使用

    在C语言程序编写中我们使用最多的函数一定包括printf以及很多类似的变形体.这个函数包含在C库函数中,定义为 int printf( const char* format, ...); 除了一个格式 ...

随机推荐

  1. CentOS下安装配置Cacti

    cacti简介 随着公司规模扩大,服务器也日渐增多,对服务器集群的网络流量和服务器性能实时监测显得愈发重要.开源阵营中常用有MRTG(MultiRouter Traffic Grapher)--基于S ...

  2. mongose排序查询

    Kc.find({bjid:req.params.bjid}).sort({'_id':1}).exec(function(err,kcs){ if(err){ res.json({no:0,msg: ...

  3. poj2352 Stars

    http://poj.org/problem?id=2352 #include <cstdio> #include <cstring> #define maxn 400000 ...

  4. (转载)PHP使用header函数设置HTTP头的示例方法表头

    (转载)http://justcoding.iteye.com/blog/601117/ 代码: //定义编码 header( 'Content-Type:text/html;charset=utf- ...

  5. Java中join()方法的理解

    thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程. 比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B. t.join ...

  6. 【6】JAVA---地址App小软件(QueryPanel.class)(表现层)

    查找模块: 年龄可进行段查找. 其他的都是模糊匹配. 空格为无用字符,会屏蔽的(除年龄). (如果在年龄中输入空格,会出现异常,当时没想到这点,要防护这点很容易的,但因为在这个小软件的编写过程,我主要 ...

  7. 基于用户映射的CAS单点登录系统设计与实现

    http://wenku.baidu.com/link?url=wAZR9AMkAAcOt5J_SfroXqU5IM5RhNWaP0-YUwvZT94761Qq1-7pKAt6ngOX1zG4tYec ...

  8. Post/Redirect/Get pattern | PRG 模式

    Post/Redirect/Get 是一种 web 开发设计模式,用于防止表单的重复提交. 默认情况,提交 Post 请求到服务器后,如果直接刷新浏览器,会重新在提交一次 Post 请求.在访问电商网 ...

  9. C# 实现3Des加密 解密

    3Des对每个数据块进行了三次的DES加密算法,是DES的一个更安全的变形.比起最初的DES,3DES更为安全. 都是感觉一目了然的摘过来. 下面是加密解密的源码.ECB模式的. public cla ...

  10. Cantor表(中等)

    2 3 1/2 2/1 题目分析 这是NoI的一道题目,不过题目比较有创意也比较适合新生,就是一道简单的找规律的题目,首先找到第N个数应该在第几个斜行,然后判断这一行是奇数还是偶数,偶数分母递减,分子 ...