External object definitions

M

maxw_cc

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
 
D

Douglas A. Gwyn

maxw_cc said:
Then I think that in this case I will be having,
*two definitions* for the same identifier with
external linkage!

No, the two identifiers designate the same object.
Default initialization is for the object, not for
each designation.
 
J

Jack Klein

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.

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.
To my surprise, I didn't receive any diagnostics
from the compiler. I compiled this program using
this command line:

The compiler by definition deals with one translation unit at a time.
$ 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

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
 
F

Francis Glassborow

Douglas A. Gwyn said:
No, the two identifiers designate the same object.
Default initialization is for the object, not for
each designation.

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.
 
T

Tony Finch

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.

As explained in another followup, this results in undefined behaviour.
To my surprise, I didn't receive any diagnostics
from the compiler.

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.
 
D

Dan Pop

In said:
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

It's undefined behaviour, so why do you expect a diagnostic? Besides,
-pedantic has nothing to do with the linking stage.
In my linker, I added a warning, but still left this as is. I am still wandering
why this behavior is continued.

Should I discontinue this? Should I give an error rather than just
a warning???

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).
P.S. All this happens only with bss variables of course. If you mix
bss with initialized data variables the behavior is even stranger!

Undefined behaviour can never be too strange ;-)

Dan
 
R

Richard Delorme

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.
In my linker, I added a warning, but still left this as is. I am still
wandering why this behavior is continued.

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.
 
A

Andreas Schwab

|> 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.
 
M

maxw_cc

Tony Finch said:
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.


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
 
C

Clive D. W. Feather

jacob navia said:
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

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.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top