In article <2d**********************************@25g2000hsx.g ooglegroups.com>
whirlwindkevin <ma*******@gmail.comwrote:
[regarding two separate .c files, both with "int g;" with no
initializer for either one, that are compiled separately and
then linked]
>whirlwindkevin said:
>>I saw a program source code in which a variable is defined in a header
file and that header file is included in 2 different C files.
>On Jun 9, 12:27 pm, Richard Heathfield <r...@see.sig.invalidwrote:
>And it didn't compile, obviously.
Well, actually, it did (for him). But as you note:
>>int g;
This is not *exactly* a definition. It's a "tentative definition", and it's
one of the silliest ideas in C - a silly feature of the language, and
silly of the programmer to exploit the feature.
Tentative definitions are actually useful in a few corner cases,
such as creating a circularly linked list at compile time that
has several elements:
struct foolist {
struct foolist *next, *prev;
... more data here ...
};
static struct foolist foo_head, foo_elem_2, foo_tail;
static struct foo_list foo_head = { &foo_elem_2, &foo_tail, ... };
static struct foo_list foo_elem_2 = { &foo_tail, &foo_head, ... };
static struct foo_list foo_tail = { &foo_head, &foo_elem_2, ... };
In C, you must use tentative definitions to make this work. (For
non-"static" variables, which have external linkage, you can use
"extern" to avoid creating tentative definitions.)
>"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."
However, as one can see if one has an implementation like the OP's,
this is not quite true:
% cat a.define.c
int x = 0;
% cat b.define.c
int x = 0;
int main(void) { return 0; }
% cc -o define a.define.c b.define.c
/tmp/ccodx0za.o(.data+0x0): multiple definition of `x'
/tmp/ccV8U2Fi.o(.data+0x0): first defined here
% cat a.tent.c
int x;
% cat b.tent.c
int x;
int main(void) { return 0; }
% cc -o tent a.tent.c b.tent.c
%
So, we now have to ask: is this implementation broken? The behavior
differs for tentative definitions and actual (non-tentative)
definitions, despite the above quote from the C standard.
The answer is no, because what the Standard giveth at this point,
the Standard taketh away elsewhere:
K.2 Undefined behavior
[#1] The behavior is undefined in the following circumstances:
[massive snippage]
- An identifier with external linkage is used, but in the
program there does not exist exactly one external definition
for the identifier, or the identifier is not used and there
exist multiple external definitions for the identifier.
(6.7).
In other words, the implementation can do anything it wants if you
define the same identifier twice (or any other number of times than
1 or 0, with "defined 0 times" being OK only for one particular
case).
The OP's implementation, like mine above, uses this "undefined
behavior" license to provide a "feature" of sorts: tentative
definitions are turned into actual definitions, but not at the end
of each translation unit. Instead, the compiler waits until the
last possible moment -- the "link" phase of compilation -- and only
*then* turns all remaining tentative definitions into actual
definitions. In the process, it saves a bit of disk space. This
feature also lets lazy programmers leave out the keyword "extern".
>Many Thanks Richard for replying...
Still i have a doubt related to definition of the variable.Can you
please tell where variable g (variable in the above program) is
defined (space is allocated for the variable)? Is it in a.c or is it
in b.c?
According to the Standard, it is in both. However, your implementation
(like mine) makes use of the undefined behavior to cause it to
happen in *neither*. It happens in a third, "invisible" source
file that your compiler makes up at the last possible moment. (In
my implementation, this is not even an actual file at all, although
some compilers do use an extra file, and even a program called
"collect2", to handle some tricky cases, particularly when mixing
C with other languages. If your linker is smart enough, it can
create a sort of "pretend" file purely in RAM, and avoid the
"collect2" step. This job is a lot easier for pure C programs; it
is only those other langauges that create the need for "collect2".)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out)
http://web.torek.net/torek/index.html