11.2.2 C语言标准库(2)
'
下面让我们来看va_list等宏应该如何实现。
va_list实际是一个指针,用来指向各个不定参数。由于类型不明,因此这个va_list以void*或char*为最佳选择。
va_start将va_list定义的指针指向函数的最后一个参数后面的位置,这个位置就是第一个不定参数。
va_arg获取当前不定参数的值,并根据当前不定参数的大小将指针移向下一个参数。
va_end将指针清0。
按照以上思路,va系列宏的一个最简单的实现就可以得到了,如下所示:
#define va_list char* #define va_start(ap,arg) (ap=(va_list)&arg+sizeof(arg)) #define va_arg(ap,t) (*(t*)((ap+=sizeof(t))-sizeof(t))) #define va_end(ap) (ap=(va_list)0) |
【小提示】
变长参数宏
在很多时候我们希望在定义宏的时候也能够像print一样可以使用变长参数,即宏的参数可以是任意个,这个功能可以由编译器的变长参数宏实现。在GCC编译器下,变长参数宏可以使用"##"宏字符串连接操作实现,比如:
#define printf(args…) fprintf(stdout, ##args) |
那么printf("%d %s", 123, "hello")就会被展开成:
fprintf(stdout, "%d %s", 123, "hello") |
而在MSVC下,我们可以使用__VA_ARGS__这个编译器内置宏,比如:
#define printf(…) fprintf(stdout,__VA_ARGS__) |
它的效果与前面的GCC下使用##的效果一样。
2. 非局部跳转
非局部跳转即使在C语言里也是一个备受争议的机制。使用非局部跳转,可以实现从一个函数体内向另一个事先登记过的函数体内跳转,而不用担心堆栈混乱。下面让我们来看一个示例:
#include <setjmp.h> #include <stdio.h> jmp_buf b; void f() { longjmp(b, 1); } int main() { if (setjmp(b)) printf("World!"); else { printf("Hello "); f(); } } |
这段代码按常理不论setjmp返回什么,也只会打印出"Hello "和"World!"之一,然而事实上的输出是:
实际上,当setjmp正常返回的时候,会返回0,因此会打印出"Hello "的字样。而longjmp的作用,就是让程序的执行流回到当初setjmp返回的时刻,并且返回由longjmp指定的返回值(longjmp的参数2),也就是1,自然接着会打印出"World!"并退出。换句话说,longjmp可以让程序"时光倒流"回setjmp返回的时刻,并改变其行为,以至于改变了未来。
是的,这绝对不是结构化编程(www.cppentry.com)。
【责任编辑:
云霞 TEL:(010)68476606】