1

The following code snippet compiles just fine on Mac OS X with gcc, but fails to compile on Windows with lcc-win32:

typedef struct Foo Foo;
typedef struct Bar Bar;

struct Bar { int age; int height; };
struct Foo { Bar barOne; Bar barTwo; };

// Elsewhere, in some function:

Bar barOne = { 2, 4 };
Bar barTwo = { 6, 8 };
Foo instance = { barOne, barTwo };

And gives this error:

found 'struct Bar' expected 'int'

I can 'overcome' this by initializing the struct this way:

Foo instance = { barOne.age, barOne.height, barTwo.age, barTwo.height };

So, I understand what's going on... but I feel like this makes my code a lot more complex (I need to understand the implementation and layout of the other structs I'm using, instead of simply consuming them - and if that layout changes, I have to spider that change out to anyone else using the struct).

I'm wondering if LCC is being either "more strict" (adhering to some standard) or "more dumb" (the compiler is too dumb to handle this situation).

Thanks.

Also, please see my other LCC-Win32 question: LCC: Forward Declaration of Typedef'd Enum Failing?

Community
  • 1
  • 1
Steve
  • 31,144
  • 19
  • 99
  • 122

2 Answers2

3

As written:

typedef struct Foo Foo;
typedef struct Bar Bar;

struct Foo { Bar barOne; Bar barTwo; };
struct Bar { int age, int height };

// Elsewhere, in some function:

Bar barOne = { 2, 4 };
Bar barTwo = { 6, 8 };
Foo instance = { barOne, barTwo };

the code should fail to compile everywhere (and specifically fails on MacOS X 10.7.1 with GCC 4.6.0) with these errors (plus some others):

xx.c:4: error: field ‘barOne’ has incomplete type
xx.c:4: error: field ‘barTwo’ has incomplete type

This is because you try to use Bar before it is defined. Reverse the order of the structure definitions, and fix the syntax errors in Bar (comma should be semi-colon; missing semi-colon), and then (finally) it does compile on MacOS X.

What does the standard say about using structures as initializers?

§6.7.8 Initialization

¶13 The initializer for a structure or union object that has automatic storage duration shall be either an initializer list as described below, or a single expression that has compatible structure or union type. In the latter case, the initial value of the object, including unnamed members, is that of the expression.

Consider the context of a function (this code compiles OK with GCC set fussy):

typedef struct Foo Foo;
typedef struct Bar Bar;

struct Bar { int age; int height; };
struct Foo { Bar barOne; Bar barTwo; };

void somefunction(void)
{
    Bar barOne = { 2, 4 };
    Bar barTwo = { 6, 8 };
    Foo instance = { barOne, barTwo };
}

Superficially, it looks to me like barOne and barTwo are not single expressions. However, the standard goes on to say:

¶16 Otherwise, the initializer for an object that has aggregate or union type shall be a brace-enclosed list of initializers for the elements or named members.

If the aggregates had to be enclosed braces, then writing this would work:

Foo instance = { { barOne }, { barTwo } };

GCC emphatically rejects this construct, though.

i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)

/usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -c xx.c
xx.c:8: warning: no previous prototype for ‘somefunction’
xx.c: In function ‘somefunction’:
xx.c:11: error: incompatible types in initialization
xx.c:11: warning: missing initializer
xx.c:11: warning: (near initialization for ‘instance.barOne.age’)
xx.c:11: error: incompatible types in initialization
xx.c:11: warning: missing initializer
xx.c:11: warning: (near initialization for ‘instance.barTwo.age’)
xx.c:11: warning: missing initializer
xx.c:11: warning: (near initialization for ‘instance.barOne’)
xx.c:11: warning: unused variable ‘instance’

On the whole, I'm inclined to trust GCC's judgement and point the finger at LCC not handling a case validly. Disputing that will require a complete parsing of §6.7.8 of the C standard, and I've not provided all the material (it goes to ¶23 before starting on the examples).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Sorry, sample code was just thrown together on SO as an example. The exact syntax wasn't meant to be scrutinized. Obviously, I'm not using structs in my actual application called "Foo" and "Bar" LOL. `On the whole, I'm inclined to trust GCC's judgement and point the finger at LCC not handling a case validly.` Agreed. I think this is the point Jesus was making in his answer as well. – Steve Sep 01 '11 at 17:47
  • Statements like these make me feel like gcc is correct as well: `1662 The initializer for a structure or union object that has automatic storage duration shall be either an initializer list as described below, or a single expression that has compatible structure or union type.` and `1678 If the aggregate or union contains elements or members that are aggregates or unions, these rules apply recursively to the subaggregates or contained unions.` – Steve Sep 01 '11 at 17:56
  • @Steve: The exact syntax is often critical to understanding things like this. Copy-and-paste a small complete program that exhibits the issue. – Keith Thompson Sep 01 '11 at 19:18
  • @Keith; Jonathan's code immediately following `Consider the context of a function` is perfect. I switched to MinGW and my problem went away. Even the `-pedantic` flag doesn't warn me about anything wrong with the way I'm doing it. I'm pretty sure it's just unsupported by LCC. – Steve Sep 01 '11 at 19:49
1

Well it's not called the Little C Compiler sometimes for nothing. It can handle most things but to save space and time it will generally be stricter in these cases. Implementing something that looks simple usually isn't in a compiler. Either that or LCC was just never updated to handle these situations. Is there a specific reason for using LCC instead of Borland, MSVC++, Cygin/MingW32 gcc?

Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
  • Yeah, maybe you can point me in the right direction if I'm off here. AFAIK, MSVC compiler doesn't support C99. Cygwin forces me to depend on that posix emulation lib. I ran into some problems running MinGW, but I may return to that if LCC proves to be deficient for my needs. Right now I like it because it's "simple" (easy to install, easy to use, builds Windows DLLs easily). I'm open to paying for a good compiler. Would you personally recommend Borland, or someone else? I also saw the Intel suite. (Especially) if I'm paying for one, it should be easy to setup and use. – Steve Sep 01 '11 at 17:17
  • Intel is very nice but as a student I get it for free, its kind of pricey. For ease I would recommend Borland or if you're into C++ the Bloodshed compiler. C99 has always been odd on windows but Cygwin/MingW32 with (can't remember flag now) will compile and then work on any system even without those tools installed (for instance firefox is compiled on cygwin IIRC) – Jesus Ramos Sep 01 '11 at 17:21
  • Use MinGW GCC. Don't install any of the supporting stuff (MSYS). To make DLLs, use `-shared` just like on linux/osx. If you need an IDE, not a compiler, use Code::Blocks. It's apparently also possible to use the MSVC/++ IDE with GCC somehow. I don't recommend Borland. – τεκ Sep 01 '11 at 17:28
  • I agree with Tek, there's no reason not to use GCC even on windows :) and unless you're writing really high performance code then don't bother with ICC (but it is amazing). – Jesus Ramos Sep 01 '11 at 17:33
  • @Steve: you should also take a look at Clang - its error reporting is superior to what GCC offers – Christoph Sep 01 '11 at 17:42
  • @Christoph; Thanks! I'm actually already using Clang & LLVM on Mac for iOS. It's awesome. – Steve Sep 01 '11 at 17:49
  • Note that lcc-win32 is derived from lcc; it's quite different, and I understand it has much better C99 support. – Keith Thompson Sep 01 '11 at 19:23