我正在修补一些旧代码(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中的所有内容都具有相同的字长。任何见识将不胜感激。

最佳答案

让我们玩“想象力”。想象一下这段代码:

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显然不像我编码的那样工作,但是这些应该有助于阐明一般概念。

09-09 20:10