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,这可能很有趣,但在编写可移植代码时,您应该将它们视为不透明操作.
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,这可能很有趣,但在编写可移植代码时,您应该将它们视为不透明操作.
在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()通常没有/ empty/null.但是,在传统方案较少的平台上,这是必要的.包含它以保持平台中立是一种"好习惯".