I'm messing around with variadic functions in C to learn how they work, and am trying to build a simple 'print lines' function without requiring manual counting of the lines. I'm doing this by wrapping the function in a macro that adds a null pointer to the end of a list of char *
arguments, so the function can print line-by-line until a null argument is found.
I know I've avoided some common pitfalls, like forgetting to cast the null pointer in the argument list, but for whatever reason this code still isn't working. Calling the function with any number of parameters prints them properly, then fails to detect the null, prints a bunch of garbage data, and crashes.
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
}
If you take a look at this:
char* next;
do{
char* next = va_arg(ptr,char*);
if(next){ comment(next); }
}while(next);
You'll see that you have two separate variables called next
, with the one inside of the do..while
loop masking the one defined outside. You're assigning the result of va_arg
to the inner next
. Then when you get the while (next)
condition, the inner next
is out of scope and you're now reading the outer next
which was never written to. This triggers undefined behavior.
You instead want:
char* next;
do{
next = va_arg(ptr,char*);
if(next){ comment(next); }
}while(next);
So that you only have a single variable called next
that you're using.