Re: Who owns the variable in my header file ?

Discussion in 'C Programming' started by Edward A. Falk, Oct 3, 2012.

  1. In article <>,
    lipska the kat <> 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://thespamdiaries.blogspot.com/
    Edward A. Falk, Oct 3, 2012
    #1
    1. Advertising

  2. (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) <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"
    Keith Thompson, Oct 4, 2012
    #2
    1. Advertising

  3. Edward A. Falk

    Kaz Kylheku Guest

    On 2012-10-03, Edward A. Falk <> wrote:
    > In article <>,
    > lipska the kat <> 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).
    Kaz Kylheku, Oct 4, 2012
    #3
  4. In article <>,
    Keith Thompson <> 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,
    http://thespamdiaries.blogspot.com/
    Edward A. Falk, Oct 4, 2012
    #4
  5. On Wed, 03 Oct 2012 16:21:37 -0700, Keith Thompson <>
    wrote:

    > (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]
    David Thompson, Oct 11, 2012
    #5
  6. David Thompson <> 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) <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"
    Keith Thompson, Oct 11, 2012
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric Sosman

    Re: Who owns the variable in my header file ?

    Eric Sosman, Oct 3, 2012, in forum: C Programming
    Replies:
    46
    Views:
    1,075
    Edward A. Falk
    Dec 19, 2012
  2. Kaz Kylheku

    Re: Who owns the variable in my header file ?

    Kaz Kylheku, Oct 3, 2012, in forum: C Programming
    Replies:
    0
    Views:
    369
    Kaz Kylheku
    Oct 3, 2012
  3. Ike Naar

    Re: Who owns the variable in my header file ?

    Ike Naar, Oct 3, 2012, in forum: C Programming
    Replies:
    0
    Views:
    384
    Ike Naar
    Oct 3, 2012
  4. James Kuyper

    Re: Who owns the variable in my header file ?

    James Kuyper, Oct 4, 2012, in forum: C Programming
    Replies:
    0
    Views:
    300
    James Kuyper
    Oct 4, 2012
  5. Eric Sosman

    Re: Who owns the variable in my header file ?

    Eric Sosman, Oct 4, 2012, in forum: C Programming
    Replies:
    0
    Views:
    342
    Eric Sosman
    Oct 4, 2012
Loading...

Share This Page