Gives the output: 65 A
No it doesn't, necessarily, because the code invokes undefined behavior since u1 isn't initialized and doesn't have its address taken, see (Why) is using an uninitialized variable undefined behavior?
Undefined behavior = no telling what will happen. On a 32 bit little endian system, at best it prints 3 bytes of indeterminate garbage followed by a 4th byte with the ASCII value. I get some garbage 2005012545. To make any sense of the garbage, I could print it as hex instead: 0x77821041, where 41 is the LSB containing the ASCII value 0x41/65.
This is because you didn't initialize the union and then you are probably running a debug build which just happens to zero-out all stack values, so that the program seems to work for you.
Fix this by initializing the union to a known value: ... } u1 = {0};.
As for u1.theChar = "A";, it doesn't even compile on a correctly configured C compiler (see "Pointer from integer/integer from pointer without a cast" issues). Because the string literal "A" end up as a char* type when assigned. but theChar is of type char. This is also undefined behavior, that's pointless to make any sense of. At best, you would end up with one of the bytes from the character pointer's address stored in theChar, but the code isn't valid, so there's no telling what it will do.
Necessary fixes:
#include <stdio.h>
int main (void)
{
union {
int theInt;
char theChar;
} u1 = {0};
u1.theChar = 'A';
printf("%d\n", u1.theInt);
printf("%c\n\n", u1.theChar);
u1.theChar = "A"[0];
printf("%d\n", u1.theInt);
printf("%c\n\n", u1.theChar);
}
The output is now predictable as long as we stick to little endian systems:
65
A
65
A
A big endian 32 bit system should give this output instead:
1090519040
A
1090519040
A
1090519040 = 0x41000000