Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

why can't array forward declarations be static?

1,174 views
Skip to first unread message

Alan Curry

unread,
Dec 10, 2009, 1:11:38 PM12/10/09
to
An array can be declared and used without the size being known, as long as
it's fully defined with a size in some other source file:

$ cat a.c
cat: a.c: No such file or directory
$ cd /tmp
$ cat a.c
extern int foo[];
int get_foo(int x)
{
return foo[x];
}
$ cat b.c
int foo[100];
int main(void) { }
$ gcc -std=c99 -pedantic -Wall a.c b.c
$

The full definition can also come later in the same source file:

$ cat ab.c
extern int foo[];
int get_foo(int x)
{
return foo[x];
}
int foo[100];
int main(void) { }
$ gcc -std=c99 -pedantic -Wall ab.c
$

What if you don't want it to be exported?

$ cat ab_static.c
static int foo[];
int get_foo(int x)
{
return foo[x];
}
static int foo[100];
int main(void) { }
$ gcc -std=c99 -pedantic -Wall ab_static.c
ab_static.c:1: error: array size missing in 'foo'
ab_static.c:6: error: conflicting types for 'foo'
ab_static.c:1: error: previous declaration of 'foo' was here
$

Oops. But look at this:

$ gcc -std=c99 -Wall ab_static.c
$

Without -pedantic, not an error, not even a warning. That means gcc believes
that the forward declaration with static is perfectly reasonable although
technically forbidden by the standard. I agree that it's perfectly
reasonable, so now I come to my point: why is this forbidden?

Sighting in the wild: this is the only thing preventing ffmpeg from being
compilable with -pedantic. The array is an array of structs, it's initialized
using a size implicitly defined by the length of the initializer, each struct
contains a function pointer that's initialized to point to a static function
that's declared after the array's forward declaration, and all these
functions use the array.

So there's really no way to rearrange it to eliminate the forward declaration
without adding a lot more forward declarations of static functions, or
manually counting the array elements to provide a size in the array forward
declaration. This is the best possible organization of the code. It really
seems weird that the only way to make it pedantically correct is to make
something extern that isn't used in another source file.

--
Defiantly on topic,
Alan Curry
--
comp.lang.c.moderated - moderation address: cl...@plethora.net -- you must
have an appropriate newsgroups line in your header for your mail to be seen,
or the newsgroup name in square brackets in the subject line. Sorry.

Clive D. W. Feather

unread,
Dec 14, 2009, 1:08:55 PM12/14/09
to
In message <clcm-2009...@plethora.net>, Alan Curry
<pac...@kosh.dhis.org> wrote:
>An array can be declared and used without the size being known, as long as
>it's fully defined with a size in some other source file:
[...]

>The full definition can also come later in the same source file:

Correct.

>What if you don't want it to be exported?
>
>$ cat ab_static.c
>static int foo[];
>int get_foo(int x)
>{
> return foo[x];
>}
>static int foo[100];
>int main(void) { }
>$ gcc -std=c99 -pedantic -Wall ab_static.c
>ab_static.c:1: error: array size missing in 'foo'
>ab_static.c:6: error: conflicting types for 'foo'
>ab_static.c:1: error: previous declaration of 'foo' was here
>$

Also correct - this is undefined behaviour.

>Without -pedantic, not an error, not even a warning. That means gcc believes
>that the forward declaration with static is perfectly reasonable although
>technically forbidden by the standard.

Right. Note that it's not a constraint violation, so even if GCC allows
the code it is still conforming to the Standard.

>I agree that it's perfectly
>reasonable, so now I come to my point: why is this forbidden?

Because pre-1989 linkers varied in their ability to handle "hidden"
symbols. If a linker can't cope with two symbols called "foo" in two
different translation units that *aren't* the same object, then the
compiler has to resolve the issue at translation - rather than link -
time. And many C compilers were one-pass, so they need to assign an
address and space to "foo" when they first see it, or at least the first
time it's referenced. Hence the restriction.

As the Rationale puts it: "Prior to C90, implementations varied widely
with regard to forward referencing identifiers with internal linkage."

Incidentally, the reason it was required to work in a single file for a
symbol with external linkage is that the incomplete version ("int
foo[];") might have been in a #included header file.

--
Clive D.W. Feather | Home: <cl...@davros.org>
Mobile: +44 7973 377646 | Web: <http://www.davros.org>
Please reply to the Reply-To address, which is: <cl...@davros.org>

Alan Curry

unread,
Dec 15, 2009, 2:12:54 AM12/15/09
to
In article <clcm-2009...@plethora.net>,

Clive D. W. Feather <cl...@davros.org> wrote:
>In message <clcm-2009...@plethora.net>, Alan Curry
><pac...@kosh.dhis.org> wrote:
>
>>I agree that it's perfectly
>>reasonable, so now I come to my point: why is this forbidden?
>
>Because pre-1989 linkers varied in their ability to handle "hidden"
>symbols. If a linker can't cope with two symbols called "foo" in two
>different translation units that *aren't* the same object, then the
>compiler has to resolve the issue at translation - rather than link -
>time. And many C compilers were one-pass, so they need to assign an
>address and space to "foo" when they first see it, or at least the first
>time it's referenced. Hence the restriction.

One-pass compiler compatibility, when the compiler can punt to the linker if
the symbols are extern. All right, that makes sense historically. Any good
reason for it still being forbidden in C99 and beyond?

--
Alan Curry

Clive D. W. Feather

unread,
Dec 16, 2009, 10:15:59 PM12/16/09
to
In message <clcm-2009...@plethora.net>, Alan Curry
<pac...@kosh.dhis.org> wrote:
>One-pass compiler compatibility, when the compiler can punt to the linker if
>the symbols are extern. All right, that makes sense historically. Any good
>reason for it still being forbidden in C99 and beyond?

Nobody brought a proposal to WG14 asking for it to be changed.

This isn't an attempt to duck responsibility. Proposals didn't have to
come from outsiders: if WG14 attendees thought something needed
changing, they were not shy about bringing proposals off their own bat.
But in this case, as far as I recall, nobody, outside *or inside* WG14
brought a proposal. That means nobody who thought about it considered it
an issue. For myself, I don't even remember thinking about it, let alone
dismissing it as a non-issue.

If a proposal *had* been brought (or if one were brought to the next
revision process), the next question would be whether this is a
significant issue for any existing implementations. I would personally
be tempted to say "no, anyone with enough resources to move their
compiler from C99 to C0X has the ability to fix this as well", but I
could be wrong - for example, there might well be situations where the
compiler author has no control over the linker.

--
Clive D.W. Feather | Home: <cl...@davros.org>
Mobile: +44 7973 377646 | Web: <http://www.davros.org>
Please reply to the Reply-To address, which is: <cl...@davros.org>

0 new messages