ctypesprintfunions

How does printing a union itself and not its member work in C?


I'm making a hashtable in C that I want to contain elements of different types, so I made it return a union that can hold int, float or char *. But when trying printing the union itself out float gets printed as 0.

So it looks like this:

typedef union{
 int ival;
 float fval;
 char *strval;
} Data_u;

Say I get arbitrary data from the table:

 Data_u ip = getdata();  -- returns union with .ival of 89;
 Data_u fp = getdata();  --returns union with .fval of 205.6f;
 Data_u strp = getdata();  -- returns union with .srval to "Hello";

Then

 printf("STRING IS: %s\n",strp);  --works, prints Hello;
 printf("INT IS: %i\n",ip);  --works, prints 89;
 printf("FLOAT IS: %f\n",fp); --DOESN't work, prints 0.0000..

However, printf("%f\n",fp.fval works, so the data is here, but for some reason only float can't be accessed by union variable itself.

My question is why is that? If union is just a struct that holds 1 value at a time at 0 offset, shouldn't this work for any type as long as the formatting in printf() is correct? If not, why does this works for int and char *? float should be the biggest type out of the three too, so it shouldn't be cut or something. And since printing fp.fval works correctly it should be the case. I'm confused.


Solution

  • The conversion specifiers for printf tell printf what type of argument to expect. If you pass the wrong type of argument, the C standard does not define the behavior.

    If optimization by the compiler does not interfere, then the mechanics of what happens are often as described below.

    To pass an argument to a function, a program must put the bytes of the argument somewhere. For the function to use an argument, the function must get the bytes of the argument from somewhere.

    Where the bytes are put often depends on the type of the argument:

    Your union is probably four or eight bytes, depending on the size of char * in your C implementation. In your C implementation, a union this small is probably passed in a general register. Probably integer and pointer arguments are also passed in a general register, and floating-point arguments are passed in floating-point registers.

    Then: