65

va_end- 宏重置arg_ptr

访问变量参数列表后,arg_ptr指针通常用 重置va_end()。我知道如果您想重新迭代列表,它是必需的,但如果您不打算这样做,真的需要它吗?这只是一种好的做法,比如“default:你的总是有一个”的规则switch吗?

4

3 回答 3

55

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()那里可能更复杂。您可以查看给定实现如何执行可变参数,这可能很有趣,但在编写可移植代码时,您应该将它们视为不透明操作。

于 2009-02-25T18:03:54.060 回答
15

在 Linux x86-64 上,只能对va_list变量进行一次遍历。要进行更多遍历,必须首先使用复制它va_copyman 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,因为这是提案草案中使用的名称。

于 2012-04-03T21:00:46.780 回答
13

在常见的“堆栈上传递的参数”实现中,我相信 va_end() 通常是空/空/空。但是,在传统方案较少的平台上,它变得很有必要。包含它以保持平台中立是一种“良好做法”。

于 2009-02-25T18:08:52.223 回答