Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Re: Who owns the variable in my header file ?

Reply
Thread Tools

Re: Who owns the variable in my header file ?

 
 
Edward A. Falk
Guest
Posts: n/a
 
      10-03-2012
In article <(E-Mail Removed)>,
lipska the kat <(E-Mail Removed)> wrote:
>Hi
>
>I have the following program
>distributed over 4 files
>
>/* foo.h */
>int foo;
>
>void fooset(int f);
>int fooget(void);
>void fooinc(void);


Perhaps the C standard has changed since I first read it, but AFAIK,
most of the answers so far have been wrong.

If you write this in your code:

int foo;

You've *declared* foo -- that is, described what it is -- but you haven't
*defined* it. There's a difference. In this case, no actual space
for foo has been allocated yet, and it's known as a "common" symbol.
If no module ever actually defines it, the linker will allocate space
for it at the end.

If you write

int bar = 1;

Now you've defined it. Space and an initial value for it will be
included in your module. If more than one module does this, there will
be a conflict.

So add "int bar = 1;" to your foo.h and then compile fooget into a binary.
In Unix, you can give the command "nm fooget.o" to see all the symbols
associated with fooget:

0000000000000000 D bar
0000000000000004 C foo
0000000000000000 T fooget

So you see that bar has been defined, while foo is merely a common
symbol. (And fooget is text -- executable code).

When you compile all of your .c files and link them together, the
linker notes that there is a common named "foo" which is never actually
defined by any module, and so it allocates space for it. Bar, on the
other hand, has been defined more than once, so you have an error:

cc -o foo main.o fooget.o fooset.o
fooget.o.data+0x0): multiple definition of `bar'
main.o.data+0x0): first defined here
fooset.o.data+0x0): multiple definition of `bar'
main.o.data+0x0): first defined here
collect2: ld returned 1 exit status

As you can see, it never complained about foo.

If you wanted, you could have defined foo in one place, say in main.c:

#include "foo.h"

int foo = 1;

...

Now, when we do "nm main.o", we get:

0000000000000004 D bar
0000000000000000 D foo
U fooget
U fooinc
U fooset
0000000000000000 T main
U printf

You see that foo and bar are both defined here.


Now, a couple of semi-off-topic points:

You should do

#include "foo.h"

instead of

#include <foo.h>

The <> form is for system header files. My compiler (gcc on Linux) barfed
on the includes until I fixed them. I don't know how you got it to compile
on yours unless you used '-I." or something.

As other posters have mentioned, you really should use "extern" in your
declarations.
--
-Ed Falk, http://www.velocityreviews.com/forums/(E-Mail Removed)
http://thespamdiaries.blogspot.com/
 
Reply With Quote
 
 
 
 
Keith Thompson
Guest
Posts: n/a
 
      10-03-2012
(E-Mail Removed) (Edward A. Falk) writes:
[...]
> Perhaps the C standard has changed since I first read it, but AFAIK,
> most of the answers so far have been wrong.
>
> If you write this in your code:
>
> int foo;
>
> You've *declared* foo -- that is, described what it is -- but you haven't
> *defined* it. There's a difference. In this case, no actual space
> for foo has been allocated yet, and it's known as a "common" symbol.
> If no module ever actually defines it, the linker will allocate space
> for it at the end.
>
> If you write
>
> int bar = 1;


Did you mean to change the name from "foo" to "bar"?

> Now you've defined it. Space and an initial value for it will be
> included in your module. If more than one module does this, there will
> be a conflict.


N1370 6.9.2p2:

A declaration of an identifier for an object that has file scope
without an initializer, and without a storage-class specifier or
with the storage-class specifier static, constitutes a *tentative
definition*. If a translation unit contains one or more tentative
definitions for an identifier, and the translation unit contains
no external definition for that identifier, then the behavior
is exactly as if the translation unit contains a file scope
declaration of that identifier, with the composite type as of
the end of the translation unit, with an initializer equal to 0.

So

int foo;

*can* be a definition, or at least can act as one.

> So add "int bar = 1;" to your foo.h and then compile fooget into a binary.


But then two or more translation units within your program can see that
definition, and you can get a "multiple definition" error.

Instead, add

extern int foo;

to "foo.h", and

int foo = 1;

to "foo.c". Then any translation unit that includes "foo.h" can use the
object "foo", which is defined in exactly one place in your program.
(You could drop the "extern" in foo.h, making it a tentative definition,
but adding "extern" is more explicit.)

(I'm snipping some context in which you make some of these same points.)

[snip]

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
 
 
 
Kaz Kylheku
Guest
Posts: n/a
 
      10-04-2012
On 2012-10-03, Edward A. Falk <(E-Mail Removed)> wrote:
> In article <(E-Mail Removed)>,
> lipska the kat <(E-Mail Removed)> wrote:
>>Hi
>>
>>I have the following program
>>distributed over 4 files
>>
>>/* foo.h */
>>int foo;
>>
>>void fooset(int f);
>>int fooget(void);
>>void fooinc(void);

>
> Perhaps the C standard has changed since I first read it, but AFAIK,
> most of the answers so far have been wrong.
>
> If you write this in your code:
>
> int foo;
>
> You've *declared* foo -- that is, described what it is -- but you haven't
> *defined* it.


The above is a tentative definition. It is a definition, but there
is still a chance to override it with a value other than zero
given by a firm (term mine) definition.

When the end of a translation unit is reached, all definitions which
are still tentative become definitions with value zero.

int foo; /* tentative def */
int bar; /* tentative def */

int foo = 3; /* no longer tentative */

/* end of translation unit */
int bar = 0; /* <- not written in the source: but effective behavior */

> for foo has been allocated yet, and it's known as a "common" symbol.


Common symbols originally come from the "Common" linkage model. C supports that
model, but that model allows latitudes that are not permitted of strictly
conforming programs.

See here: http://www.lysator.liu.se/c/rat/c1.html#3-1-2-2

And anyway, ironically, under the Common model, every external declaration is
also a definition (whether or not the keyword extern appears in the
declaration).

> So add "int bar = 1;" to your foo.h and then compile fooget into a binary.
> In Unix, you can give the command "nm fooget.o" to see all the symbols
> associated with fooget:
>
> 0000000000000000 D bar
> 0000000000000004 C foo
> 0000000000000000 T fooget


The "common" designation is here only being used to distinguish those
definitions which have intializers from those which do not, so that the ones
which do not can be put into a different section when the executable is linked
(the "BSS" section).
 
Reply With Quote
 
Edward A. Falk
Guest
Posts: n/a
 
      10-04-2012
In article <(E-Mail Removed)>,
Keith Thompson <(E-Mail Removed)> wrote:
>
>Instead, add
>
> extern int foo;
>
>to "foo.h", and
>
> int foo = 1;
>
>to "foo.c". Then any translation unit that includes "foo.h" can use the
>object "foo", which is defined in exactly one place in your program.


Yes, exactly. This is how I write all my code, and is IMHO
best practices.

Also: I didn't know that tentative definitions were required to
be initialized with zero. That's good to know; I'd always assumed
it was implementation-specific.

--
-Ed Falk, (E-Mail Removed)
http://thespamdiaries.blogspot.com/
 
Reply With Quote
 
David Thompson
Guest
Posts: n/a
 
      10-11-2012
On Wed, 03 Oct 2012 16:21:37 -0700, Keith Thompson <(E-Mail Removed)>
wrote:

> (E-Mail Removed) (Edward A. Falk) writes:
> [...]
> > Perhaps the C standard has changed since I first read it, but AFAIK,
> > most of the answers so far have been wrong.
> >
> > If you write this in your code:
> >
> > int foo;
> >
> > You've *declared* foo -- that is, described what it is -- but you haven't
> > *defined* it. There's a difference. In this case, no actual space
> > for foo has been allocated yet, and it's known as a "common" symbol.
> > If no module ever actually defines it, the linker will allocate space
> > for it at the end.
> >
> > If you write
> >
> > int bar = 1;

>
> Did you mean to change the name from "foo" to "bar"?
>
> > Now you've defined it. Space and an initial value for it will be
> > included in your module. If more than one module does this, there will
> > be a conflict.

>
> N1370 6.9.2p2: <snip>
> So
>
> int foo;
>
> *can* be a definition, or at least can act as one.
>

It *must* produce a definition in that t.u., as far as C is concerned,
and if in multiple t.u.s linked together formally produces UB. But
many object-formats and linkers treat "default" initialization to zero
differently -- commonly(!) as "bss" or "udata" or somesuch -- than
initialization to nonzero value. Whether explicit = 0 is treated like
explicit (thus "idata" etc) or zero (not) may vary.

*WG14* n1370 is not a version of the standard; is that an SC (or
higher) document maybe? I generally use n869 (preadoption) or n1256
(postcorrigenda) as the best and easily available versions of C99. But
this provision hasn't changed since C90 at least; I don't have K&R1 to
check, and the original (Labs) implementation did use a Fortran-common
linker. (I don't think Labs Unix actually supported Fortran, but this
style was then mainstream.) And to nitpick, the "static" is in
typewriter font and the first "tentative definition" is in italic (not
bold) marking it as the defining occurrence of the term.

> > So add "int bar = 1;" to your foo.h and then compile fooget into a binary.

>
> But then two or more translation units within your program can see that
> definition, and you can get a "multiple definition" error.
>

To be exact multiple t.u.s can #include foo.h, and the declaration in
it, producing multiple definitions, which may or may not be diagnosed.

> Instead, add
>
> extern int foo;
>
> to "foo.h", and
>
> int foo = 1;
>
> to "foo.c". Then any translation unit that includes "foo.h" can use the
> object "foo", which is defined in exactly one place in your program.


Yes.

> (You could drop the "extern" in foo.h, making it a tentative definition,
> but adding "extern" is more explicit.)
>

No. Then you're back to multiple definitions = undefined.
Maybe you meant 'extern' is optional on the definition in in foo.c ?

> (I'm snipping some context in which you make some of these same points.)
>
> [snip]

 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      10-11-2012
David Thompson <(E-Mail Removed)> writes:
[...]
> *WG14* n1370 is not a version of the standard; is that an SC (or
> higher) document maybe? I generally use n869 (preadoption) or n1256
> (postcorrigenda) as the best and easily available versions of C99. But
> this provision hasn't changed since C90 at least; I don't have K&R1 to
> check, and the original (Labs) implementation did use a Fortran-common
> linker. (I don't think Labs Unix actually supported Fortran, but this
> style was then mainstream.) And to nitpick, the "static" is in
> typewriter font and the first "tentative definition" is in italic (not
> bold) marking it as the defining occurrence of the term.


Sorry, that was a typo; I meant n1570.

n869 is a pre-C89 draft. n1256 is a post-C99 draft which
incorporates the three Technical Corrigenda; it's actually better for
most purposes than the original (non-free) C99 standard. n1570 is
a pre-C11 draft; I'm not aware of any differences between it and
the offical C11 standard (other than page headers and so forth).
There's a small Technical Corrigendum to C11 to correct the
definition of __STDC_VERSION__ and one other macro.

[...]

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Who owns the variable in my header file ? Eric Sosman C Programming 46 12-19-2012 07:01 PM
Re: Who owns the variable in my header file ? James Kuyper C Programming 0 10-04-2012 12:43 PM
Re: Who owns the variable in my header file ? Ike Naar C Programming 0 10-03-2012 07:52 PM
Re: Who owns the variable in my header file ? Kaz Kylheku C Programming 0 10-03-2012 07:40 PM



Advertisments