我们在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. Expanding Rods

    http://poj.org/problem?id=1 #include<cstdio> #include<cstring> #include<cmath> #in ...

  2. 查看Linux某个进程打开的文件句柄(file descriptor)数量

    先找到进程的pid 然后: lsof -p [pid] | wc -l 或者 ls /proc/[pid]/fd | wc -l 查看系统总共使用中的文件描述符数量: lsof | wc -l ref ...

  3. 【转】(DT系列一)DTS结构及其编译方法----不错

    原文网址:http://www.cnblogs.com/biglucky/p/4057476.html DTS结构及其编译方法 一:主要问题 1,需要了解dtsi与dts的关系 2,dts的结构模型 ...

  4. Jquery中常见问题

    最近也是在做项目的时候刚接触到jQuery.下面汇总一下遇到的几个问题 如何动态创建按钮 方式一: $("#a").html("<input type='butto ...

  5. UVa 10025: The ? 1 ? 2 ? ... ? n = k problem

    这道题仔细思考后就可以得到比较快捷的解法,只要求出满足n*(n+1)/2 >= |k| ,且n*(n+1)/2-k为偶数的n就可以了.注意n==0时需要特殊判断. 我的解题代码如下: #incl ...

  6. Asp.Net Webapi路由基本设置

    1.直接在Global.asax中添加配置 如: using MvcApplication4.App_Start; using System; using System.Collections.Gen ...

  7. windows 编程—— 常用函数 与 操作

    目录: MessageBox() 和 PlaySound() 获得窗口 或屏幕大小 获得字体大小 输出文字 屏蔽和显示控制台窗口 1. MessageBox() 和 PlaySound() Messa ...

  8. ViewPager禁止滑动以及它与内层滑动控件水平方向上事件冲突的解决方法

    一.上图 二.场景描写叙述 最近在做项目的时候.遇到一个怪异的需求,描写叙述例如以下: 1.ViewPager中嵌套3个View,当从View1滑动到View2时禁止ViewPager的滑动事件. 2 ...

  9. MySQL定时备份之使用Linux下的crontab定时备份实例

    这篇文章主要介绍了使用Linux下的crontab进行MySQL定时备份的例子,需要的朋友可以参考下   复制代码代码如下: ##################################### ...

  10. [转] vim 正则表达式 很强大

    毋庸多言,在vim中正则表达式得到了十分广泛的应用. 最常用的 / 和 :s 命令中,正则表达式都是不可或缺的. 下面对vim中的正则表达式的一些难点进行说明. 关于magic vim中有个magic ...