深入解析C语言中的动态参数函数

更新:11-16 民间故事 我要投稿 纠错 投诉

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语言中编写函数。

    有5位网友表示赞同!

颜洛殇

可变参数函数是不是可以让我的代码更灵活?

    有8位网友表示赞同!

花海

学习可变参数可以让我写出更简洁的代码吗?

    有9位网友表示赞同!

不浪漫罪名

我想知道可变参数函数有什么实际应用场景?

    有20位网友表示赞同!

丢了爱情i

我需要用到可变参数的时候,应该怎么处理参数的数量和类型?

    有8位网友表示赞同!

无关风月

会遇到什么误区是在写可变参数函数吧?

    有9位网友表示赞同!

歆久

可变参数的C函数难理解吗?

    有6位网友表示赞同!

屌国女农

看了这个标题,觉得可以扩展一些 C 函数的其他技巧。

    有8位网友表示赞同!

自繩自縛

学习这方面知识肯定对编程水平有提升!

    有5位网友表示赞同!

花开丶若相惜

想看一看使用可变参数的代码示例。

    有9位网友表示赞同!

聽風

我以前也遇到过需要灵活处理不同数量参数的情况,这个主题很有用。

    有20位网友表示赞同!

陌上花

这个概念听起来很新颖,我很想深入了解一下。

    有13位网友表示赞同!

∞◆暯小萱◆

C 的可变参数函数是怎么实现的?

    有19位网友表示赞同!

七夏i

是不是可以使用可变参数来处理自定义数据结构?

    有20位网友表示赞同!

夜晟洛

可以用可变参数写的代码更具扩展性吗?

    有15位网友表示赞同!

拥抱

我想知道学习可变参数需要花多长时间 ?

    有10位网友表示赞同!

*巴黎铁塔

这个知识点对以后的程序设计很有帮助吧。

    有9位网友表示赞同!

隔壁阿不都

我听说过这个概念,但是一直没有深入了解过,现在正好可以学习一下。

    有14位网友表示赞同!

【深入解析C语言中的动态参数函数】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:揭秘潮牌复刻市场:十大知名复刻工厂货源一览 下一篇:麦吉丽祛斑产品评测:哪款祛斑效果更佳?