来自省略号的 va_list 如何与 vfprintf() 调用交互?
How does a va_list from an ellipses interact with a vfprintf() call?
我正在修补一些旧代码(15-20 岁),我时常遇到奇怪的片段。这是一个让我摸不着头脑的人。
27 void FormDefFileScanner::FormDefFileerror(char *fmt, ...)
28 {
29 va_list va;
30 va_start(va, fmt);
31 /* This is related to some sort of debuging */
32 if (FormDefFilelineno)
33 fprintf(stderr, "%d: ", FormDefFilelineno);
34 /* This is where I'm unsure */
35 (void) vfprintf(stderr, fmt, va);
36 fputc('\n', stderr);
37 va_end(va);
... /* rest of the program */
... }
我从对“...”论证的研究中了解到 va_list 应该如何运作。我的意思是,需要使用列表和变量类型调用 va_arg() 才能从 va_list 中正确提取值。我想我想知道 vprintf() 调用如何正确解析 va_list。我假设格式字符串有帮助,但我不确定 va_list 中的所有内容都具有相同的字长。任何见解将不胜感激。
让我们玩"Imagination"。想象一下这段代码:
typedef char* va_list;
#define va_start(va_bytes, arg) (va_bytes=reinterpret_cast<char*>((&arg)+1))
#define va_end(va_bytes)
#define va_arg(va_bytes,type) (*reinterpret_cast<type*>((va_bytes+=sizeof(type))-sizeof(type)))
所以你的代码变成这样:
void FormDefFileScanner::FormDefFileerror(char *fmt, ...)
{
char* va_bytes;
va_bytes = reinterpret_cast<char*>((&fmt)+1); //points at first byte of ...
vfprintf(stderr, fmt, va_bytes); //passes a pointer to the bytes to vfprintf.
那么vprintf可以这样做:
void vfprintf(FILE*, char* format, char* va_bytes)
{
if (strcmp(format,"%d")==0) { //now we know the first param is an int
//I'm splitting the macro into two lines here for clarity
int value = *reinterpret_cast<int*>(va_bytes);
va_bytes += sizeof(int); //va_bytes now points at the second parameter
} else if (strcmp(format,"%llu")==0) { //first param is an long long unsigned int
//I'm splitting the macro into two lines here for clarity
long long unsigned value = *reinterpret_cast<long long unsigned*>(va_bytes);
va_bytes += sizeof(long long unsigned); //va_bytes now points at the second parameter
}
在任何时候,va_bytes 都指向下一个参数的开始。当给定一个 va_arg
时,它将这些字节转换为该类型,并将指针前进到紧随其后的位置,即 后续 参数的开始。在您通过 va_arg
告诉它类型之前它无法前进,因为它不知道类型,因此它不知道每个参数中有多少字节。
真正的 va_arg
宏要复杂得多,因为它处理类型对齐等,而且 vfprintf
显然 与我编写的代码完全不同,但这些应该有助于澄清一般概念。
I guess I'm wondering how a vprintf()
call can correctly parse a
va_list
. I assume the format string helps, but I'm not sure that every
thing in va_list
has the same word size.
va_list
中的东西不 大小相同。 vprintf
不必手动 "parse a va_list
" -- 它可以只使用 va_arg
。由于格式字符串中的格式说明符,它知道下一个参数需要是什么类型。所以你可以想象,如果你要写 vprintf
,在解析格式字符串的循环中,你会有每个格式说明符的案例,然后在每个案例中他们会使用 va_arg
来获得下一个正确类型的参数:
if (strcmp(format,"%d")==0) {
int value = va_arg(va, int);
// do stuff ...
} else if (strcmp(format,"%f")==0) {
double value = va_arg(va, double);
// do stuff ...
}
//...
至于va_list
、va_arg
等是如何实现的,那是不透明的东西,你不应该关心,因为它因架构和系统而异。你只需要知道,如果你遵循 va_start
、va_arg
等契约,它就有效。
我正在修补一些旧代码(15-20 岁),我时常遇到奇怪的片段。这是一个让我摸不着头脑的人。
27 void FormDefFileScanner::FormDefFileerror(char *fmt, ...)
28 {
29 va_list va;
30 va_start(va, fmt);
31 /* This is related to some sort of debuging */
32 if (FormDefFilelineno)
33 fprintf(stderr, "%d: ", FormDefFilelineno);
34 /* This is where I'm unsure */
35 (void) vfprintf(stderr, fmt, va);
36 fputc('\n', stderr);
37 va_end(va);
... /* rest of the program */
... }
我从对“...”论证的研究中了解到 va_list 应该如何运作。我的意思是,需要使用列表和变量类型调用 va_arg() 才能从 va_list 中正确提取值。我想我想知道 vprintf() 调用如何正确解析 va_list。我假设格式字符串有帮助,但我不确定 va_list 中的所有内容都具有相同的字长。任何见解将不胜感激。
让我们玩"Imagination"。想象一下这段代码:
typedef char* va_list;
#define va_start(va_bytes, arg) (va_bytes=reinterpret_cast<char*>((&arg)+1))
#define va_end(va_bytes)
#define va_arg(va_bytes,type) (*reinterpret_cast<type*>((va_bytes+=sizeof(type))-sizeof(type)))
所以你的代码变成这样:
void FormDefFileScanner::FormDefFileerror(char *fmt, ...)
{
char* va_bytes;
va_bytes = reinterpret_cast<char*>((&fmt)+1); //points at first byte of ...
vfprintf(stderr, fmt, va_bytes); //passes a pointer to the bytes to vfprintf.
那么vprintf可以这样做:
void vfprintf(FILE*, char* format, char* va_bytes)
{
if (strcmp(format,"%d")==0) { //now we know the first param is an int
//I'm splitting the macro into two lines here for clarity
int value = *reinterpret_cast<int*>(va_bytes);
va_bytes += sizeof(int); //va_bytes now points at the second parameter
} else if (strcmp(format,"%llu")==0) { //first param is an long long unsigned int
//I'm splitting the macro into two lines here for clarity
long long unsigned value = *reinterpret_cast<long long unsigned*>(va_bytes);
va_bytes += sizeof(long long unsigned); //va_bytes now points at the second parameter
}
在任何时候,va_bytes 都指向下一个参数的开始。当给定一个 va_arg
时,它将这些字节转换为该类型,并将指针前进到紧随其后的位置,即 后续 参数的开始。在您通过 va_arg
告诉它类型之前它无法前进,因为它不知道类型,因此它不知道每个参数中有多少字节。
真正的 va_arg
宏要复杂得多,因为它处理类型对齐等,而且 vfprintf
显然 与我编写的代码完全不同,但这些应该有助于澄清一般概念。
I guess I'm wondering how a
vprintf()
call can correctly parse ava_list
. I assume the format string helps, but I'm not sure that every thing inva_list
has the same word size.
va_list
中的东西不 大小相同。 vprintf
不必手动 "parse a va_list
" -- 它可以只使用 va_arg
。由于格式字符串中的格式说明符,它知道下一个参数需要是什么类型。所以你可以想象,如果你要写 vprintf
,在解析格式字符串的循环中,你会有每个格式说明符的案例,然后在每个案例中他们会使用 va_arg
来获得下一个正确类型的参数:
if (strcmp(format,"%d")==0) {
int value = va_arg(va, int);
// do stuff ...
} else if (strcmp(format,"%f")==0) {
double value = va_arg(va, double);
// do stuff ...
}
//...
至于va_list
、va_arg
等是如何实现的,那是不透明的东西,你不应该关心,因为它因架构和系统而异。你只需要知道,如果你遵循 va_start
、va_arg
等契约,它就有效。