设为首页 加入收藏

TOP

11.2.2 C语言标准库(2)
2013-10-07 00:45:15 来源: 作者: 【 】 浏览:65
Tags:11.2.2 语言 标准

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!"之一,然而事实上的输出是:

Hello World!

实际上,当setjmp正常返回的时候,会返回0,因此会打印出"Hello "的字样。而longjmp的作用,就是让程序的执行流回到当初setjmp返回的时刻,并且返回由longjmp指定的返回值(longjmp的参数2),也就是1,自然接着会打印出"World!"并退出。换句话说,longjmp可以让程序"时光倒流"回setjmp返回的时刻,并改变其行为,以至于改变了未来。

是的,这绝对不是结构化编程(www.cppentry.com)。

【责任编辑:云霞 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇5.4.2 代码演练(1) 下一篇11.2.2 C语言标准库(1)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: