
你的问题的意思不明确啊。简单为你说下吧。
形参是定义函数时的参数,目的是用来接收调用函数时的实参值。实参是调用函数时的参数,为函数对应的形参提供明确的数值或地址。函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。
形参和实参可以相同也可以不相同,但实参和形参在数量上,类型上,顺序上应严格一致。
例如:
int
process(int
a
,double
b)
//a,b为形参,没有具体数值
{
b=a2;
return
b;
}
int
main()
{
int
a=1
;
double
b;
process(a,b);
//a,b为实参,a具体的值由main()中定义初始化的a对应传入,b由process的函数体计算可得。
printf("%f",b);
}
形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。
所谓函数,只是一个相对独立的封装代码块,其作为就是为了方便各处重复调用。
别说是重复调用两次,你无限调用都没人能管你。
你这里的原因,首先得看你当前的函数的作用,也就是运行它将产生什么效果。
当前函数运行的效果是替换,还是追加。如果是替换,那不用说,肯定是以最后一运行为准。
如果是追加,结果还是只能看到最后的运行结果,那么你得看看,你前一次调用是否起作用了。
写一个简单的可变参数的C函数
下面我们来探讨如何写一个简单的可变参数的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在这里是variable-argument(可变参数)的意思
这些宏定义在stdargh中,所以用到可变参数的程序应该包含这个
头文件下面我们写一个简单的可变参数的函数,改函数至少有一个整数
参数,第二个参数也是整数,是可选的函数只是打印这两个参数的值
void simple_va_fun(int i, )
{
va_list arg_ptr;
int j=0;
va_start(arg_ptr, i);
j=va_arg(arg_ptr, int);
va_end(arg_ptr);
printf("%d %d\n", i, j);
return;
}
我们可以在我们的头文件中这样声明我们的函数:
extern void simple_va_fun(int i, );
我们在程序中可以这样调用:
simple_va_fun(100);
simple_va_fun(100,200);
从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:
1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变
量是指向参数的指针
2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第
一个可变参数的前一个参数,是一个固定的参数
3)然后用va_arg返回可变的参数,并赋值给整数j va_arg的第二个
参数是你要返回的参数的类型,这里是int型
4)最后用va_end宏结束可变参数的获取然后你就可以在函数里使
用第二个参数了如果函数有多个可变参数的,依次调用va_arg获
取各个参数
如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)simple_va_fun(100);
结果是:100 -123456789(会变的值)
2)simple_va_fun(100,200);
结果是:100 200
3)simple_va_fun(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果
正确,但和我们函数最初的设计有冲突下面一节我们探讨出现这些结果
的原因和可变参数在编译器中是如何处理的
(二)可变参数在编译器中的处理
我们知道va_start,va_arg,va_end是在stdargh中被定义成宏的,
由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下
面以VC++中stdargh里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)主要是为了某些需要内存的对齐的系统C语言的函
数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置我
们看到va_list被定义成char,有一些平台或 *** 作系统定义为void再
看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的
地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆
栈的地址,如图:
高地址|-----------------------------|
|函数返回地址 |
|-----------------------------|
| |
|-----------------------------|
|第n个参数(第一个可变参数) |
|-----------------------------|<--va_start后ap指向
|第n-1个参数(最后一个固定参数)|
低地址|-----------------------------|<-- &v
图( 1 )
然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我
们看一下va_arg取int型的返回值:
j= ( (int)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
首先ap+=sizeof(int),已经指向下一个参数的地址了然后返回
ap-sizeof(int)的int指针,这正是第一个可变参数在堆栈里的地址
(图2)然后用取得这个地址的内容(参数值)赋给j
高地址|-----------------------------|
|函数返回地址 |
|-----------------------------|
| |
|-----------------------------|<--va_arg后ap指向
|第n个参数(第一个可变参数) |
|-----------------------------|<--va_start后ap指向
|第n-1个参数(最后一个固定参数)|
低地址|-----------------------------|<-- &v
图( 2 )
最后要说的是va_end宏的意思,x86平台定义为ap=(char)0;使ap不再
指向堆栈,而是跟NULL一样有些直接定义为((void)0),这样编译器不
会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的
在这里大家要注意一个问题:由于参数的地址用于va_start宏,所
以参数不能声明为寄存器变量或作为函数或数组类型
关于va_start, va_arg, va_end的描述就是这些了,我们要注意的
是不同的 *** 作系统和硬件平台的定义有些不同,但原理却是相似的
(三)可变参数在编程中要注意的问题
因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,
可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能
地识别不同参数的个数和类型
有人会问:那么printf中不是实现了智能识别参数吗那是因为函数
printf是从固定参数format字符串来分析出参数的类型,再调用va_arg
的来获取可变参数的也就是说,你想实现智能识别可变参数的话是要通
过在自己的程序里作判断来实现的
另外有一个问题,因为编译器对可变参数的函数的原型检查不够严
格,对编程查错不利如果simple_va_fun()改为:
void simple_va_fun(int i, )
{
va_list arg_ptr;
char s=NULL;
va_start(arg_ptr, i);
s=va_arg(arg_ptr, char);
va_end(arg_ptr);
printf("%d %s\n", i, s);
return;
}
可变参数为char型,当我们忘记用两个参数来调用该函数时,就会出现
core dump(Unix) 或者页面非法的错误(window平台)但也有可能不出
错,但错误却是难以发现,不利于我们写出高质量的程序
以下提一下va系列宏的兼容性
System V Unix把va_start定义为只有一个参数的宏:
va_start(va_list arg_ptr);
而ANSI C则定义为:
va_start(va_list arg_ptr, prev_param);
如果我们要用system V的定义,应该用varargh头文件中所定义的
宏,ANSI C的宏跟system V的宏是不兼容的,我们一般都用ANSI C,所以
用ANSI C的定义就够了,也便于程序的移植
使用字典,比如下面这样:
def funcA():pass
def funcB():
pass
def func_None():
print "cannot find func"
func_dict = {"a": funcA, "b": funcB}
def func(x):
return func_dictget(x, func_None)()
在有switch的语言中,一般都是使用switch来根据入参进行判断。但是python中没有switch,因为根本不需要!!使用字典代替switch,性能更高,而且这种方法的表述能力更强一点。
另外func_dictget(x, func_None)()中方法是从字典中取出值对应的函数对象,然后后面加上()是执行该对象的__call__方法。因为python中函数就是实现了__call__方法的对象。所以可以这么使用。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)