By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,686 Members | 1,510 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,686 IT Pros & Developers. It's quick & easy.

External object definitions

P: n/a
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
Nov 13 '05 #1
Share this Question
Share on Google+
9 Replies


P: n/a
maxw_cc wrote:
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.

Nov 13 '05 #2

P: n/a
On 12 Aug 2003 23:50:28 -0700, ma*****@yahoo.com (maxw_cc) wrote in
comp.lang.c:
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
Nov 13 '05 #3

P: n/a
ma*****@yahoo.com (maxw_cc) wrote:

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.
--
f.a.n.finch <do*@dotat.at> http://dotat.at/
FAIR ISLE: VARIABLE BECOMING NORTHWESTERLY 3 OR 4 INCREASING 5 TO 7. RAIN THEN
SQUALLY SHOWERS. MODERATE BECOMING GOOD.
Nov 13 '05 #4

P: n/a
In message <Q9********************@comcast.com>, Douglas A. Gwyn
<DA****@null.net> writes
maxw_cc wrote:
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.


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.
--
ACCU Spring Conference 2003 April 2-5
The Conference you should not have missed
ACCU Spring Conference 2004 Late April
Francis Glassborow ACCU

Nov 13 '05 #5

P: n/a
In <bh**********@news-reader5.wanadoo.fr> "jacob navia" <ja*********@jacob.remcomp.fr> writes:
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
--
Dan Pop
DESY Zeuthen, RZ group
Email: Da*****@ifh.de
Nov 13 '05 #6

P: n/a
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.

--
Richard
Nov 13 '05 #7

P: n/a
Richard Delorme <ab****@nospam.fr> writes:

|> 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, SuSE Labs, sc****@suse.de
SuSE Linux AG, Deutschherrnstr. 15-19, D-90429 Nürnberg
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
Nov 13 '05 #8

P: n/a
Tony Finch <do*@dotat.at> wrote in message news:<Vo*******@news.chiark.greenend.org.uk>...
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
Nov 13 '05 #9

P: n/a
In article <bh**********@news-reader5.wanadoo.fr>, jacob navia
<ja*********@jacob.remcomp.fr> writes
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.

--
Clive D.W. Feather, writing for himself | Home: <cl***@davros.org>
Tel: +44 20 8371 1138 (work) | Web: <http://www.davros.org>
Fax: +44 870 051 9937 | Work: <cl***@demon.net>
Written on my laptop; please observe the Reply-To address
Nov 13 '05 #10

This discussion thread is closed

Replies have been disabled for this discussion.