int printf( const char* 格式,);
除了有固定的参数格式外,后面的参数个数和类型还有
变量,例如我们可以有以下不同的调用方式:
printf("%d",i);
printf("%s",s);
printf("数字是%d ,字符串是:%s", i, s);
如何编写带有可变参数的C函数以及编译器如何实现这些可变参数函数
目前情况如何?本文将讨论这个问题。希望能给大家带来一些帮助。懂C++ 的人
网友们都知道,这些问题在C++中不存在,因为C++具有多态性。但C++是C的一个版本
Superset,以下技术也可以用在C++程序中。限于我的水平,如果有的话
如果我说得不合适,请指正。
(1) 编写一个简单的可变参数C函数
让我们探讨如何编写带有可变参数的简单C 函数。写入可变参数
C函数在程序中需要使用以下宏:
void va_start( va_list arg_ptr, prev_param );
类型va_arg( va_list arg_ptr, 类型);
无效va_end( va_list arg_ptr );
va 这里的意思是变量参数。
这些宏在stdarg.h 中定义,因此使用可变参数的程序应包含此宏
头文件。接下来我们编写一个带有可变参数的简单函数。该函数必须至少有一个整数。
参数,第二个参数也是一个整数,并且是可选的。该函数只是打印这两个参数的值。
void simple_va_fun(int i,)
{
va_list arg_ptr;
整数j=0;
va_start(arg_ptr,我);
j=va_arg(arg_ptr, int);
va_end(arg_ptr);
printf("%d %d/n", i, j);
返回;
}
我们可以在头文件: 中声明我们的函数
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
我们看到第一次调用有错误,第二次调用是正确的,第三次调用不管结果
正确,但是和我们函数的最初设计有冲突。在下一节中我们将讨论这些结果
编译器中处理可变参数的原因和方式。
(2)编译器中对可变参数的处理
我们知道va_start、va_arg和va_end在stdarg.h中被定义为宏。
由于1)硬件平台不同和2)编译器不同,定义的宏也不同。下列
以下是VC++中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) )
#定义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。
高位地址|-----------------------------------------|
|函数返回地址|
|--------------------------------|
|. |
|--------------------------------|--ap 指向va_arg
|第n 个参数(第一个可变参数)|
|--------------------------------|--va_start ap 指向
|第n-1 个参数(最后一个固定参数)|
低位地址|----------------------------|-- v
图(2)
最后要说的是va_end宏的含义。 x86平台定义为ap=(char*)0;这样ap 就不再是
指向堆栈,但与NULL相同。有的直接定义为((void*)0),所以编译器不会
会为va_end生成代码,例如Linux的x86平台上gcc就是这样定义的。
这里大家要注意一个问题:由于参数的地址是用于va_start宏的,所以
参数不能声明为寄存器变量、函数或数组类型。
这就是va_start、va_arg 和va_end 的描述。我们应该注意什么
对于不同的操作系统和硬件平台,定义略有不同,但原理相似。
(3) 可变参数编程时应注意的问题
因为va_start、va_arg、va_end等被定义为宏,看起来很愚蠢,
该函数中变量参数的类型和个数完全由程序代码控制,不具有智能性。
确定不同参数的数量和类型。
有人可能会问:printf不是实现了参数的智能识别吗?那是因为函数
printf从固定参数格式字符串中分析出参数类型,然后调用va_arg
来获取可变参数。也就是说,如果想要实现可变参数的智能识别,就必须通过
这是通过在自己的程序中进行判断来实现的。
还有一个问题是编译器对可变参数函数的原型检查不够严格。
格式,这不利于编程错误检查。如果将simple_va_fun()改为:
void simple_va_fun(int i,)
{
va_list arg_ptr;
字符*s=NULL;
va_start(arg_ptr,我);
s=va_arg(arg_ptr, char*);
va_end(arg_ptr);
printf("%d %s/n", i, s);
返回;
}
变量参数的类型为char*。当我们忘记调用带有两个参数的函数时,就会发生这种情况。
核心转储(Unix)或非法页面错误(window 平台)。但它可能不会发生。
错了,但是错误很难发现,这不利于我们编写高质量的程序。
下面提到va系列宏的兼容性。
System V Unix 将va_start 定义为只有一个参数的宏:
va_start(va_list arg_ptr);
ANSI C 定义为:
va_start(va_list arg_ptr, prev_param);
如果我们想使用系统V定义,我们应该使用vararg.h头文件中定义的
宏、ANSI C 宏与系统V 宏不兼容。我们一般使用ANSI C,所以
使用ANSI C的定义就足够了,这也方便了程序的移植。
总结:
可变参数的作用原理其实很简单,va系列就是通过宏定义来定义的。
现在就和栈有关了。当我们写变量函数的C函数时,各有利弊,所以没必要
必要的时候,我们不需要使用可变参数。如果是C++的话,我们应该利用C++的很多
使用有状态来实现可变参数的功能,尽量避免使用C语言来实现。
OK,关于深入解析C语言中的动态参数函数和的内容到此结束了,希望对大家有所帮助。
【深入解析C语言中的动态参数函数】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
这个主题听起来很有意思,我一直想学习了解一下如何使用可变参数在C语言中编写函数。
有5位网友表示赞同!
可变参数函数是不是可以让我的代码更灵活?
有8位网友表示赞同!
学习可变参数可以让我写出更简洁的代码吗?
有9位网友表示赞同!
我想知道可变参数函数有什么实际应用场景?
有20位网友表示赞同!
我需要用到可变参数的时候,应该怎么处理参数的数量和类型?
有8位网友表示赞同!
会遇到什么误区是在写可变参数函数吧?
有9位网友表示赞同!
可变参数的C函数难理解吗?
有6位网友表示赞同!
看了这个标题,觉得可以扩展一些 C 函数的其他技巧。
有8位网友表示赞同!
学习这方面知识肯定对编程水平有提升!
有5位网友表示赞同!
想看一看使用可变参数的代码示例。
有9位网友表示赞同!
我以前也遇到过需要灵活处理不同数量参数的情况,这个主题很有用。
有20位网友表示赞同!
这个概念听起来很新颖,我很想深入了解一下。
有13位网友表示赞同!
C 的可变参数函数是怎么实现的?
有19位网友表示赞同!
是不是可以使用可变参数来处理自定义数据结构?
有20位网友表示赞同!
可以用可变参数写的代码更具扩展性吗?
有15位网友表示赞同!
我想知道学习可变参数需要花多长时间 ?
有10位网友表示赞同!
这个知识点对以后的程序设计很有帮助吧。
有9位网友表示赞同!
我听说过这个概念,但是一直没有深入了解过,现在正好可以学习一下。
有14位网友表示赞同!