7

Consider the following code snippet:

#include <stdio.h>
#include <stdarg.h>

void display(int num, ...) {
    char c;
    int j;
    va_list ptr;
    va_start(ptr,num);
    for (j= 1; j <= num; j++){
        c = va_arg(ptr, char);
        printf("%c", c);

    }
    va_end(ptr);
}

int main() {
    display(4, 'A', 'a', 'b', 'c');
    return 0;
}

The program gives runtime error because vararg automatically promotes char to int, and i should have used int in this case.

What are all types are permitted when I use vararg, how to know which type to use and avoid such runtime errors.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 2
    Hehe, GCC says, "warning: ‘char’ is promoted to ‘int’ when passed through ‘...’". Nice. – Kerrek SB Aug 16 '11 at 20:55
  • 2
    In `display(4, 'A', 'a', 'b', 'c');` there are 5 `int`s; not 1 `int` and 4 `char`s as you seem to believe :) – pmg Aug 16 '11 at 20:57
  • 1
    @Kerrek: it says that for the expression `va_arg(ptr, char)`, not the call to the `display` function -- where there are no `char`s :) – pmg Aug 16 '11 at 21:02
  • 2
    @pmg: Ah, indeed -- that's one of the changes from C to C++: in C char literals are integers... I never knew you couldn't use chars variadically. `wchar_t` also doesn't work, and neither do `char16_t` or `char32_t` (they're all promoted to `unsigned int`). – Kerrek SB Aug 16 '11 at 21:05
  • This probably depends on the compiler / machine word length. You could even redefine va_arg to take account of this. – QuentinUK Aug 16 '11 at 21:09
  • @Kerrek - What is this `char16_t` you speak of? – Chris Lutz Aug 16 '11 at 21:42
  • @Chris: A new type in C++11, and probably in C1x. Good for UTF16 strings :-) (E.g. [read here](http://stackoverflow.com/questions/6796157/unicode-encoding-for-string-literals-in-c0x).) – Kerrek SB Aug 16 '11 at 21:44
  • @Kerrek - They are being added to C1x. Good to know. – Chris Lutz Aug 16 '11 at 22:04
  • @QuentinUK - How could you (portably) redefine `va_arg`? And how would it take the standard promotions into account? – Chris Lutz Aug 16 '11 at 22:06

3 Answers3

10

another case that the others forgot to mention are pointer types, critical is NULL in particular. Since this could expand to 0 or (void*)0 (or some other weird things) you will not know if the compiler puts an int or a void* in the list. Since these can have different width, this can lead to annoying bugs.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    Yep, that's always a subtle source of bugs. GCC is nice and expands `NULL` to `__nullptr` instead of `0`, so you won't run into that bug with GCC, but other compilers are not so nice. The C standard only requires that `NULL` expand to an "implementation-defined null pointer constant", which is "an integer constant expression with the value 0, or such an expression cast to type `void*`. – Adam Rosenfield Aug 16 '11 at 23:02
5

You can use any standard type with va_arg except char, signed char, unsigned char, short, unsigned short, _Bool, and float. It's possible that an implementation defines additional nonstandard types that also have integer conversion rank lower than int, or likewise nonstandard small floating-point types, but you would not need to be aware of these unless you intend to use them, so for practical purposes the list I gave is complete.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
1

While using va_arg the char is promoted to int. There are other types(the list given by @R..) which are promoted.

so in order to read it as a char you have to do typecast.

char c = (char) va_arg(ap, int);

For a complete list, please see the 'Default conversions' section in:

http://en.cppreference.com/w/cpp/language/variadic_arguments

A. K.
  • 34,395
  • 15
  • 52
  • 89