0

Here is my code snippet: [Full Code]

void Draw_Square (HDC DeviceContext, int Cnt, ...)
{
    int Cnt_1, Cnt_2, Cnt_3, Cnt_4;
    for (Cnt_1 = 0; Cnt_1 < 4; Cnt_1++)
    {
        for (Cnt_2 = 0; Cnt_2 < 6; Cnt_2++)
        {
            va_list Ap;
            va_start (Ap, (Cnt*4));
            for (Cnt_3 = 0; Cnt_3 < Cnt; Cnt++)
                Draw_Line (DeviceContext, (Cnt_1*6+Cnt_2), va_arg (Ap, short), va_arg (Ap, short), va_arg (Ap, int), va_arg (Ap, COLORREF));
            va_end (Ap);
        }
        Sleep (User_Def_Delay);
    }
}

GCC/mingw32 reported:

|   |In function 'Draw_Square':                                                              |
|393|warning: second parameter of 'va_start' not last named argument [enabled by default]    |
|395|warning: 'short int' is promoted to 'int' when passed through '...' [enabled by default]|
|395|note: (so you should pass 'int' not 'short int' to 'va_arg')                            |
|395|note: if this code is reached, the program will abort                                   |
|395|warning: 'short int' is promoted to 'int' when passed through '...' [enabled by default]|
|395|note: if this code is reached, the program will abort                                   |

Why? wrong type?

Thanks.

Added:

I've modified my code, but it is still failed.

void Draw_Square (HDC DeviceContext, int Cnt, ...)
{
 va_list Ap;
 va_start (Ap, Cnt);
 int Cnt_1, Cnt_2, Cnt_3;
 for (Cnt_1 = 0; Cnt_1 < 4; Cnt_1++)
 {
  for (Cnt_2 = 0; Cnt_2 < 6; Cnt_2++)
  {
   for (Cnt_3 = 0; Cnt_3 < Cnt; Cnt++)
    Draw_Line (DeviceContext, (Cnt_1*6+Cnt_2), (short)va_arg (Ap, int), (short)va_arg (Ap, int), va_arg (Ap, int), va_arg (Ap, COLORREF));
  }
  Sleep (User_Def_Delay);
 }
 va_end (Ap);
}

and get no warnings

Kevin Dong
  • 5,001
  • 9
  • 29
  • 62

2 Answers2

4

Read the manual. You want something like this:

void Draw_Square (HDC DeviceContext, int Cnt, ...)
{
    va_list Ap;
    va_start(Ap, Cnt);

    // ... loop can use "va_arg" repeatedly

    va_end(Ap);
}

Generally, it doesn't make sense to pass mandatory arguments like your Cnt_1, ..., Cnt_4 as variable arguments. Just make then normal arguments. There are sometimes situations where variable ar­gu­ments make sense for fixed arguments (e.g. when you implement the Linux system call interface), but that's rare, error prone and more of a last-resort than good design. But if you must, you can always do it:

    // prologue as above

    short Cnt_1 = va_args(Ap, int);
    short Cnt_2 = va_args(Ap, int);
    short Cnt_3 = va_args(Ap, int);
    short Cnt_4 = va_args(Ap, int);

    // loop

    // epilogue

Furthermore, you cannot pass integers as anything shorter than int as variable arguments, since they will always get standard-promoted to int or unsigned int. So va_args(Ap, short) is never correct, and it should be va_args(Ap, int): The conversion short to int to short is well defined and preserves the original value.

To stress that last point: If you were to pass an unsigned short, you would need to va-cast to unsigned int:

unsigned short n = va_args(Ap, unsigned int);

This would be fine no matter whether on your platform the unsigned short is default-promoted to int or unsigned int (both are possible; this is platform dependent): As the linked documentation says, this is well-defined as long as both versions are sign-variants of one another and the value in question is representable by both types. This is indeed the case: If the unsigned short is promoted to int, then its value must automatically also be representable by an unsigned int, since unsigned types can represent all the non-negative values of their signed counterparts.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1
  1. The order of argument-evaluation is undefined so your multiple calls to va_arg in the argument-list to Draw_Line can happen in any order, that is probably a (if not the) problem.
  2. The second parameter to va_start must be Cnt (the last parameter to your function), it can't be Count as you have. This is what your compiler is warning you about.

    Note that the value of that parameter does not matter, va_start needs it to know where to start processing the variable-arguments.

As Kerrek SB mentioned the way you're using variable-arguments here isn't the way they're supposed to be used. Since the arguments are mandatory anyway you may as well give them named parameters in you Draw_Line function. It is also even dangerous: if there are less than 4 of the variable-arguments provided the calls to va_arg will cause undefined behaviour. That won't happen if the parameters are named parameters since the compiler will raise an insufficient-number-of-arguments error.

ADDIT: You seem to be under the assumption that the varargs macros & functions keep track of how many variable-arguments there are / how many they expect. They don't: you need to keep track of them. Which is why your calls to va_arg can cause undefined behaviour if there are less variable-arguments than you expect.

va_start needs the last named (or last parameter with a known type) parameter so that it knows where to start extracting the variable-arguments but it doesn't need to know how many there are.

See also C99 §7.15.1.4 p 4 (va_start):

The parameter parmN is the identifier of the rightmost parameter in the variable parameter list in the function definition (the one just before the , ...). If the parameter parmN is declared with the register storage class, with a function or array type, or with a type that is not compatible with the type that results after application of the default argument promotions, the behavior is undefined.

and §7.15.1.1 p 2 (va_arg):

[...] If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, [...]

Kninnug
  • 7,992
  • 1
  • 30
  • 42
  • Can't it be `va_start (Ap, (Cnt*4));` ? – Kevin Dong Dec 07 '13 at 13:43
  • No, it must be just the last named parameter. But why would you want it to be `Cnt*4`? That makes no sense, `va_start` doesn't use the value of that parameter, just its name. – Kninnug Dec 07 '13 at 13:46
  • the second parameter means the amount of arguments it'll get, doesn't it? – Kevin Dong Dec 07 '13 at 13:49
  • No, it is just the last named parameter of the function, regardless of what that parameter means. The varargs macros & functions don't keep track of how many variable-arguments there are or how many they expect. That is up to you. Which is why your calls to `va_arg` can cause undefined behaviour if there are less variable-arguments than you expect. – Kninnug Dec 07 '13 at 13:53
  • I think the second parameter means the amount of argument it'll get. (Maybe I am wrong.) [Here is the example on GNU Website](http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Variadic-Example.html#Variadic-Example). It passes 3 and 10 to `add_em_up()`! – Kevin Dong Dec 07 '13 at 14:04
  • An understandable mistake, it often is the number of variable-arguments as the function does need to know that. But look for example at [`printf`](http://stackoverflow.com/a/4867288/249237) here the last named parameter is a `const char *`, definitely not a number, but it is passed to `va_start` directly. (`v`)`printf` then extracts the number of arguments it should be getting from the format. But `va_start` doesn't concern itself with this. – Kninnug Dec 07 '13 at 14:10
  • If so, is the second parameter meaningless? – Kevin Dong Dec 07 '13 at 14:14
  • I've solved this problem. Here is my [solution](https://github.com/kevin-dong-nai-jia/Console-Snake/blob/master/Console-Snake%20v1.9.0.c#L396-L400) Appreciate your help :-) – Kevin Dong Dec 07 '13 at 16:30