va_end
- 宏重置arg_ptr
。
访问变量参数列表后,arg_ptr
指针通常用 重置va_end()
。我知道如果您想重新迭代列表,它是必需的,但如果您不打算这样做,真的需要它吗?这只是一种好的做法,比如“default:
你的总是有一个”的规则switch
吗?
va_end
- 宏重置arg_ptr
。
访问变量参数列表后,arg_ptr
指针通常用 重置va_end()
。我知道如果您想重新迭代列表,它是必需的,但如果您不打算这样做,真的需要它吗?这只是一种好的做法,比如“default:
你的总是有一个”的规则switch
吗?
va_end
用于进行清理。你不想破坏堆栈,是吗?
来自man va_start
:
va_end()
va_start() 的每次调用都必须与同一函数中相应的 va_end() 调用相匹配。在调用 va_end(ap) 之后,变量 ap 未定义。可以对列表进行多次遍历,每次都由 va_start() 和 va_end() 括起来。va_end() 可以是宏或函数。
注意单词must的存在。
堆栈可能会损坏,因为您不知道va_start()
在做什么。va_*
宏被视为黑盒子。每个平台上的每个编译器都可以在那里为所欲为。它可能什么都不做,也可能做很多事情。
一些 ABI 将前几个参数传递到寄存器中,其余的传递到堆栈中。Ava_arg()
那里可能更复杂。您可以查看给定实现如何执行可变参数,这可能很有趣,但在编写可移植代码时,您应该将它们视为不透明操作。
在 Linux x86-64 上,只能对va_list
变量进行一次遍历。要进行更多遍历,必须首先使用复制它va_copy
。man va_copy
解释细节:
va_copy()
一个明显的实现将有一个 va_list 是指向可变参数函数的堆栈帧的指针。在这样的设置中(迄今为止最常见的),似乎没有什么反对分配
va_list aq = ap;
不幸的是,还有一些系统使它成为一个指针数组(长度为 1),并且需要
va_list aq; *aq = *ap;
最后,在参数在寄存器中传递的系统上,va_start() 可能需要分配内存,将参数存储在那里,并指示下一个参数,以便 va_arg() 可以遍历列表。现在 va_end() 可以再次释放分配的内存。为了适应这种情况,C99 增加了一个宏 va_copy(),这样上面的赋值就可以替换为
va_list aq; va_copy(aq, ap); ... va_end(aq);
va_copy() 的每次调用都必须与同一函数中相应的 va_end() 调用相匹配。一些不提供 va_copy() 的系统改为使用 __va_copy,因为这是提案草案中使用的名称。
在常见的“堆栈上传递的参数”实现中,我相信 va_end() 通常是空/空/空。但是,在传统方案较少的平台上,它变得很有必要。包含它以保持平台中立是一种“良好做法”。