快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

和记娱乐备用:深入浅出VA函数的使用技巧



本文主要先容可变参数的函数应用,然后阐发它的道理,法度榜样员自己若何对它们实现和封装,着末是可能会呈现的问题和避免步伐。

VA函数(variable argument function),参数个数可变函数,又称可变参数函数。C/C++编程中,系统供给给编程职员的va函数很少。*printf()/*scanf()系列函数,用于输入输出时款式化字符串;exec*()系列函数,用于在法度榜样中履行外部文件(main(int argc,char*argv[]算不算呢,与其说main()也是一个可变参数函数,倒不如说它是exec*()颠末封装后的具备特殊功能和意义的函数,至少在道理这一级上有很多相似之处)。因为参数个数的不确定,使va函数具有很大年夜的机动性,易用性,对没有应用过可变参数函数的编程职员很有诱惑力;那么,该若何编写自己的va函数,va函数的运用机会、编译实现又是若何。作者借本文谈谈自己关于va函数的一些穴见。

一、 从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:款式化到一个文件流,可用于日志文件

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;

}

// 调用时,与应用printf()没有差别。

WriteLog("%04d-%02d-%02d %02d:%02d:%02d %s/%04d logged out.",

nYear, nMonth, nDay, nHour, nMinute, szUserName, nUserID);

同理,也可以从文件中履行款式化输入;或者对标准输入输出,字符串履行款式化。

在上面的例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:求随意率性个自然数的平方和:

int SqSum(int n1, ...)

{

va_list arg_ptr;

int nSqSum = 0, n = n1;

va_start(arg_p和记娱乐备用tr, n1);

while (n > 0)

{

nSqSum += (n * n);

n = va_arg(arg_ptr, int);

}

va_end(arg_ptr);

return nSqSum;

}

// 调用时

int nSqSum = S和记娱乐备用qSum(7, 2, 7, 11, -1);

可变参数函数的原型声明款式为:

type VAFunction(type arg1, type arg2, … );

参数可以分为两部分:个数确定的固定参数和个数可变的可选参数。函数至少必要一个固定参数,固定参数的声明和通俗函数一样;可选参数因为个数不确定,声明时用"…"表示。固定参数和可选参数公同构成一个函数的参数列表。

这里,移动指针使其指向下一个参数,那么移动指针时的偏移量是若干呢,没有详细谜底,由于这里涉及到内存对齐(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)

// 中心某个固定参数的地址 + 该参数之后所有固定参数占用的内存大年夜小之和

回顾到printf/scanf系列函数的%d %s之类的款式化指令,我们不难理解这些它们的用途了- 昭示参数强制转换的类型。

(注:printf/scanf没有应用va_xxx来实现,但道理是同等的。)

3.va_end很简单,仅仅是把指针作废而已。

#define va_end(ap) (ap = (va_list)0) // x86平台

四、 简洁、机动,也有危险

从va的实现可以看出,指针的合理运用,把C说话简洁、机动的特点体现得淋漓尽致,叫人不得不佩服C的强大年夜和高效。弗成否认的是,给编程职员太多自由空间一定使法度榜样的安然性低落。va中,为了获得所有通报给函数的参数,必要用va_arg依次遍历。此中存在两个隐患:

1)若何确定参数的类型。

va_arg在类型反省和记娱乐备用方面与其说异常机动,不如说是很不认真,由于是强制类型转换,va_arg都把当前指针所指向的内容强制转换到指定类型;

2)停止标志。假如没有停止标志的判断,va将按默认和记娱乐备用类型依次返回内存中的内容,直到造访到不法内存而掉足退出。例2中SqSum()求的是自然数的平方和,以是我把负数和0作为它的停止标志。例如scanf把接管到的回车符作为停止标志,大年夜家熟知的printf()对字符串的处置惩罚用'\0'作为停止标志,无法想象C中的字符串假如没有'\0', 代码将会是如何一番情景,预计那时最盛行的可能是字符数组,或者是malloc/free。

容许对内存的随意造访,会留给不怀美意者留下进击的可能。当处置惩罚cracker精心设计好的一串字符串后,法度榜样将跳转到一些恶意代码区域履行,以使cracker达到其进击目的。(常见的exploit进击)以是,必需禁止对内存的随意造访和严格节制内存造访界限。

五、 Unix System V兼容要领的va声明

上面先容可变参数函数的声明是采纳ANSI标准的,Unix System V兼容要领的声明有一点点差别,它增添了两个宏:va_alist,va_dcl。而且它们不是定义在stdarg.h中,而是varargs.h中。stdarg.h是ANSI标准的;varargs.h仅仅是为了能与曩昔的法度榜样维持兼容而呈现的,现在的编程中不保举应用。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: