External object definitions

Discussion in 'C Programming' started by maxw_cc, Aug 13, 2003.

  1. maxw_cc

    maxw_cc Guest

    Hi to all,

    To explain my question I'll help myself
    with the following two code snippets:

    /*** file1.c ***/

    #include <stdio.h>

    void print_it(void);
    int i;

    int main(void)
    {
    print_it();
    printf("main i = %d\n", i);

    return 0;
    }

    /*** file2.c ***/

    #include <stdio.h>

    int i;

    void print_it(void)
    {
    i = 2;
    printf("print_it i = %d\n", i);
    }


    As you can see I have a
    tentative definition for object `i',
    in both TUs.
    According to C99 6.9.2 p2, one can deduce
    that both of these tentative definitions, will
    turn into real definitions at the end of the
    TUs with initializer zero.

    Then I think that in this case I will be having,
    *two definitions* for the same identifier with
    external linkage!
    Then I would be violating a semantics constraint.
    Specifically the one pointed out in C99 6.9 p5.

    To my surprise, I didn't receive any diagnostics
    from the compiler. I compiled this program using
    this command line:

    $ gcc -g -Wall -ansi -pedantic -o filex file1.c file2.c

    I even ran the executable generated, and it ran OK:
    $ filex
    print_it i = 2
    main i = 2

    What am I getting wrong?
    What is my error?
    What am I misunderstanding here?

    Thank you very much in advance,

    Max
     
    maxw_cc, Aug 13, 2003
    #1
    1. Advertisements

  2. No, the two identifiers designate the same object.
    Default initialization is for the object, not for
    each designation.
     
    Douglas A. Gwyn, Aug 13, 2003
    #2
    1. Advertisements

  3. maxw_cc

    Jack Klein Guest

    There is no such thing as a "semantics constraint". The constraints
    in 6.9 are paragraphs 2 and 3. Paragraphs 4 and 5 are in the
    semantics section, and are not constraints.
    The compiler by definition deals with one translation unit at a time.
    When your separately compiled translation units are linked together by
    your linker, you are indeed violating the terms of 6.9 p5.

    Specifically the wording "If an identifier declared with external
    linkage is used in an expression (other than as part of the operand of
    a sizeof operator whose result is an integer constant), somewhere in
    the entire program there shall be exactly one external definition for
    the identifier; otherwise, there shall be no more than one."

    But this is not a constraint violation, because it is not in a
    constraints section, but a semantics one.

    So what applies here is p2 of section 4:

    "If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a
    constraint is violated, the behavior is undefined."

    Your program invokes undefined behavior, which requires neither a
    diagnostic nor any particular results, as far as the standard is
    concerned.

    Different languages have different conceptual models of what C calls
    external linkage. Some of them require the behavior that you see,
    where there can be multiple definitions of an external symbol so long
    as no more than one of them contain an initializer. FORTRAN named
    common blocks need this, for example. So the linkers of some tools
    work this way. Essentially they pass the concept of tentative
    definition on to the linker.

    C allows but does not require this feature by making the results of
    such a program undefined.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c++-faq-lite/
    alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
     
    Jack Klein, Aug 13, 2003
    #3
  4. This is one of the areas of C that has always left me deeply
    uncomfortable. As I understand it, the programmer who decides to provide
    explicit initialisation transforms the two declarations into two
    distinct objects. I have always thought that the idea is deeply flawed.
    It breaks the general description that variables are, by default,
    defined and pure declarations need to be made using extern.
     
    Francis Glassborow, Aug 13, 2003
    #4
  5. maxw_cc

    Tony Finch Guest

    As explained in another followup, this results in undefined behaviour.
    This is one of the "common implementation extensions", see J.5.11
    "multiple external definitions":

    [#1] There may be more than one external definition for the
    identifier of an object, with or without the explicit use of
    the keyword extern; if the definitions disagree, or more
    than one is initialized, the behavior is undefined (6.9.2).

    Tony.
     
    Tony Finch, Aug 13, 2003
    #5
  6. maxw_cc

    Dan Pop Guest

    It's undefined behaviour, so why do you expect a diagnostic? Besides,
    -pedantic has nothing to do with the linking stage.
    The C standard is giving you free hand. A diagnostic is certainly
    helpful, it doesn't really matter whether it's an error or a warning
    (any user ignoring such a warning gets exactly what he deserves).
    Undefined behaviour can never be too strange ;-)

    Dan
     
    Dan Pop, Aug 13, 2003
    #6
  7. jacob navia a écrit :
    -Wl,--warn-common is the option that gives a warning with gcc, for this kind
    of code.
    It seems to be a common practice in the unix world. According to ld manual :
    --warn-common
    Warn when a common symbol is combined with another common symbol
    or with a symbol definition. Unix linkers allow this somewhat
    sloppy practice, but linkers on some other operating systems do
    not. This option allows you to find potential problems from com-
    bining global symbols. Unfortunately, some C libraries use this
    practice, so you may get some warnings about symbols in the
    libraries as well as in your programs.
     
    Richard Delorme, Aug 13, 2003
    #7
  8. |> jacob navia a écrit :
    |>
    |> > When I wrote the linker of lcc-win32 I was confronted to this problem.
    |> > All linkers have this quite uncredible behavior:
    |> >
    |> > File1.c
    |> > int table[2];
    |> >
    |> > File2.c
    |> > int table[834];
    |> > int main(void { }
    |> >
    |> > This will link without any warnings, even with
    |> >
    |> > gcc -pedantic f1.c 2.c
    |>
    |> -Wl,--warn-common is the option that gives a warning with gcc, for this kind
    |> of code.

    Another option is to compile with -fno-common, which will cause the
    linker to error out on the multiple definitions of table.

    Andreas.
     
    Andreas Schwab, Aug 13, 2003
    #8
  9. maxw_cc

    maxw_cc Guest


    First of all thanks to all of you for your good inputs.
    Especially thank you very much to Jack Klein and Tony
    Finch, you've been of great help.

    However, I still wonder why they put 6.9.2 in the last
    sentence of J.5.11 p1. I see this as if they were telling me
    to look at 6.9.2 to find justifications on why having discrepant
    definitions or having multiple external definitions (not tentative)
    invoke UB. It would have made more sense to me if they
    had put 6.9 and/or probably 6.2.2.

    Thanks again for all your help,

    Max
     
    maxw_cc, Aug 14, 2003
    #9
  10. This dates back to early versions of Fortran (my X3.9-1966 isn't to
    hand, so I can't say if it was standardised or not), where you could
    write:

    COMMON /TABLE/ T(2)

    in one subroutine and:

    COMMON /TABLE/ T(834)

    in another. The linker would assign enough memory for the largest
    version of each common block.
     
    Clive D. W. Feather, Aug 28, 2003
    #10
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.