C语言的可变参数

合集下载

C语言可变参数

C语言可变参数

一、是什么我们学习C语言时最经常使用printf()函数,但我们很少了解其原型。

其实printf()的参数就是可变参数,想想看,我们可以利用它打印出各种类型的数据。

下面我们来看看它的原型:int printf( const char* format, ...);它的第一个参数是format,属于固定参数,后面跟的参数的个数和类型都是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式:printf("%d",i);printf("%s",s);printf("the number is %d ,string is:%s", i, s);那么它的原型是怎样实现的呢?我今天在看内核代码时碰到了vsprintf,花了大半天时间,终于把它搞的有点明白了。

二、先看两个例子不必弄懂,先大致了解其用法,继续往下看。

①一个简单的可变参数的C函数在函数simple_va_fun参数列表中至少有一个整数参数,其后是占位符…表示后面参数的个数不定.。

在这个例子里,所有输入参数必须都是整数,函数的功能只是打印所有参数的值。

#include <stdio.h>#include<stdarg.h>void simple_va_fun(int start, ...){va_list arg_ptr;int nArgValue =start;int nArgCout=0; //可变参数的数目va_start(arg_ptr,start); //以固定参数的地址为起点确定变参的内存起始地址。

do{++nArgCout;printf("the %d th arg: %d\n",nArgCout,nArgValue); //输出各参数的值nArgValue = va_arg(arg_ptr,int); //得到下一个可变参数的值} while(nArgValue != -1);return;}int main(int argc, char* argv[]){simple_va_fun(100,-1);simple_va_fun(100,200,-1);return 0;}②格式化到一个文件流,可用于日志文件FILE *logfile;int WriteLog(const char * format, ...){va_list arg_ptr;va_start(arg_ptr, format);int nWrittenBytes = vfprintf(logfile, format, arg_ptr);va_end(arg_ptr);return nWrittenBytes;}稍作解释上面两个例子。

C编程的可变参数

C编程的可变参数

可变参数是C语言编程的一个特色。

在我们一般编程中,函数的参数个数都是确定的,事先定下来的。

然而就有那么一部分函数,它的个数是不确定的,长度也不一定,这中间有什么秘密吗?其实,我们可以回忆一下哪些函数是可变参数的函数?其实也就是sprintf、printf 这样的函数而已。

那么这些函数有什么规律吗?关键就是在这个字符串上面。

我们可以举一个例子看看,void test(){printf("%s, value = %d\n", "hello", 10);}void test(){printf("%s, value = %d\n", "hello", 10);}test函数里面也就是一个简单的打印函数。

那么这个函数有什么特别的地方呢,那就是%s、%d和后面的字符是一一对应的,所以有多少个这样的字符,首参数后面就会剩下多少个参数。

那么后面参数的地址怎么获取呢?我们可以回想一下,堆栈一般是怎么压栈处理的,/** stack space:** 参数3 | up* 参数2 |* 参数1 v down*//** stack space:** 参数3 | up* 参数2 |* 参数1 v down*/ 因为参数是按照从右向左依次压入的,所以后面参数的地址依次根据“%”处理即可。

下面我们就可以自己写一个PrintInt打印int数据的函数,首先创建一个框架,voidPrintInt(char* buffer, int data, ...){return;}voidPrintInt(char* buffer, int data, ...){return;} 然后验证buffer参数中是否有%d,如果存在这样的一个字符,就需要打印一个整数,voidPrintInt(char* buffer, int data, ...){static char space[1024];char temp[32];int* start;int count;if(NULL == buffer)return;memset(space, 0, 1024);memset(temp, 0, 32);start = (int*) &buffer;count = 0;while(buffer[count]){if(!strncmp(&buffer[count], "%d", strlen("%d"))){start ++;itoa(*start, temp, 10);strcat(space, temp);count += 2;continue;}space[strlen(space)] = buffer[count];count ++;}memset(buffer, 0, strlen(buffer));memmove(buffer, space, strlen(space));return;}voidPrintInt(char* buffer, int data, ...){static char space[1024];char temp[32];int* start;int count;if(NULL == buffer)return;memset(space, 0, 1024);memset(temp, 0, 32);start = (int*) &buffer;count = 0;while(buffer[count]){if(!strncmp(&buffer[count], "%d", strlen("%d"))){start ++;itoa(*start, temp, 10);strcat(space, temp);count += 2;continue;}space[strlen(space)] = buffer[count];count ++;}memset(buffer, 0, strlen(buffer));memmove(buffer, space, strlen(space));return;} 为了验证我们的函数是否正确,可以编写测试函数验证一下,void display(){char buffer[32] = {"%d %d %d %d\n"};PrintInt(buffer, 1, 2, 3, 4);printf(buffer);}void display(){char buffer[32] = {"%d %d %d %d\n"};PrintInt(buffer, 1, 2, 3, 4);printf(buffer);}。

c语言 可变参数 传递

c语言 可变参数 传递

c语言可变参数传递
可变参数传递是C语言中常用的一种技巧,它允许我们在函数中传递不定数量的参数。

这种技巧对于需要处理不定数量的数据或需要编写可重用的函数非常有用。

在C语言中,可变参数传递依赖于标准库中的stdarg.h头文件。

该头文件中定义了一系列宏和类型,用于处理可变参数。

其中最重要的类型是va_list,它是一个指向参数列表的指针。

在函数中使用可变参数传递时,我们需要先定义一组参数,然后再使用va_start宏来初始化参数列表。

接下来可以使用va_arg宏来逐个获取参数,并使用va_end宏来清理参数列表。

需要注意的是,可变参数传递存在一定的风险,因为我们无法在编译时检查传递的参数是否正确。

因此,在使用可变参数传递时,我们需要保证传递的参数类型和数量与函数的定义相匹配,以避免出现运行时错误。

总之,可变参数传递是C语言中非常有用的一种技巧,它可以帮助我们编写可重用的函数和处理不定数量的数据。

但是,需要注意传递的参数类型和数量,并保证在编写函数时考虑到可能出现的运行时错误。

- 1 -。

c语言可变参数类型

c语言可变参数类型

c语言可变参数类型C语言可变参数类型在C语言中,可变参数类型是一种强大而灵活的特性,允许我们处理不确定数量的参数。

通过使用可变参数类型,我们可以为函数提供各种不同数量的参数,这对于编写通用的函数和库非常有用。

本文将详细介绍C语言中的可变参数类型,并逐步回答以下问题:1. 什么是可变参数类型?2. 如何声明和使用可变参数类型?3. 可变参数类型的底层实现原理是什么?4. 如何在可变参数类型中处理参数的数量和类型?5. 可变参数类型的使用案例有哪些?6. 可变参数类型的优缺点是什么?7. 可变参数类型与其他语言的差异是什么?接下来,让我们逐一回答这些问题。

1. 什么是可变参数类型?可变参数类型是一种C语言特性,用于处理不确定数量的参数。

它允许我们在函数声明中指定一个或多个固定参数,然后使用省略号(...)表示可能的可变参数。

这使得函数可以接收任意数量的参数。

2. 如何声明和使用可变参数类型?要声明可变参数类型的函数,我们需要使用标准库函数`stdarg.h`中的宏和类型。

具体步骤如下:- 首先,在函数声明中包含`stdarg.h`头文件。

- 然后,在函数参数列表中指定一个或多个固定参数,接下来是省略号(...)。

- 在函数体中,我们使用`va_list`类型的变量来存储可变参数列表。

- 使用`va_start`宏初始化`va_list`变量。

- 使用`va_arg`宏来访问参数的值。

- 使用`va_end`宏结束可变参数的访问。

以下是一个使用可变参数类型的示例代码:c#include <stdarg.h>#include <stdio.h>void print_numbers(int num, ...){va_list args;va_start(args, num);for (int i = 0; i < num; i++) {int value = va_arg(args, int);printf("%d ", value);}va_end(args);}int main(){print_numbers(5, 1, 2, 3, 4, 5);return 0;}在这个示例中,`print_numbers`函数接收一个整数参数`num`,后面跟着任意数量的整数参数。

C语言可变参数函数的原理与实现

C语言可变参数函数的原理与实现

赋值为 0,使它不指向内存的变量。下面给出 va_end 在 C 中的源码:
1 #define va_end(ap)
( ap = (va_list)0 )
va_end 必须在 va_arg 读完所有参数后再调用,否则会产生意想不到的后果。特别地,当
可变参数表函数在程序执行过程中不止一次被调用时,在函数体每次处理完可变参数表之后必须
保证能对不同类型的可变参数进行正确地寻址,比如实参依次为 char 型、char * 型、int 型和
float 型时,在 va_arg 中它们的类型则应分别为 int、char *、int 和 double.
void va_end(va_list ap)也是一个宏,该宏用于被调用函数完成正常返回,功能就是把指针 ap
变参数时,它声明一个类型为 va_list 的变量,该变量用来指向 va_arg 和 va_end 所需信息的位
置。下面给出 va_list 在 C 中的源码:
1 typedef char * va_list;
void va_start(va_list ap,lastfix)是一个宏,它使 va_list 类型变量 ap 指向被传递给函数的
调用一次 va_end,以保证正确地恢复栈。
一个变参数函数至少需要有一个普通参数,其普通参数可以具有任何类型。在函数定义中,这种
函数的最后一个普通参数除了一般的用途之外,还有其他特殊用途。下面从一个例子开始说明有
关的问题。
假设我们想定义一个函数 sum,它可以用任意多个整数类t)&v + _INTSIZEOF(v) )
//得到可变
type va_arg(va_list ap,type)也是一个宏,其使用有双重目的,第一个是返回 ap 所指对象

c 多个可变参数

c 多个可变参数

c 多个可变参数C 多个可变参数在C语言中,可变参数是一种非常有用的功能。

它允许我们在函数中传入不确定数量的参数,在处理不同情况下提供更大的灵活性和可扩展性。

在本文中,我们将探讨C语言中多个可变参数的使用,并介绍一些常见的应用场景和注意事项。

一、可变参数的基本概念可变参数是指函数的参数数量不固定,可以根据实际需求传入任意数量的参数。

在C语言中,可变参数是通过stdarg.h头文件中的宏和函数来实现的。

常用的宏有va_list、va_start、va_arg和va_end。

其中,va_list用于定义一个可变参数的列表,va_start 用于初始化可变参数列表,va_arg用于获取可变参数的值,va_end 用于结束可变参数的获取。

二、多个可变参数的使用在C语言中,我们可以定义函数来接受多个可变参数。

例如,我们可以定义一个函数来计算多个数的平均值:```#include <stdio.h>#include <stdarg.h>double average(int count, ...){va_list args;va_start(args, count);double sum = 0;for (int i = 0; i < count; i++){sum += va_arg(args, double);}va_end(args);return sum / count;}int main(){double result = average(3, 1.5, 2.5, 3.5);printf("平均值为%.2lf\n", result);return 0;}```在上面的代码中,average函数接受一个整数count作为参数,表示后面传入的参数个数。

通过va_list和va_start宏来初始化可变参数列表,然后使用va_arg宏来逐个获取参数的值,最后通过va_end宏来结束可变参数的获取。

c语言可变参数函数

c语言可变参数函数
C语言是一种面向过程的编程语言,广泛应用于系统软件开发,是一门高级的程序设计语言。

变参数函数就是指允许定义函数时无限制参数,也就是说,一个函数可以接受任意个参数,而不一定是固定的参数个数。

C语言中有许多种可变参数函数,他们都是不同类型的,但都具有相似的特征。

例如,可变参数函数可以用来处理各种不同类型参数,而且函数能够有效率地处理参数列表。

可变参数函数是C语言中重要的一种函数,它在编写程序时有很多应用。

可变参数函数可以用来打印信息,比如 printf()数可以用来输出各种格式的信息,而 scanf()数则可以用来从标准输入中读取各种类型的数据。

此外,C语言中的可变参数函数还包括几个特殊的参数,比如
va_list va_start。

va_list用来定义和声明可变参数列表的变量,而 va_start是用来初始化可变参数列表的函数,并且要求参数必须是一个合法的参数列表。

另外,可变参数函数还可以用来处理可变长度的参数,比如可以通过 vfprintf()数来打印变长字符串,vprintf()数可以打印可变数量的字符,而 vscanf()可以读取可变长度的字符串。

可变参数函数是C语言中一种强大的函数,可以为编写程序带来很大的便利。

但是,可变参数函数也有一些不足之处,比如可变参数函数要求参数有一定的格式,否则就会出现错误,另外,可变参数函
数还会增加程序的复杂度,因为要处理更多的参数。

总之,可变参数函数是C语言中一种强大的函数,可以提高编程的效率,给程序设计师带来更大的便利。

但是,在使用可变参数函数时,也要注意它的不足之处,以免出现一些意想不到的错误。

可变参数模板和c语言可变参数

可变参数模板和C语言可变参数一、介绍可变参数可变参数是指函数的参数在数量和类型上是可变化的,在C语言中,可变参数通过省略号"..."来实现。

可变参数的使用可以使函数具有更大的灵活性,能够处理不同数量和类型的参数,非常适用于需要处理多个参数的函数。

二、可变参数模板的概念可变参数模板是C++11引入的新特性,它是对C语言可变参数的一个更加抽象和安全的封装。

可变参数模板通过模板函数和模板类来实现对可变参数的处理,避免了对可变参数进行手工解析的复杂工作,提高了代码的可读性和维护性。

三、C语言可变参数的使用方法在C语言中,可变参数的使用是通过标准库中的`<stdarg.h>`头文件中的宏和函数来实现的。

1. `va_list`类型和`va_start`宏:`va_list`类型用来定义一个可变参数的列表,`va_start`宏用来初始化可变参数列表。

2. `va_arg`宏:`va_arg`宏用来访问可变参数列表中的参数。

3. `va_end`宏:`va_end`宏用来结束可变参数列表的访问。

下面是一个简单的示例,演示了如何在C语言中使用可变参数:```c#include <stdio.h>#include <stdarg.h>void printNumbers(int num, ...) {va_list args;va_start(args, num);for (int i = 0; i < num; i++) {int num = va_arg(args, int);printf("d ", num);}va_end(args);}int m本人n() {printNumbers(3, 1, 2, 3);return 0;}四、可变参数模板的使用方法在C++11中,可以使用模板函数和模板类来处理可变参数,从而避免了手动解析可变参数的繁琐工作。

c语言函数可变参数列表

c语⾔函数可变参数列表在函数原型中,列出了函数期望接受的参数,但原型只能显⽰固定数⽬的参数。

让⼀个函数在不同的时候接受不同数⽬的参数是不是可以呢?答案是肯定的,但存在⼀些限制。

考虑⼀个计算⼀系列值的平均值的函数。

如果这些值存储于数组中,这个任务就太简单了,所以为了让问题变得更有趣⼀些,我们假定它们并不存储于数组中。

先来看⼀个计较差的,也是不太稳定的⼀个解决⽅案:1//计算指定数⽬的值的平均值(差的⽅案)2float average(int n_value,int v1,int v2,int v3,int v4,int v5)3 {4float sum=v1;5if(n_values>=2)6 sum+=v2;7if(n_values>=3)8 sum+=v3;9if(n_values>=4)10 sum+=v4;11if(n_values>=5)12 sum+=v5;13return sum/n_values;14 }这个函数存在⼏个问题:⾸先,它不对参数的数量进⾏测试,⽆法检测参数过多的这种情况。

不过这个问题容易解决,简单加上测试就是了。

其次,函数⽆法处理超过5个的值。

要解决这个问题,你只有在已经很臃肿的代码中再增加⼀些类似的代码。

另外还存在⼀个更为严重的问题:就是实参数与形参的对应不⼀定准确。

stdarg宏可变参数列表是通过宏来实现的,这些宏定义stdarg.h头⽂件,它是标准库的⼀部分。

这个头⽂件声明了⼀个类型va_list和三个宏——va_start、va_arg、va_end。

我们可以声明⼀个类型为va_list的变量,与这⼏个宏配合使⽤,访问参数的值。

下⾯的程序使⽤三个宏正确的完成计算指定数⽬的值的平均值的任务。

1//指定数量的值的平均值2 #include<stdarg.h>3float(int values,...)4 {5 va_list var_arg;6int count;7float sum=0;89 var_start(var_arg,n_values);//准备访问可变参数10for(count=0;count<n_values;count+=1) //添加取⾃可变参数表的值11 {12 sum+=var_varg(var_arg,int);13 }14 var_end(var_arg); //完成处理可变参数15return sum/n_values;16 }。

C语言可变参数函数详解示例

C语言可变参数函数详解示例C语言提供了可变参数函数的机制,以便可以处理不定数量的参数。

可变参数函数可以接受任意数量的参数,并且这些参数可以具有不同的类型。

可变参数函数的原型声明在<stdarg.h>头文件中,其定义如下:```cvoid va_start (va_list ap, last_arg);type va_arg (va_list ap, type);void va_end (va_list ap);```可变参数的调用方式类似于函数的调用,但是参数的个数和类型是可以变化的。

以下是一个使用可变参数函数的示例,以计算任意数量整数的和为例:```c#include <stdio.h>#include <stdarg.h>int sum(int count, ...)va_list ap;int sum = 0;va_start(ap, count);for (int i = 0; i < count; i++)int num = va_arg(ap, int);sum += num;}va_end(ap);return sum;int maiint result = sum(5, 10, 20, 30, 40, 50);printf("Sum: %d\n", result);return 0;```上述代码中,我们定义了一个可变参数的函数`sum`,它接受一个整数`count`和不定数量的整数参数。

函数使用`va_list`类型的变量`ap`来迭代访问参数列表。

在函数内部,首先调用`va_start`宏来初始化`ap`变量,该宏接受最后一个参数,用于定位可变参数的位置。

然后,我们使用`va_arg`宏来获取参数列表中的每个值,并累加到`sum`变量中。

最后,我们使用`va_end`宏来结束可变参数的访问。

在`main`函数中,我们调用`sum`函数,并传递5个整数作为参数。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C语言的可变参数C语言中有些函数使用可变参数,比如常见的int printf( const char* format, ...),第一个参数format是固定的,其余的参数的个数和类型都不固定。

V A函数(variable argument function),参数个数可变函数,又称可变参数函数。

C/C++编程中,系统提供给编程人员的va函数很少。

*printf()/*scanf()系列函数,用于输入输出时格式化字符串;exec*()系列函数,用于在程序中执行外部文件(main(int argc,char*argv[]算不算呢,与其说main()也是一个可变参数函数,倒不如说它是exec*()经过封装后的具备特殊功能和意义的函数,至少在原理这一级上有很多相似之处)。

由于参数个数的不确定,使va函数具有很大的灵活性,易用性,对没有使用过可变参数函数的编程人员很有诱惑力;C语言用va_start等宏来处理这些可变参数。

这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。

下面我们来分析这些宏。

在stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:typedef char * va_list;#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#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 )_INTSIZEOF(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。

一般的sizeof(int)=4,也就是参数在内存中的地址都为4的倍数。

比如,如果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。

为了能从固定参数依次得到每个可变参数,va_start,va_arg充分利用下面两点:1.C语言在函数调用时,先将最后一个参数压入栈2.X86平台下的内存分配顺序是从高地址内存到低地址内存高位地址第N个可变参数。

第二个可变参数第一个可变参数? ap固定参数? v低位地址由上图可见,v是固定参数在内存中的地址,在调用va_start后,ap指向第一个可变参数。

这个宏的作用就是在v的内存地址上增加v所占的内存大小,这样就得到了第一个可变参数的地址。

接下来,可以这样设想,如果我能确定这个可变参数的类型,那么我就知道了它占用了多少内存,依葫芦画瓢,我就能得到下一个可变参数的地址。

让我再来看看va_arg,它先ap指向下一个可变参数,然后减去当前可变参数的大小即得到当前可变参数的内存地址,再做个类型转换,返回它的值。

要确定每个可变参数的类型,有两种做法,要么都是默认的类型,要么就在固定参数中包含足够的信息让程序可以确定每个可变参数的类型。

比如,printf,程序通过分析format 字符串就可以确定每个可变参数大类型。

最后一个宏就简单了,va_end使得ap不再指向有效的内存地址。

一、从printf()开始从大家都很熟悉的格式化字符串函数开始介绍可变参数函数。

原型:int printf(const char * format, ...);参数format表示如何来格式字符串的指令,…表示可选参数,调用时传递给"..."的参数可有可无,根据实际情况而定。

系统提供了vprintf系列格式化字符串的函数,用于编程人员封装自己的I/O函数。

int vprintf / vscanf(const char * format, va_list ap); // 从标准输入/输出格式化字符串int vfprintf / vfsacanf(FILE * stream, const char * format, va_list ap); // 从文件流int vsprintf / vsscanf(char * s, const char * format, va_list ap); // 从字符串// 例1:格式化到一个文件流,可用于日志文件同理,也可以从文件中执行格式化输入;或者对标准输入输出,字符串执行格式化。

在上面的例1中,WriteLog()函数可以接受参数个数可变的输入,本质上,它的实现需要vprintf()的支持。

如何真正实现属于自己的可变参数函数,包括控制每一个传入的可选参数。

二、 va函数的定义和va宏C语言支持va函数,作为C语言的扩展--C++同样支持va函数,但在C++中并不推荐使用,C++引入的多态性同样可以实现参数个数可变的函数。

不过,C++的重载功能毕竟只能是有限多个可以预见的参数个数。

比较而言,C中的va函数则可以定义无穷多个相当于C++的重载函数,这方面C++是无能为力的。

va函数的优势表现在使用的方便性和易用性上,可以使代码更简洁。

C编译器为了统一在不同的硬件架构、硬件平台上的实现,和增加代码的可移植性,提供了一系列宏来屏蔽硬件环境不同带来的差异。

ANSI C标准下,va的宏定义在stdarg.h中,它们有:va_list,va_start(),va_arg(),va_end()。

// 例2:求任意个自然数的平方和:可变参数函数的原型声明格式为:type VAFunction(type arg1, type arg2, … );参数可以分为两部分:个数确定的固定参数和个数可变的可选参数。

函数至少需要一个固定参数,固定参数的声明和普通函数一样;可选参数由于个数不确定,声明时用"…"表示。

固定参数和可选参数公同构成一个函数的参数列表。

借助上面这个简单的例2,来看看各个va_xxx的作用。

va_list arg_ptr:定义一个指向个数可变的参数列表指针;va_start(arg_ptr, argN):使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,说明:argN是位于第一个可选参数之前的固定参数,(或者说,最后一个固定参数;…之前的一个参数),函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。

如果有一va函数的声明是void va_test(char a, char b, char c, …),则它的固定参数依次是a,b,c,最后一个固定参数argN 为c,因此就是va_start(arg_ptr, c)。

va_arg(arg_ptr, type):返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中下一个参数。

va_copy(dest, src):dest,src的类型都是va_list,va_copy()用于复制参数列表指针,将dest初始化为src。

va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。

说明:指针arg_ptr被置无效后,可以通过调用va_start()、va_copy()恢复arg_ptr。

每次调用va_start() / va_copy()后,必须得有相应的va_end()与之匹配。

参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内。

三、编译器如何实现va例2中调用SqSum(7, 2, 7, 11, -1)来求7, 2, 7, 11的平方和,-1是结束标志。

简单地说,va函数的实现就是对参数指针的使用和控制。

typedef char * va_list; // x86平台下va_list的定义函数的固定参数部分,可以直接从函数定义时的参数名获得;对于可选参数部分,先将指针指向第一个可选参数,然后依次后移指针,根据与结束标志的比较来判断是否已经获得全部参数。

因此,va函数中结束标志必须事先约定好,否则,指针会指向无效的内存地址,导致出错。

这里,移动指针使其指向下一个参数,那么移动指针时的偏移量是多少呢,没有具体答案,因为这里涉及到内存对齐(alignment)问题,内存对齐跟具体使用的硬件平台有密切关系,比如大家熟知的32位x86平台规定所有的变量地址必须是4的倍数(sizeof(int) = 4)。

va机制中用宏_INTSIZEOF(n)来解决这个问题,没有这些宏,va的可移植性无从谈起。

首先介绍宏_INTSIZEOF(n),它求出变量占用内存空间的大小,是va的实现的基础。

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )#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 ) // 将指针置为无效下表是针对函数int TestFunc(int n1, int n2, int n3, …)参数传递时的内存堆栈情况。

(C编译器默认的参数传递方式是__cdecl。

)对该函数的调用为int result = TestFunc(a, b, c, d. e); 其中e为结束标志。

从上图中可以很清楚地看出va_xxx宏如此编写的原因。

1.va_start。

为了得到第一个可选参数的地址,我们有三种办法可以做到:A) = &n3 + _INTSIZEOF(n3)// 最后一个固定参数的地址+ 该参数占用内存的大小B) = &n2 + _INTSIZEOF(n3) + _INTSIZEOF(n2)// 中间某个固定参数的地址+ 该参数之后所有固定参数占用的内存大小之和C) = &n1 + _INTSIZEOF(n3) + _INTSIZEOF(n2) + _INTSIZEOF(n1)// 第一个固定参数的地址+ 所有固定参数占用的内存大小之和从编译器实现角度来看,方法B),方法C)为了求出地址,编译器还需知道有多少个固定参数,以及它们的大小,没有把问题分解到最简单,所以不是很聪明的途径,不予采纳;相对来说,方法A)中运算的两个值则完全可以确定。

相关文档
最新文档