3

It is possible to scanf values into a union depending on the format specifier.

union {
    int i;
    double f;
} u;

scanf("%lf", &u.i); // implicitly scan the double

printf("%lf", u.f); // explicitly print the double

This works because scanf takes pointers as arguments, and all elements in the union are aligned, (void*)&u.i == (void*)&u.f.

Is it possible to change the printf line in a way that does the same thing, picking the correct value to print from the union based on the format, without accidentally slicing some of the possible values?

printf("%lf", ???); // implicitly print the double
timrau
  • 22,578
  • 4
  • 51
  • 64

2 Answers2

3

printf cannot infer the type of the arguments passed to it. The argument will be interpreted as per the corresponding conversion specifier in the format string. Therefore, the arguments must correspond properly to their respective conversion specifiers. Quoting C11 standard § 7.21.6.1 ¶9 about fprintf

If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

Again quoting from the C11 standard § 7.21.6.2 ¶13 about fscanf

If a conversion specification is invalid, the behavior is undefined.

Therefore, the following call to scanf invokes undefined behaviour because u.i is of type int but %lf conversion specifier means you are reading a double.

scanf("%lf", &u.i);

Please read these -

  1. What happens when I use the wrong format specifier?
  2. Wrong format specifiers in scanf or printf

Also, it's undefined behaviour to access the field of a union which was not most recently written.

union {
    int i;
    double f;
} u;

// undefined behaviour. u.i is of type int but %lf
// means scanf will read a double
scanf("%lf", &u.i);

// undefined behaviour because reading the field u.f
// but the last written field is u.i
printf("%lf", u.f);
Community
  • 1
  • 1
ajay
  • 9,402
  • 8
  • 44
  • 71
1

No, you can't do that.

There's no indirect printf() format specifier, and different members of the union can have different size and thus be handled differently as a varargs parameter.

Even if there was a way to say "pointer to integer" instead of just "integer", there would still of course be no way to have printf() decide between "pointer to integer" and "pointer to double" based only on that pointer value.

If I understood you correctly, you'd like something like:

printf("%>g\n", &u);  /* prints the double */
printf("%>d\n", &u);  /* prints the int */

Where I invented (!) the > format modifier to say "the argument is a pointer to the corresponding data type, i.e. >g is "pointer todouble`". Sadly this is not standard. You can of course implement this yourself.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Not only on the value of the pointer, but in the format string, I am telling `printf` what to look for. Much like in the case of `scanf`. –  Apr 16 '14 at 08:53