0

I have the following example code

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
 
void myprint(char *inizio, ...);
int main(void)
{
myprint("Inizio", 2, 3.0, 4, 5.0, 'c');
return(0);
}

void myprint(char *inizio, ...)
{
va_list argPoint;
int pi;
char pc;
double pd;

va_start(argPoint, inizio);

pi=va_arg(argPoint,int);
printf("Int = %d\n", pi);
pi=va_arg(argPoint,int);
printf("Int = %d\n", pi);
pd=va_arg(argPoint,double);
printf("Int = %f\n", pd);

/* need a cast here since va_arg only takes fully promoted types
pc=va_arg(argPoint,char); */

pc=(char)va_arg(argPoint,int);

printf("Int = %c\n", pc);
pd=va_arg(argPoint,double);
printf("Int = %f\n", pd);
pi=va_arg(argPoint,int);
printf("Int = %d\n", pi);
va_start(argPoint, inizio);
pi=va_arg(argPoint,int);
printf("Int = %d\n", pi);

va_end(argPoint);
}

I am getting the following output:

Int = 2
Int = 0
Int = 0.000000
Int =
Int = 0.000000
Int = 117
Int = 2

The answer according to my handout should be:

Int = 2
Int = 4
Int = 3.000000
Int = c
Int = 5.000000
Int = 1235099264
Int = 2

Why is it not working? I am using Codeblocks by the way, just in case.

Additionally what does the comment " need a cast here since va_arg only takes fully promoted types pc=va_arg(argPoint,char); " means? va_arg(argPoint,char) is supposed to return a char, I don't see why they do pc=(char)va_arg(argPoint,int) instead, the (char) seems redundant.

some_math_guy
  • 333
  • 1
  • 8
  • `3.0` is not an `int`. It seems you have some misalignment of args and types. – kaylum Feb 22 '21 at 02:12
  • 2
    You call it with `int, double, int, double, int` but the function reads `int, int, double, int, double, int`. – dxiv Feb 22 '21 at 02:13
  • @kaylum ignoring the "Int=" text , which I guess they forget to edit, the rest of the code does consider it as a double – some_math_guy Feb 22 '21 at 02:14
  • 1
    When you pass a char to a function that takes variadic arguments the parameter is changed to an int. So when you read it with va_arg you need to read an int and convert it back to a char. – Jerry Jeremiah Feb 22 '21 at 02:15
  • Not referring to the `printf`. This: `va_arg(argPoint,int);` You have incorrect types in the `va_arg` calls. – kaylum Feb 22 '21 at 02:16
  • The last va_arg before the second va_start is "supposed" to be 117 but yours is a huge number because that many ints are not passed so the value returned is complete rubbish. You can only read the same number of items as you pass. – Jerry Jeremiah Feb 22 '21 at 02:20
  • @Jerry Jeremiah According to my notes each time va_arg(argPoint,int) is called, the next integer is returned, so it should jump over the non-integer arguments. – some_math_guy Feb 22 '21 at 02:22
  • 2
    The textbook is saying that you can pass the arguments in whatever order you want and then you can read all the ints first and all the doubles later (or vice versa) and that seems to work sometimes. But I don't know if it works for all compilers every time - I have only ever read them in the order they are passed. But it does definitely work sometimes - see: https://onlinegdb.com/LusoXHadB – Jerry Jeremiah Feb 22 '21 at 02:25
  • @Jerry Jeremiah Yes exactly the book assumes that it's like having three independent indices, one that goes forward every time I call va_arg(argPoint,int); another one that goes forward when va_arg(argPoint,double) is called; and another one for va_arg(argPoint,char) calls – some_math_guy Feb 22 '21 at 02:28
  • https://en.cppreference.com/w/cpp/utility/variadic/va_arg says "The va_arg macro expands to an expression of type T that corresponds to the next parameter from the va_list ap. ... If the type of the next argument in ap (after promotions) is not compatible with T, the behavior is undefined,..." So I think it just happens to work for you accidentally. It also says "If va_arg is called when there are no more arguments in ap, the behavior is undefined." (the Int = 117 case) I think that's what @kaylum was trying to say. – Jerry Jeremiah Feb 22 '21 at 02:29
  • @kaylum are you saying the calls are incorrect because you're assuming va_arg calls should be made in order ? Or because of what Jerry Jeremiah commented? – some_math_guy Feb 22 '21 at 02:34
  • 1
    I'm not assuming. I'm reading the [manual](https://linux.die.net/man/3/va_arg) (and from experience): "The va_arg() macro expands to an expression that has the type and value of the **next argument** in the call. If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), **random errors will occur**". I think you are misunderstanding the text book or it is wrong. – kaylum Feb 22 '21 at 02:35
  • @JerryJeremiah I think it just happens to work for you accidentally." What are you refferig to? The 117? I thought it was just garbage, but I actually get the same value every time The compiler in onlinegdb.com/LusoXHadB does accept inputs in any order as my books suggest, right? As opposed to my gcc compiler – some_math_guy Feb 22 '21 at 02:41
  • You can't infer from one "working" case that the code is correct. Especially since you now have an example where it doesn't work. In C this is called Undefined Behaviour. Code which has UB can in some cases appear to "work" but can fail at any time. [Undefined, unspecified and implementation-defined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – kaylum Feb 22 '21 at 02:44
  • @kaylum So the fix of the previous code, would be making calls in order, so that the type is always the correct one? – some_math_guy Feb 22 '21 at 02:47
  • Yes. Just try it. – kaylum Feb 22 '21 at 02:48
  • 2
    Any time something says that it is "undefined behaviour" (like asking for a double when the next parameter was int or asking for an int where there aren't any more parameters) the compiler is allowed to do ANYTHING it wants. It could crash (that's a good outcome) or it could do exactly what you expect (that's bad because a different compiler won't do the same thing) or it could give you random values or if you are on a computer without virtual memory it could reboot your computer or reformat your hard drive. https://randomascii.wordpress.com/2014/05/19/undefined-behavior-can-format-your-drive – Jerry Jeremiah Feb 22 '21 at 02:48
  • 3
    And make sure you only call `va_arg` the same number of times as the number of passed in args. That means you need some way to tell how many args there are or where they end. Ever wonder why the first arg of `printf` and friends is a format specifier string? It tells the variadic implementation the type, order and number of args for each call. – kaylum Feb 22 '21 at 02:50
  • Look at this link https://stackoverflow.com/a/49738312/7462275 to have some details about the __builtin_va_list implementation. As Basile wrote : "On current processors and ABIs the calling conventions do use processor registers to pass some arguments (and the evil is in the details)." – Stef1611 Feb 23 '21 at 08:01

0 Answers0