我们在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. iOS常见内存泄漏解决

    iOS常见内存泄漏解决     1 OC和CF转化出现的内存警告 CFStringRef cfString = CFURLCreateStringByAddingPercentEscapes(kCFA ...

  2. Asp.net中request.QueryString与request.Params的区别 【转】

    Request.Form:获取以POST方式提交的数据(接收Form提交来的数据): Request.QueryString:获取地址栏参数(以GET方式提交的数据) Request:包含以上两种方式 ...

  3. 基于Karma和Jasmine的AngularJS测试

    1:工程目录结构 y@y:karma-t01$ tree -L 3.├── client│   ├── app│   │   └── user│   ├── bower_components│   │ ...

  4. GCC警告提示错误“cc1:all warnings being treated as errors”

    http://blog.csdn.net/zhangjs0322/article/details/25131787

  5. Windows消息拦截技术的应用

    Windows消息拦截技术的应用 民航合肥空管中心 周毅 一.前 言 众所周知,Windows程式的运行是依靠发生的事件来驱动.换句话说,程式不断等待一个消息的发生,然后对这个消息的类型进行判断,再做 ...

  6. 研究了下apache的漏洞CVE-2012-0053

    发一个大cookie过去,最新版本的,竟然显示了个\n 嘛意思 干嘛不直接删掉 Your browser sent a request that this server could not under ...

  7. POJ1068 Parencodings(模拟)

    题目链接. 分析: 水题. #include <iostream> #include <cstdio> #include <cstring> using names ...

  8. HBase协处理器

    说明:类似于RDBMS中触发器,允许用户在region服务器上运行自己的代码,在客户端用户不用关心操作具体在哪进行 使用场景:权限控制,回调函数(钩子函数).扫描统计等 主要类:observer和en ...

  9. 酷派D530刷机指引

    酷派D530是我的第一台智能手机,刚入手的时候是挺激动的,什么Root啦,精简系统删官方应用啦,app2sd啦,杂七杂八的应用装了一堆,折腾得不亦乐乎.但过了那个热度之后,现在我对于智能手机的要求还是 ...

  10. mysql启动停止,一台服务器跑 多个mysql数据库

    一.以非特权用户运行MySQL服务器在讨论如何启动MySQL服务器之前,让我们考虑一下应该以什么用户身份运行MySQL服务器.服务器可以手动或自动启动.如果你手动启动它, 服务器以你登录Unix(Li ...