[comp.std.c] 3.7.2 External Object Definitions

eyal@echo.canberra.edu.au (Eyal Lebedinsky) (02/06/91)

G'day

I have a situation where two static structures at the external level
are defined and initialized to each have a member pointer to the other.
My problem is finding the proper ANSI declarations/definitions for this
case.

It is obvious that the second object defined should first have a
forward declaration for the first object to be properly initialized.
Maybe a sketch will do here:

	<linkage> struct x i7;
	static struct x i6 = { &i7 };
	<linkage> struct x i7 = { &i6 };

Now, the <linkage> is what I find different compilers have different
expectations of. ANSI 3.7.2 is where I looked for the answer. I tried
gnu (1.39) which I hope is ANSI by now. It likes this one:

(1)	extern int i7;
(2)	static int i7 = 1;

It hates this one:

	static int i7;		/* tentative definition, internal linkage */
(3)	extern int i7 = 1;

Microsoft 5.1 likes both, but I wouldn't trust it much.

Q: what should the comment on the right say for (1,2,3) in the sense on
the examples at the end of 3.7.2? What I want is to first give a
'tentative definition' or a 'declaration' followed later by the
initialized 'definition'.

I know how shady this area is with traditional compilers and do not wish to
discuss it, so please limit replies to ANSI issues.

sarima@tdatirv.UUCP (Stanley Friesen) (02/07/91)

In article <eyal.665840220@echo> eyal@echo.canberra.edu.au (Eyal Lebedinsky) writes:
>I tried gnu (1.39) which I hope is ANSI by now. It likes this one:
>
>(1)	extern int i7;
>(2)	static int i7 = 1;

Well, then it is wrong.  (1) is a tentative definition with *external* linkage.
As such it is incompatible with (2), which is illegal in the same scope.
[(1) is external by 3.1.2.2 paragraph 4, the incompatibility is required
by paragraph 7].

>It hates this one:
>
>	static int i7;		/* tentative definition, internal linkage */
>(3)	extern int i7 = 1;

Beep, Gnu is wrong again!  This is actually the correct way to do it.
(3) could also be:
	static int i7 = 1;
The original (3) is legal due to the statement that makes (1) external.
In essence, if an 'extern' appears *after* a 'static' it is a backwards
reference to the 'static' declaration, otherwise it is an external declaration.
The prior declaration is clearly a tentative definition of a variable with
internal linakage by 3.7.2 paragraph 2.

The examples in 3.7.2 actually support this treatment if read carefully.

>Microsoft 5.1 likes both, but I wouldn't trust it much.

Well, at least it is only half wrong.  It is *required* to diagnose (2)
if it follows (1).

>Q: what should the comment on the right say for (1,2,3) in the sense on
>the examples at the end of 3.7.2?

(1) /* tentative definition, external linkage */
(2) /* undefined - linkage disagreement, would be internal linkage */
(3) /* definition, refers to previous (internal linkage) */


Sigh, well, I guess gcc isn't as ANSI as it likes to claim.
-- 
---------------
uunet!tdatirv!sarima				(Stanley Friesen)

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (02/07/91)

Say struct foo's first element points to struct bar, and vice versa.
Here's how to initialize mutually referencing static structures:

  static struct {
    struct foo f;
    struct bar b;
  } x = {
    { &x.b },
    { &x.f }
  };

Done. Most compilers will use fixed addresses for &x.b and &x.f, so this
will be just as efficient as separate static structure declarations.
  
In article <eyal.665840220@echo> eyal@echo.canberra.edu.au (Eyal Lebedinsky) writes:
> It is obvious that the second object defined should first have a
> forward declaration for the first object to be properly initialized.

No need.

> I know how shady this area is with traditional compilers and do not wish to
> discuss it, so please limit replies to ANSI issues.

No, the solution above works fine under any C compiler.

This is the fourth time since September that the same question has come
up in comp.lang.c (and now comp.std.c); my fingers are getting tired.
Anyone else think this belongs in the comp.lang.c FAQ? Steve, are you
listening?

---Dan

eyal@echo.canberra.edu.au (Eyal Lebedinsky) (02/07/91)

In <14327:Feb622:47:1391@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:

>Say struct foo's first element points to struct bar, and vice versa.
>Here's how to initialize mutually referencing static structures:

>  static struct {
>    struct foo f;
>    struct bar b;
>  } x = {
>    { &x.b },
>    { &x.f }
>  };

>Done. Most compilers will use fixed addresses for &x.b and &x.f, so this
>will be just as efficient as separate static structure declarations.
>  
[stuff deleted]

Yes, this would work. It is a practical solution and I may do it. But I
STILL want to know how to do it STRAIGHT, as ANSI intended. After all,
this is comp.std.c, not comp.lang.c.

>---Dan

mkahl@world.std.com (Michael Kahl) (02/07/91)

In article <131@tdatirv.UUCP> sarima@tdatirv.UUCP (Stanley Friesen) writes:
>In article <eyal.665840220@echo> eyal@echo.canberra.edu.au (Eyal Lebedinsky) writes:
>>I tried gnu (1.39) which I hope is ANSI by now. It likes this one:
>>
>>(1)	extern int i7;
>>(2)	static int i7 = 1;
>
>Well, then it is wrong.  (1) is a tentative definition with *external* linkage.
>As such it is incompatible with (2), which is illegal in the same scope.
>[(1) is external by 3.1.2.2 paragraph 4, the incompatibility is required
>by paragraph 7].

Exactly right... except: it's not really "illegal".  Paragraph 7 says "if,
within a translation unit, the same identifier appears with both internal
and external linkage, the behavior is undefined."  Undefined, not illegal.
Therefore, this construct should be avoided in portable programs, but gcc
is perfectly within its rights to accept the usage.

>>It hates this one:
>>
>>	static int i7;		/* tentative definition, internal linkage */
>>(3)	extern int i7 = 1;
>
>Beep, Gnu is wrong again!  This is actually the correct way to do it.

Right.  An ANSI compiler should accept this.

>>Microsoft 5.1 likes both, but I wouldn't trust it much.
>
>Well, at least it is only half wrong.  It is *required* to diagnose (2)
>if it follows (1).

Wrong.  The behavior is undefined.  This is neither a constraint violation
nor a syntax violation, and so no diagnosis is required.

IMHO, allowing an "extern" forward declaration followed later by a definition
of the identifier as "static" is a reasonable extension to the language.  In
some pre-ANSI compilers, this was the *only* way to forward-declare a name
not meant to be exported.  In deference to existing code, it is reasonable
to accept this usage.  The Standard permits this extension by leaving
the behavior undefined.

I don't know this for a fact, but I like to think that it was for this very
reason that the committee left it undefined rather than requiring a diagnosis.
Neither did they did not mandate this treatment, presumably so as not to place
undue burden on compilers that need to address variables differently depending
on their linkage, and don't read the entire source file before starting to
generate code. 
-- 
Michael Kahl, Symantec Corporation
mkahl@world.std.com  -or-  75236.3146@compuserve.com
Disclaimer:  Keep this quiet; what my employer doesn't know won't get me fired.

thorinn@diku.dk (Lars Henrik Mathiesen) (02/09/91)

sarima@tdatirv.UUCP (Stanley Friesen) writes:
>In article <eyal.665840220@echo> eyal@echo.canberra.edu.au (Eyal Lebedinsky) writes:
>>I tried gnu (1.39) which I hope is ANSI by now. It likes this one:
>>
>>(1)	extern int i7;
>>(2)	static int i7 = 1;

>Well, then it is wrong. (1) is a tentative definition with *external* linkage.

Actually, (1) is _not_ a definition, just a declaration. If (2) wasn't
there, i7 would be reference to a definition in another file. (1)
still makes the linkage external since (2) isn't visible at that point.