c语言中可变参数函数设计方案_c语言可变长参数函数

2020-02-28 其他范文 下载本文

c语言中可变参数函数设计方案由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“c语言可变长参数函数”。

c语言中可变参数函数的设计

c语言中可变参数函数的设计

c语言中可变参数函数的设计

-----最近想好好学学这个, 先把网上搜集得资料贴上.========================

参数可变函数的实现(上)CSDN Blog推出文章指数概念,文章指数是对Blog文章综合评分后推算出的,综合评分项分别是该文章的点击量,回复次数,被网摘收录数量,文章长度和文章类型;满分100,每月更新一次。

此文献给如我一般还在探索C语言之路的朋友们。

注:本文中测试程序的编译环境为win2000和VC6.0 缘起:

作为一个程序员,我没有写过参数可变的函数,我相信大部分朋友也没有涉及过,或者我的境界层次太低了。那么缘何我要去揭这一层面纱呢?因为好奇!

我是个思维具有极大惰性的人,曾经识得参数可变函数,也懒得去深究,但是它的三点(函数声明时参数列表中的“…”)却深刻的映入/ 20 了我的记忆里,而且是带着若干个闪耀的问号。可是就在昨天,在拜读某君的高论时,它再一次出现了。我的资质真的是不太够,因为某君在谈到它时只是给出了中关于它的宏定义,我想大概在高手眼里,点这一下就神会了吧。可是他这么轻轻一点却使留在记忆里曾经的那几个问号无限的膨胀,以至于我这个又菜又懒的所谓程序员也萌生了莫大的好奇。

破题:

但凡所谓“实现”都是从没有到有的过程,但是我只是想去解惑它的实现,因为它原本就是好端端的正为成千上万的程序员们服务。

还是从我们熟悉的printf说起:

如果你是个C语言的程序员,无论你是初学者还是高高手,对于printf都不会陌生,甚至你已经用了无数次了。我已经说过我是个有极大惰性的人,所以每次用printf都是照本宣科,规规矩矩的按教科书上说的做,从来没有问过一个为什么,这就是所谓的“熟视无睹”吧。

其实,printf函数是一个典型的参数可变的函数。在保证它的第一个参数是字符串的条件下,你可以输任意数量任意合法类型的参数。只要你在第一个字符串参数中使用了对应的格式化字符串,你就可以输出正确的值。这难道不是件很有趣的事吗?那它是怎么做到的?

1,首先,怎么得到参数的值。对于一般的函数,我们可以通过参数对应在参数列表里的标识符来得到。但是参数可变函数那些可变的参数是没有参数标识符的,它只有“…”,所以通过标识符来得到是不可能的,我们只有另辟途径。/ 20

我们知道函数调用时都会分配栈空间,而函数调用机制中的栈结构如下图所示:

|......|

------------------

| 参数2 |

------------------

| 参数1 |

------------------

| 返回地址 |

------------------

|调用函数运行状态|

------------------

可见,参数是连续存储在栈里面的,那么也就是说,我们只要得到可变参数的前一个参数的地址,就可以通过指针访问到那些可变参数。但是怎么样得到可变参数的前一个参数的地址呢?不知道你注意到没有,参数可变函数在可变参数之前必有一个参数是固定的,并使用标识符,而且通常被声明为char*类型,printf函数也不例外。这样的话,我们就可以通过这个参数对应的标识符来得到地址,从而访问其他参数变得可能。我们可以写一个测试程序来试一下:/ 20

#include

void va_test(char* fmt,...);//参数可变的函数声明

void main(){

int a=1,c=55;

char b='b';

va_test(“”,a,b,c);//用四个参数做测试

}

void va_test(char* fmt,...)//参数可变的函数定义,注意第一个参数为char* fmt {

char *p=NULL;/ 20

p=(char *)&fmt;//注意不是指向fmt,而是指向&fmt,并且强制转化为char *,以便一个一个字节访问

for(int i = 0;i

{

printf(“%.4d ”,*p);//输出p指针指向地址的值

p++;} }

编译运行的结果为

0056 0000 0066 0000 | 0001 0000 0000 0000 | 0098 0000 0000 0000 | 0055 0000 0000 0000

由运行结果可见,通过这样方式可以逐一获得可变参数的值。

至于为什么通常被声明为char*类型,我们慢慢看来。

2,怎样确定参数类型和数量/ 20

通过上述的方式,我们首先解决了取得可变参数值的问题,但是对于一个参数,值很重要,其类型同样举足轻重,而对于一个函数来讲参数个数也非常重要,否则就会产生了一系列的麻烦来。通过访问存储参数的栈空间,我们并不能得到关于类型的任何信息和参数个数的任何信息。我想你应该想到了——使用char *参数。Printf函数就是这样实现的,它把后面的可变参数类型都放到了char *指向的字符数组里,并通过%来标识以便与其它的字符相区别,从而确定了参数类型也确定了参数个数。其实,用何种方式来到达这样的效果取决于函数的实现。比如说,定义一个函数,预知它的可变参数类型都是int,那么固定参数完全可以用int类型来替换char*类型,因为只要得到参数个数就可以了。

3,言归正传

我想到了这里,大概的轮廓已经呈现出来了。本来想就此作罢的(我的惰性使然),但是一想到如果不具实用性便可能是一堆废物,枉费我打了这么些字,决定还是继续下去。

我是比较抵制用那些不明所以的宏定义的,所以在上面的阐述里一点都没有涉及定义在的va(variable-argument)宏。事实上,当时让我产生极大疑惑和好奇的正是这几个宏定义。但是现在我们不得不要去和这些宏定义打打交道,毕竟我们在讨生计的时候还得用上他们,这也是我曰之为“言归正传”的理由。

好了,我们来看一下那些宏定义。

打开文件,找一下va_*的宏定义,发现不单单只有一组,但是在各组定义前都会有宏编译。宏编译指示的是不同硬件平台和编译器下用怎样的va宏定义。比较一下,不同之处主要在偏移量的计算上。我们还是拿个典型又熟悉的——X86的相关宏定义:/ 20

1)typedef char * va_list;

2)#define _INTSIZEOF(n)((sizeof(n)+ sizeof(int)1))

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

4)#define va_arg(ap,t)(*(t *)((ap += _INTSIZEOF(t))sizeof(type)))

其中,argp的类型是char *。

如果你想用va_arg从可变参数列表中提取出函数指针类型的参数,例如

int(*)(),则va_arg(argp, int(*)())被扩展为:

(*(int(*)()*)(((argp)+= sizeof(int(*)()))-sizeof(int(*)())))

显然,(int(*)()*)是无意义的。

解决这个问题的办法是将函数指针用typedef定义成一个独立的数据类型,例如:

typedef int(*funcptr)(); / 20

这时候再调用va_arg(argp, funcptr)将被扩展为:

(*(funcptr *)(((argp)+= sizeof(funcptr))-sizeof(funcptr)))

这样就可以通过编译检查了。

问题:可变长参数的获取

有这样一个具有可变长参数的函数,其中有下列代码用来获取类型为float的实参:

va_arg(argp, float);

这样做可以吗?

答案与分析:

不可以。在可变长参数中,应用的是“加宽”原则。也就是float类型被扩展成double;char, short被扩展成int。因此,如果你要去可变长参数列表中原来为float类型的参数,需要用va_arg(argp, double)。对char和short类型的则用va_arg(argp, int)。

问题:定义可变长参数的一个限制

为什么我的编译器不允许我定义如下的函数,也就是可变长参数,但是没有任何的固定参数?

int f(...)

{/ 20

...}

答案与分析:

不可以。这是ANSI C 所要求的,你至少得定义一个固定参数。

这个参数将被传递给va_start(),然后用va_arg()和va_end()来确定所有实际调用时可变长参数的类型和值。

第一篇

C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:

int printf(const char* format,...);

它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式:

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

一个简单的可变参数的C函数

先看例子程序。该函数至少有一个整数参数,其后占位符…,表示后面参数的个数不定。在这个例子里,所有的输入参数必须都是整/ 20 数,函数的功能只是打印所有参数的值。函数代码如下:

//示例代码1:可变参数函数的使用 #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”,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;}

下面解释一下这些代码。从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:/ 20

⑴由于在程序中将用到以下这些宏: 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 / 20

《c语言中可变参数函数设计方案.docx》
将本文的Word文档下载,方便收藏和打印
推荐度:
c语言中可变参数函数设计方案
点击下载文档
相关专题 c语言可变长参数函数 设计方案 函数 参数 c语言可变长参数函数 设计方案 函数 参数
[其他范文]相关推荐
    [其他范文]热门文章
      下载全文