struct initializer, typedef with visual studio - c

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;

Related

How to get a warning when assigning integer to enum?

Is there a way to get Clang or GCC to warn when assigning a plain integer to a variable of enum type? This question refers to C, not C++.
Example:
typedef enum foo_e { A=1, B=2 } foo_t;
foo_t fun() {
foo_t x = A; // this is fine
foo_t y = 2; // should trigger a warning
int z = B; // this is fine
return 1; // should trigger a warning, as the return type is foo_t
}
The "classic" Intel compiler issues a warning for these cases: warning #188, "enumerated type mixed with another type". This revealed several real bugs in our code. However, this being an open-source project run by volunteers, we do not have the possibility to test with this non-free compiler on a regular basis, and cannot integrate it into CI pipeline. Having seen the value of these checks, I am wondering if there is a way to get them with Clang or GCC.
Checking the GCC warning documentation shows no options that would do as you request.
There are options for missing enumeration values in switch statements (-Wswitch and -Wswitch-enum), comparisons between values of different enumeration types (-Wenum-compare), and conversions between different enumeration types (-Wenum-conversion).
Likely the compiler developers have felt that warnings about assigning int values to an lvalue of enumeration type will not be useful because it will warn about ordinary desired assignments such as i = i+1 in:
for (enum weather i = sunny; i <= rain; i = i+1)
…
In that, i+1 is an int because 1 is an int and the operands of + are converted to a common type, and then this int is assigned to the enum weather i.
There are some kludges to use enumerations with some type safety.

keil error #28 expression must have a constant value

I'm using Keil uVision to compile this code for an embedded project.
void doSomething(void)
{
unsigned char a = 0x01;
unsigned char b = 0x02;
typedef struct
{
void *pVoid;
} test_t;
test_t t[] = {{&a}, {&b}};
}
On the last line I receive an error
error: #28: expression must have a constant value
I've read that this is an issue where the compiler does not know the size of the variable. I don't understand what that means.
The variables a and b are of a defined type, so their pointers will always be the same size? Regardless of their type, this is embedded so pointers are all the same size?
It works to make var a and b static, why? This being embedded, I don't want the memory allocated continuously so that is not a solution here.
UPDATE:
I'm using Keil uVision 4.72.10.0 with Armcc v5.03.0.76 - I was able to get Keil to compile this, using the "--c99" flag, as found here.
Your function is perfectly fine in modern C, or even in C99, but C90 had stricter rules for initializers, and your code does not comply with those.
Here is the relevant provision of C90:
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.
(C90 6.5.7/4; emphasis added)
Structure types are aggregate types, so that applies to your code (when interperted according to C90). Where a and b identify function-scope variables, the expressions &a and &b are not constant expressions, so your code does not conform (to C90).
C99 drops the bit about aggregate or union type, and C2011 adds a provision for objects with thread storage duration (new in that version of C) to yield:
All the expressions in an initializer for an object that has static or
thread storage duration shall be constant expressions or string
literals.
That does not apply to your code.
It seems, then, that your compiler is enforcing C90 rules. Perhaps there is an option to select a more recent standard, but if not, then your best alternative is probably to set the structure members' values with assignment statements instead of an initializer:
test_t t[2];
t[0].pVoid = &a;
t[1].pVoid = &b;

Is it undefined behaviour in C to use pointer to a typedef type as a pointer to the primitive type?

Suppose that one has the following code:
#include <stdio.h>
typedef int myIntType;
int main()
{
int a;
myIntType *ptr = &a;
*ptr = 1;
printf("%d\n", a);
return 0;
}
Does this invoke undefined behaviour?
Under any reasonable interpretation, I expect this should simply set the value of a to 1 and therefore print the line 1 on stdout.
Compiling with gcc -Wall -Wextra -pedantic main.c -o main on Mac OS X does not produce any errors or warnings, and produces the expected output on execution.
My question is purely theoretical, and I do not intend to use such a construct in real-life code.
From cppreference.com about the typedef specifier:
The typedef specifier ... may declare array and function types, pointers and references, class types, etc. Every identifier introduced in this declaration becomes a typedef-name, which is a synonym for the type of the object or function that it would become if the keyword typedef were removed.
In other words, it is the exact same as if you removed your custom type and replaced it with int. Thus, it is well-defined behavior. int and myIntType are 100% interchangeable.
That is actually a c++ reference. From K&R, chapter 6.7 on typedef:
C provides a facility called typedef for creating new data type names. For example, the declaration typedef int Length; makes the name Length a synonym for int. The type Length can be used in declarations, casts, etc., in exactly the same ways that the type int can be.
Do be aware that K&R isn't the most up-to-date standard. The other answer cites the current standard. As far as I know, typedef hasn't changed.
In your example, myIntType * is the same as int *.
Section 6.7.8 of the C standard gives an example of this:
4 EXAMPLE 1 After
typedef int MILES, KLICKSP();
typedef struct { double hi, lo; } range;
the constructions
MILES distance;
extern KLICKSP *metricp;
range x;
range z, *zp;
are all valid declarations. The type of distance is int, that of
metricp is "pointer to function with no parameter specification
returning int", and that of x and z is the specified structure; zp is
a pointer to such a structure. The object distance has a type
compatible with any other int object.

C structure initialization with variable

I've run into a problem that seems to not be addressed by any of the C Standards after C89 save for the mention that structures initialization limits had been lifted. However, I've run into an error using the Open Watcom IDE (for debugging) where the compiler states that the initializer must be a constant expression.
Here's the gist of what's going on.
typedef struct{
short x;
short y;
} POINT;
void foo( short x, short y )
{
POINT here = { x, y }; /* <-- This is generating the error for the compiler */
/* ... */
}
Any ideas why, or what standard disallows that?
The following quote is from the C99 rationale:
The C89 Committee considered proposals for permitting automatic
aggregate initializers to consist of a brace-enclosed series of
arbitrary execution-time expressions, instead of just those usable for
a translation-time static initializer. Rather than determine a set of
rules which would avoid pathological cases and yet not seem too
arbitrary, the C89 Committee elected to permit only static
initializers. This was reconsidered and execution-time expressions are
valid in C99.
The problem is that C isn't an Object language and only does strict typing. Further, C maintains a difference between structs and arrays.
The way your code will have to work is
void foo( short x, short y )
{
POINT here;
here.x = x;
here.y = y;
}
This is normal for C89... initializers do need to be constant, ie. able to be determined at compile time. This means no variables in initializers, and it's true for other types as well, not just structs. In C99, your code would work.

Casting a constant to a union

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

Resources