以 Null 终止 C 可变参数函数的参数列表

P..*_*... 0 c variadic

我正在摆弄 C 中的可变参数函数来了解它们是如何工作的,并且正在尝试构建一个简单的“打印行”函数,而不需要手动计算行数。我通过将函数包装在一个宏中来实现此目的,该宏将空指针添加到char *参数列表的末尾,以便该函数可以逐行打印,直到找到空参数。

我知道我已经避免了一些常见的陷阱,例如忘记在参数列表中强制转换空指针,但无论出于何种原因,该代码仍然无法正常工作。使用任意数量的参数调用该函数都会正确打印它们,然后无法检测空值,打印一堆垃圾数据,然后崩溃。

int printline(const char *str) {
    printf("%s\n", str);
}

#define printlines(...) _comments(__VA_ARGS__, (char*)0)
int _printlines(char* first, ...) {
    if (first) {
        printline(first);

        va_list ptr;
        va_start(ptr, first);

        char *next;

        do {
            char *next = va_arg(ptr, char *);
            if (next) {
                printline(next);
            }
        } while(next);

        va_end(ptr);
    }
}

int main() {
    printlines("hi");
    //prints 'hi', then prints garbage data and crashes

    printlines("how", "are", "you");
    //prints 'how', 'are', and 'you', then prints garbage data and crashes
    
    _printlines("help", (char *)0);
    //prints 'help', then prints garbage data and crashes

    _printlines("something", "is", "wrong", (char *)NULL);
    //prints 'something', 'is', and 'wrong', then prints garbage data and crashes
}
Run Code Online (Sandbox Code Playgroud)

dbu*_*ush 5

如果你看一下这个:

    char* next;
    do{
        char* next = va_arg(ptr,char*);
        if(next){ comment(next); }
    }while(next);
Run Code Online (Sandbox Code Playgroud)

您将看到有两个名为 的独立变量next,其中do..while循环内部的变量掩盖了外部定义的变量。您将 的结果分配va_arg给内部next. 然后,当您得到while (next)条件时,内部next超出范围,您现在正在读取next从未写入的外部。这会触发未定义的行为

相反,你想要:

    char* next;
    do{
        next = va_arg(ptr,char*);
        if(next){ comment(next); }
    }while(next);
Run Code Online (Sandbox Code Playgroud)

这样您就只有一个名为next您正在使用的变量。