The following code:
#include <stdio.h>
typedef union {
int n;
char *s;
} val_t;
int main(void) {
val_t v1,v2;
v1 = (val_t)"Hello World";
v2 = (val_t)10;
printf("%s %d\n", v1.s, v2.n);
return(1);
}
compiles and executes correctly with gcc. If one tries to cast a constant for which there's not a suitable field in the union, an error message is produced.
Looking at the (C99) standard, though, I've not been able to locate the section where this behaviour is described. Hence, my question:
Does the C standard guarantee that I can cast a constant to a union type, provided that the union type has a field with a compatible type?
or, in other words:
Is ((val_t)10) a valid rvalue of type val_t?
It would also be interesting to know if this behaviour is supported by other compilers (or at least MS Visual C++). Does anybody know?
EDIT:
Casting to a union is a GCC extension, so it's not a good idea to use it.
Thanks to Maurits and Neil! I didn't think about using -pedantic to check!
In GNU C language extensions casting to a union is marked as an extension to the C standard. So most probably you won't find it in the C99 or any other C standard. The IBM C compiler supports this extension as well.
[neilb#GONERIL NeilB]$ gcc -Wall -pedantic sw.c
sw.c: In function 'main':
sw.c:11: warning: ISO C forbids casts to union type
sw.c:12: warning: ISO C forbids casts to union type
Related
I have declared a flexible array member in union, like this:
#include <stdio.h>
union ut
{
int i;
int a[]; // flexible array member
};
int main(void)
{
union ut s;
return 0;
}
and compiler gives an error :
source_file.c:8:9: error: flexible array member in union
int a[];
But, Declared array zero size like this :
union ut
{
int i;
int a[0]; // Zero length array
};
And it's working fine.
Why does zero length array work fine union?
No, unions do not support flexible array members, only structs. C11 6.7.2.1 §18
As a special case, the last element of a structure with more than one
named member may have an incomplete array type; this is called a
flexible array member.
In addition, zero-length arrays is not valid C, that's a gcc non-standard extension. The reason why you get this to work is because your compiler, gcc, is configured to compile code for the "non-standard GNU language". If you would rather have it compile code for the C programming language, you need to add compiler options -std=c11 -pedantic-errors.
int a[] is the C standard notation (since C99).
int a[0] is GNU C syntax, which predates C99. Other compilers might also support it, I don't know.
Your compiler seems to default to C90 standard with GNU extensions, which is why latter compiles, but first one does.
Furthermore, as stated in Lundin's answer, standard C does not support flexible array members in union at all.
Try adding -std=c99 or -std=c11 to your compiler options (gcc docs here).
Also -pedantic or -pedantic-errors is probably a good idea too, it'll enforce more strict standard compliance.
And, obligatory aside, -Wall -Wextra won't hurt either...
I'm not sure what the standard would say about this, but G++' unions seems to accept flexible arrays just fine. If you wrap them in an anonymous struct first, like so:
union {
unsigned long int ul;
char fixed[4][2];
struct {
char flexible[][2];
};
};
The sizes of enum types in the following code are different, why? Will the declararation of an enum type will cause gcc to treat it as signed int? And this feature cause a problem in my project, for example, the size of one enum type is 1 in "file_1.c", but the size of it is 4 in "file_2.c".
My test code:
enum foo foo_test(void);
enum foo {
FOO_0
};
enum bar {
BAR_0
};
int main(int argc, char **argv)
{
printf("sizeof(enum foo) %d, sizeof(enum bar) %d\n", sizeof(enum foo), sizeof(enum bar));
return 0;
}
When I compile it with arm-none-eabi-gcc v4.9.3 in my embedded project
the output is sizeof(enum foo) 4, sizeof(enum bar) 1.
When I compile it with gcc v4.8.3 in Windows
compile with gcc -Wall -o sizeofTest.exe sizeofTest.c
the output is sizeof(enum foo) 4, sizeof(enum bar) 4.
compile with gcc -Wall -fshort-enums -o sizeofTest.exe sizeofTest.c
the output is sizeof(enum foo) 4, sizeof(enum bar) 1.
As is so common with the questions asking about peculiar outcomes in c, the reason here is undefined behaviour. The C11 standard draft n1570 6.7.2.3p3:
A type specifier of the form
enum identifier
without an enumerator list shall only appear after the type it specifies is complete.
And of completeness in C11 6.7.2.2p4:
[...] The enumerated type is incomplete until immediately after the } that terminates the list of enumerator declarations, and complete thereafter.
As a shall in a constraints section was violated, a conforming compiler must output a diagnostics message - however GCC by default isn't a conforming implementation unless you ask it to be -pedantic:
ISO C forbids forward references to ‘enum’ types [-Werror=pedantic]
enum foo foo_test(void);
^
Now, it seems that the compiler there uses the shortest possible enums for any type. Since you used the enum foo before it was actually defined what was in there, the compiler had to resort to int for its type, otherwise char was used. It can be reproduced with -fshort-enums. Your program prints 4 1, whereas this program prints 1 1 with -fshort-enums on my gcc.
#include <stdio.h>
enum foo {
FOO_0
};
enum foo foo_test(void);
enum bar {
BAR_0
};
int main(int argc, char **argv)
{
printf("sizeof(enum foo) %zu, sizeof(enum bar) %zu\n",
sizeof(enum foo), sizeof(enum bar));
return 0;
}
First of all, fix the undefined behaviour in your code. The correct format specifier for the sizeof expression type is %zu.
The backing type of an enumerator in C can be char, int, or unsigned. The compiler can pick any of these so long as it is capable of storing all the enumerated values.
So a value of 1 is feasible (as sizeof(char) is always 1), and you have asked some of your compilers to squeeze the enum backing types.
The fact that an int appears to be chosen for the backing type for foo is due, I think, to your referring to enum foo before you define it, and your friendly C compiler is using some sort of implicit int. Such an implicit declaration is no longer standard C and your code is not strictly portable.
Reference: http://en.cppreference.com/w/c/language/enum
This is undefined behavior.
You must use %zu to make printf() format a value of type size_t.
If the size of the value differs from that of int, the code in printf() will mis-read the varargs-argument and get the wrong idea of its value (va_arg(args, int); instead of va_arg(args, size_t);.
The answer is actually quite simple.
As you have function prototype returning enum type, the compiler assumes it as the int and its size is hardware dependent (in your case 4 bytes) It is done for the optimisation purposes. For the another one is stored in the smallest possible data type. (probably -Os optimisation and / or -fshort-enums were used)
For coders having the real programming experience this behaviour is well known, and that is the reason to do not declare functions as returning enums if you want to keep them short.
For C standard fanatics uint8_t foo(); and return FOO_0; might be unacceptable but the programming is a real, live and practical science.
#include<stdio.h>
void fun(z)
{
printf("%d",z);
}
int main()
{
int a=5;
fun(a);
}
This is giving output as 5. Shouldn't it give an error - undeclared variable z ?
Is this a compiler optimization ?
This is not a compiler optimization, it is a compliance with an ancient C convention that allowed you to skip variable and parameter types when the desired type is int. This convention pre-dates the ANSI standard, and should be avoided even if your compiler is fine with such code.
You will get a warning if you tell the compiler that you want your code to comply with one of more modern standards, say, C99 or C11. The flag is compiler-dependent. If you are using gcc, add
-std=c99
flag to see the warning.
Given the following C code:
union Test {
struct {
int f1;
int f2;
};
struct {
int f3;
int f4;
int f5;
};
};
union Test test = {.f1 = 1, .f2 = 2};
When I compile this with gcc 6.1.1 f5 will be zero initialized. When I do with clang 3.8.0 it is not. I tried with -O0 and -O2 for both compilers which did not make any difference. This is on Linux x64.
Which is the correct behavior and can I tell clang to behave like gcc in this case? Reason is I try to compile some code with clang that assumes zero initialization in this case.
Update
Since the answers so far cite C11. Were there any changes in the standard that changed the behavior in later versions?
C11 specifies at section 6.2.6.1.7 :
When a value is stored in a member of an object of union type, the bytes of the object
representation that do not correspond to that member but do correspond to other members
take unspecified values.
You access the union via the first struct, accessing members of the second struct can produce unspecified values, so clang is not wrong neither is gcc.
Update: anonymous members were added in C11. Designated inits appeared in C99.
I wonder why the following does not work with Visual studio
typedef struct {
float x, y;
} complexf;
typedef union {
complexf f;
long long d;
} rope;
int main(void)
{
complexf a;
rope z = {a};
}
The error is at line rope z = {a}, cannot convert from complexf to float. If the first member of the union is not a typedef, then it works. Is this a compiler bug, or a dark edge of C ?
ANSI C standard (aka C89), 3.5.7:
All the expressions in an initializer
for an object that has static storage
duration or in an initializer list for
an object that has aggregate or union
type shall be constant expressions.
The latter part of this restriction has been dropped in C99, which isn't properly supported by VS.
in VS 6.0 when I compile with /W4 I get
warning C4204: nonstandard extension used : non-constant aggregate initializer
so that makes me think it's not standard C and you are in compiler dependent land.
Works fine on my G++ 3.4.4 on Cygwin.
Except for the ':' at the end needing a change to ';'.
And, I changed '__int64' to 'long long'.
With my stronger bias towards C for such code, I would have written,
rope z = *(rope *)&a;
to force it on to the C++ compiler :-).
But, don't do that...
The right way to go is of course, as David notes in his comment,
rope z; z.f = a;