c语言可变参数列表处理

函数参数的传递原理

函数参数在内存中是以栈的形式存取,从右至左入栈。

参数在内存中存放格式:

在进程中,堆栈地址是从高到低分配的。当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减。

总之,函数在堆栈中的分布情况是,地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段。堆栈中,各个函数的分布情况是倒叙的。即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分。参数在堆栈中的

分布情况如下:最后一个参数->倒数第二个参数->...->第一个参数->函数返回地址->函数代码段.

宏定义:

typedef char* va_list

#define va_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))

#define va_arg(ap,t)(*(t*)((ap +=_INTSIZEOF(t))-_INTSIZEOF(t)))

#define va_end(ap) ( ap = (va_list)0)

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

1.在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);

2.然后应该对ap进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;

3.然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;

4.获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现

各个宏的功能:

va_list 用于声明一个变量,我们知道函数的可变参数列表其实就是一个字符串,所以va_list才被声明为字符型指针,这个类型用于声明一个指向参数列表的字符型指针变量,例如:va_list ap;//ap:arguement pointer

va_start(ap,v) 它的第一个参数是指向可变参数字符串的变量,第二个参数是可变参数函数的第一个参数,通常用于指定可变参数列表中参数的个数。

va_arg(ap,t) 它的第一个参数指向可变参数字符串的变量,第二个参数是可变参数的类型。

va_end(ap) 用于将存放可变参数字符串的变量清空(赋值为NULL).

va_list的用法:

(1)首先在函数里定义一具va_list型的变量,这个变量是指向参数的指针

(2)然后用va_start宏初始化变量刚定义的va_list变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。

(3)然后用va_arg返回可变的参数,va_arg的第二个参数是你要返回的参数的类型。

(4)最后用va_end宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。如果函数有多个可变参数的,依次调用va_arg获取各个参数。

va_list在编译器中的处理:

(1)在运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址。

(2)va_arg()取得类型t的可变参数值,在这步操作中首先apt = sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型*指针,这正是第一个可变参数在堆栈里的地址。然后用*取得这个地址的内容。

(3)va_end(),x86平台定义为ap = ((char*)0),使ap不再指向堆栈,而是跟null一样,有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如 gcc在linux的x86平台就是这样定义的。

例如:

1.
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

//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, int);#类型不能为char、signed char、unsigned char、short、unsigned short、signed short、short int、signed short int、unsigned short int、float
           if (strcmp(para, "") == 0 )
               break;
           printf("Parameter #%d is: %s\n", argno, para);
           argno++;
  }
  va_end( argp );
  //将argp置为NULL
  return 0;
}
int main()
{
   demo("DEMO", "This", "is", "a", "demo!", "");
   return 0;
}

2.
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

//第一个参数指定了参数的个数
int sum(int number,...)
{

  va_list vaptr;
  int i;
  int sum = 0;
  va_start(vaptr,number);
  for(i=0; i<number;i++)
  {
    sum += va_arg(vaptr,int); #类型不能为char、signed char、unsigned char、short、unsigned short、signed short、short int、signed short int、unsigned short int、float
  }
  va_end(vaptr);
  return sum;
}

int main()
{

  printf("%d\n",sum(4,4,3,2,1));
  return 0;

}

注意:

1.因为va_start, va_arg, va_end等定义成宏,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型,也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现。

2.va_arg(ap,type)取出一个参数的时候,type绝对不能为以下类型:char、signed char、unsigned char、short、unsigned short、signed short、short int、signed short int、unsigned short int、float。在C语言中,调用一个不带原型声明的函数时:调用者会对每个参数执行“默认实际参数提升(default argument promotions)”。同时,对可变长参数列表超出最后一个有类型声明的形式参数之后的每一个实际参数,也将执行上述提升工作。提升工作如下:a.float类型的实际参数将提升到double;b.char、short和相应的signed、unsigned类型的实际参数提升到int;c.如果int不能存储原值,则提升到unsigned
int。然后,调用者将提升后的参数传递给被调用者。所以,my_printf是绝对无法接收到上述类型的实际参数的。

时间: 2024-10-12 21:26:01

c语言可变参数列表处理的相关文章

C语言函数可变参数列表

C语言允许使用可变参数列表,我们常用的printf函数即为可变参数函数,C标准库提供了stdarg.h为我们提供了这方面支持:该头文件提供了一些类型和宏来支持可变参数列表,包括类型va_list,宏va_start.va_arg.va_end: 可变函数参数定义方法: #include <stdarg.h> void func(int count,...){ va_list ap; int ix, tmp; va_start(ap, a); for(ix=0;ix < count; ++

c语言中对可变参数列表的简单理解

函数原型中一般情况下参数的数目是固定的,但是如果想在不同的时候接收不定数目的参数时该怎么办呢?c语言提供了可变参数列表来实现. 可变参数列表是通过宏来实现的,这些宏定义在stdarg.h的头文件中.头文件中声明了一个va_list类型和va_start.va_arg.va_end三个宏.我们使用可变参数列表的时候需要声明一个va_list类型的变量配合这三个宏使用. va_start(va_list变量名,省略号前面最后一个有名字的参数):在提取可变参数前必须调用这个宏实现初始化. va_arg

【C语言】可变参数列表。

(1)先看一个求平均值的函数 #include <stdio.h> double average(int val,int v1,int v2,int v3,int v4,int v5) { double sum = v1; if(val >= 2) sum += v2; if(val >= 3) sum += v3; if(val >= 4) sum += v4; if(val >= 5) sum += v5; return sum/val; } int main ()

【C语言】求多个数中的最大值(可变参数列表)

求多个数中的最大值要求用可变参数列表: 代码如下: <span style="font-size:18px;">#include<stdio.h> #include<stdarg.h> int Max(int n,...) { int i=0; int max=0; va_list arg; va_start(arg,n); for(i=0;i<n;i++) { int val=va_arg(arg,int); if (val>max)

c语言 使用可变参数列表实现printf(my_printf)

//使用可变参数列表实现print("s\t c\n","bit-tech",'w'); #include<stdio.h> #include<stdarg.h> void int_to_char(int num) { if ((num / 10) > 0) int_to_char(num / 10); putchar(num %10+ 48); } void my_print(char p[],...) { char *str1 = 

【C语言】printf函数的简单实现(可变参数列表)

stdarg宏: 可变参数列表是通过宏来实现的,这些宏定义于stdarg.h头文件中,它是标准库的一部分.这个头文件声明一个类型va_list和三个宏va_start.va_arg和va_end.我们可以声明一个类型为va_list的变量,与这几个宏配合使用,访问参数. 声明一个va_list类型的变量arg,它用于访问参数列表的未确定部分.这个变量是调用va_start来初始化的.它的第一个参数是va_list的变量名,第2个参数是省略号前最后一个有名字的参数.初始化过程把var_arg变量设

(C语言)可变参数列表

C函数要在程序中用到以下这些宏: void va_start( va_list arg_ptr, prev_param ); type va_arg( va_list arg_ptr, type ); void va_end( va_list arg_ptr ); va_list:用来保存宏va_start.va_arg和va_end所需信息的一种类型.为了访问变长参数列表中的参数,必须声明 va_list类型的一个对象       定义: typedef char *  va_list; va

【C语言】用可变参数列表实现printf函数

//用可变参数列表实现printf函数 #include <stdio.h> #include <stdarg.h> void my_printf(const char *str,...) { va_list fun; va_start (fun,str); while(*str!=0) { if(*str=='c') { putchar(va_arg(fun,char )); } if(*str=='s') { puts(va_arg(fun,char *)); } str++;

C语言函数的可变参数列表

可变参数列表可通过宏来实现,这些宏定义在stdarg.h头文件中,它是标准库的一部分.这个头文件声明了一个类型va_list和三个宏----va_start,va_arg,va_end.可以声明一个类型为va_list的变量,与这几个宏配合使用,访问参数的值.参数列表的可变部分位于一个或多个普通参数(命名参数)的后面(即参数列表中至少要有一个命名参数),它在函数原型中以一个省略号表示. 例如:定义一个函数average可以计算若干个整数的平均值: 1 #include <stdarg.h> 2