va_end
- 重置arg_ptr
的宏。
访问变量参数列表后,arg_ptr
指针通常会被va_end()
重置。我知道如果你想重新迭代这个列表是必需的,但如果你不去,它是否真的需要?这只是一种很好的做法,就像规则“default:
中总是有switch
一样吗?”
答案 0 :(得分:45)
va_end
用于进行清理。你不想粉碎堆栈,是吗?
来自man va_start
:
va_end用来()
每次调用va_start()都必须与同一函数中相应的va_end()调用相匹配。在调用va_end(ap)之后,变量ap未定义。列表的多次遍历,每个遍历由va_start()和va_end()括起来都是可能的。 va_end()可以是宏或函数。
请注意一词必须。
堆栈可能会损坏,因为您不知道va_start()
正在做什么。 va_*
宏意味着被视为黑盒子。每个平台上的每个编译器都可以做任何想做的事情。它可能什么都不做,或者可能做很多事情。
有些ABI在寄存器中传递前几个args,而其余的在堆栈中传递。 va_arg()
可能会更复杂。您可以查看给定实现如何执行varargs,这可能很有趣,但在编写可移植代码时,您应该将它们视为不透明操作。
答案 1 :(得分:13)
在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()都必须与相应的invoca-匹配 在同一个函数中的va_end()。一些不供应的系统 va_copy()改为__va_copy,因为那是在中使用的名称 提案草案。
答案 2 :(得分:12)
在常见的“在堆栈上传递的参数”实现中,我相信va_end()通常没有/ empty / null。但是,在传统方案较少的平台上,这是必要的。包含它以保持平台中立是一种“良好做法”。