Discussion:
"extern" specifier ?
(too old to reply)
Robby
2009-12-02 17:25:01 UTC
Permalink
Hello,

I am in the process of porting a large C program from a non- ansi C
compilant embedded compiler to an ansi C compliant emebedded compiler. I see
that the rules are not quite the same.

I am reviewing some specifics of extern in a book called "Teach yourself C"
3rd edition from Herbert Schildt and there is a short sample code on p.339
which explains the use of the extern specifier. Here is the sample code:

================================F1.c
#include <stdio.h>

int count;
void f1();

int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================

============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
===============================

According to the book, the above example is not supposed to work as it
explains on quote:

"If you declare count a second time, many linkers will report a duplicate
symbol error, which means that count is defined twice and the linker doesn't
know which to use."

Now, I tried this in VC++ as a simple .c program as shown above and I get no
errors or warnings and VC++ promts a build succeeded in the output box ???

But the book goes on to suggest that the correct way to do this is by using
the extern specifier in F2.c like this:

============================F2.c
#include <stdlib.h>
extern int count;
void f1(void)
{ count = rand(); }
===============================

This also compiles without errors or warnings. But why does the original
sample code (F1.c and F2.c) still work when the book says I should get an
duplicate symbol error?

All help appreciated! Excuse the simple question!
--
Best regards
Roberto
Igor Tandetnik
2009-12-02 18:25:28 UTC
Permalink
Post by Robby
================================F1.c
#include <stdio.h>
int count;
void f1();
int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================
============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
===============================
According to the book, the above example is not supposed to work as it
"If you declare count a second time, many linkers will report a
duplicate symbol error, which means that count is defined twice and
the linker doesn't know which to use."
Now, I tried this in VC++ as a simple .c program as shown above and I
get no errors or warnings and VC++ promts a build succeeded in the
output box ???
Looks like a bug to me. The program is in fact invalid, and the linker should have complained.

6.9p5 An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. 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.

6.9.2p1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

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.


In your example, "int count;" is a tentative definition, and should behave the same as "int count=0;" (in both source files), which is an external definition, of which there must be exactly one in the entire program, but the example has two. Interestingly, if you say "int count=0;" explicitly in both places, then the linker does complain.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-02 21:54:43 UTC
Permalink
Hello,
Post by Igor Tandetnik
6.9.2p1 If the declaration of an identifier for an object has file
scope and an initializer, the declaration is an external definition
for the identifier.
There is no initializer.
Post by Igor Tandetnik
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,
Contains no external definitions, thus it is not external definition.
Tentative def != external def.
Post by Igor Tandetnik
In your example, "int count;" is a tentative definition, and should
behave the same as "int count=0;" (in both source files), which is an
external definition, of which there must be exactly one in the
entire program, but the example has two.
Yes - behave the same as "int count=0;", but it still does not contain
an external definition - it is a tentative one. The same behavior,
different name. Translator should behave in the same way, thus it should
define object count of type int with initial value 0. But now its name
is tentative definition. But we still do not know if ,,int count;'' is
forward declaration or definition, as it has external linkage. With
,,static count;'' ... ,,static count = 0;'' there is no such problem as
it has internal linkage and a translator can define storage for count at
the end of translation.

OK - let's look at the rationale
Post by Igor Tandetnik
6.9.2 External object definitions
Prior to C90, implementations varied widely with regard to forward
referencing identifiers with internal linkage (see §6.2.2). The C89
committee invented the concept of tentative definition to handle this
situation. A tentative definition is a declaration that may or may
not act as a definition: If an actual definition is found later in
the translation unit, then the tentative definition just acts as a
declaration. If not, then the tentative definition acts as an actual
definition.
For the sake of consistency, the same rules apply to identifiers with
external linkage, although they're not strictly necessary.
A. Translator takes:

,,static int count;''

Hmm... is it fwd decl or def?

... and:

case1:
,,static int count = 5;''

Ohh... there was fwd decl. Now I know that I must allocate
storage and init it with value 5.

case2:

<<EOF>>

Ohh... there was def. Now I know that I must allocate
storage and init it with value 0.

B. Translator takes:

,,int count;''

Hmm... is it fwd decl or def?

... and:

case1:
,,int count = 5;''

Ohh... there was fwd decl. Now I know that I must allocate
storage and init it with value 5.

case2:

<<EOF>>

Ohh... still I do not know. Now I'm allocating storage and
initing it with value 0 (maybe there was def). But I cannot
name it ,,extern def'' (maybe there was fwd decl). So let me
name it ,,tent def'' and let the linker worry.


The std is not very clear in case of externs. Igor, your sentence
,,should behave the same as "int count=0;", which is an external
definition'' can be correct as well. Book and MSVC - both could be
correct. It looks that ending words of rationale: ,,they're not strictly
necessary'' resulted in ambiguity of the std. However, IMHO, Crtl-C,
Ctrl-V from the rationale to the std would be far better.

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-02 23:02:54 UTC
Permalink
Post by Robby
Hello,
Post by Igor Tandetnik
6.9.2p1 If the declaration of an identifier for an object has file
scope and an initializer, the declaration is an external definition
for the identifier.
There is no initializer.
Post by Igor Tandetnik
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,
Contains no external definitions, thus it is not external definition.
Tentative def != external def.
Curious how you chose to snip precisely the part inconvenient for your argument. Let me put it back:

"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*" (emphasis mine).

The behavior is exactly as if there's a declaraion with an initializer == the behavior is per 6.9.2p1
Post by Robby
Post by Igor Tandetnik
In your example, "int count;" is a tentative definition, and should
behave the same as "int count=0;" (in both source files), which is an
external definition, of which there must be exactly one in the
entire program, but the example has two.
Yes - behave the same as "int count=0;", but it still does not contain
an external definition
We must have different definitions of "exactly". Mine doesn't involve the word "but". What's yours?
Post by Robby
OK - let's look at the rationale
Post by Igor Tandetnik
6.9.2 External object definitions
Prior to C90, implementations varied widely with regard to forward
referencing identifiers with internal linkage (see §6.2.2). The C89
committee invented the concept of tentative definition to handle this
situation. A tentative definition is a declaration that may or may
not act as a definition: If an actual definition is found later in
the translation unit, then the tentative definition just acts as a
declaration. If not, then the tentative definition acts as an actual
definition.
So, which part of "If not [that is, if there's no actual definition by the end of translation unit] then the tentative definition acts as an actual definition" supports your argument?
Post by Robby
,,int count;''
Hmm... is it fwd decl or def?
,,int count = 5;''
Ohh... there was fwd decl. Now I know that I must allocate
storage and init it with value 5.
<<EOF>>
Ohh... still I do not know.
... but the standard requires me to act exactly as if I've seen "int count = 0;"
Post by Robby
The std is not very clear in case of externs.
It seems clear enough to me.
Post by Robby
Igor, your sentence
,,should behave the same as "int count=0;", which is an external
definition'' can be correct as well. Book and MSVC - both could be
correct. It looks that ending words of rationale: ,,they're not
strictly necessary'' resulted in ambiguity of the std.
I don't understand this last claim. First, the rationale is non-normative. Second, the standard is 13 years older: how can the rationale, published years later, go back and create an ambiguity in the standard where none existed before? Third, you seem to assign too much significance to this phrase. The rationale appears to be saying: there was a lot of variance in existing (pre-standard) compilers with respect to forward declarations of static variables (those with internal linkage). We came up with this elaborate scheme to make those pre-standard programs work. There was no such variance for identifiers with external linkage, but we decided to apply the same wording for consistency.

If you want to hold the rationale as an authoritative document, then please look at section 6.2.2 - it's rather devastating to your argument:

"The Standard model is a combination of features of the strict ref/def model and the initialization model. As in the strict ref/def model, only a single translation unit contains the definition of a given object because many environments cannot effectively or efficiently support the “distributed definition” inherent in the common or relaxed ref/def approaches. However, either an initialization, or *an appropriate declaration without storage class specifier* (see §6.9), serves as the external definition." (emphasis mine)
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-03 03:22:58 UTC
Permalink
Post by Igor Tandetnik
I don't understand this last claim. First, the rationale is
non-normative. Second, the standard is 13 years older: how can the
rationale, published years later, go back and create an ambiguity in
the standard where none existed before? Third, you seem to assign
too much significance to this phrase. The rationale appears to be
saying: there was a lot of variance in existing (pre-standard)
compilers with respect to forward declarations of static variables
(those with internal linkage). We came up with this elaborate scheme
to make those pre-standard programs work. There was no such variance
for identifiers with external linkage, but we decided to apply the
same wording for consistency.
First, OK - it is not normative, but it is helpful when trying to answer
the question ,,why some compiler work in such a way that...''. Second,
Why are you assuming that published rationale (paper?) develops
standards? People developing the standard need not read the publication
to know their rationale. I do not know what could published rationale go
back in time for?
Post by Igor Tandetnik
If you want to hold the rationale as an authoritative document, then
please look at section 6.2.2 - it's rather devastating to your
"The Standard model is a combination of features of the strict
ref/def model and the initialization model. As in the strict ref/def
model, only a single translation unit contains the definition of a
given object because many environments cannot effectively or
efficiently support the “distributed definition” inherent in the
common or relaxed ref/def approaches. However, either an
initialization, or *an appropriate declaration without storage class
specifier* (see §6.9), serves as the external definition." (emphasis
mine)
Third, I do not know why are you referring to the rationale? It is
non-normative, cannot go back in time, it is non authoritative, but I
can hold it as an authoritative doc at a pinch. The condition is that I
should look at the carefully selected sections and sentences.
Post by Igor Tandetnik
Curious how you chose to snip precisely the part inconvenient for
your argument.
I can snip precisely inconvenient parts - you can not-less-precisely
point convenient ones out. Everybody has some abilities ;))

To be serious, certainly, you are right if about the standard and
strictness. However the question was ,,Why does MSVC compile without
errors?''. In your opinion it is a bug, IMO it is a feature. UDB can
work well in a documented manner without any messages. MSVC and LINK use
nearly least restrictive model - I've described it in a table in my post
- that's all.

|TENTs|DEF1 |DECLs
-----+-----+-----+-----
TENTs| OK | OK | OK
-----+-----+-----+-----
DEF1 | OK | ERR | OK
-----+-----+-----+-----
DECLs| OK | OK | ERR

TENTs + DEF1 + DECLs == OK // all of them

TENT: T v; // 1 or more per program
DEF: [extern] T v = i; // at most one per program
DECL: extern T v; // 1 or more per program
Post by Igor Tandetnik
Interestingly, if you say "int count=0;" explicitly in both places,
then the linker does complain.
Simply, it is a case DEF/DEF in the middle of the table. Tentative
definitions with external linkage are resolved by the linker.

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-03 04:41:01 UTC
Permalink
Post by Cezary H. Noweta
Post by Igor Tandetnik
I don't understand this last claim. First, the rationale is
non-normative. Second, the standard is 13 years older: how can the
rationale, published years later, go back and create an ambiguity in
the standard where none existed before? Third, you seem to assign
too much significance to this phrase. The rationale appears to be
saying: there was a lot of variance in existing (pre-standard)
compilers with respect to forward declarations of static variables
(those with internal linkage). We came up with this elaborate scheme
to make those pre-standard programs work. There was no such variance
for identifiers with external linkage, but we decided to apply the
same wording for consistency.
First, OK - it is not normative, but it is helpful when trying to answer
the question ,,why some compiler work in such a way that...''. Second,
Why are you assuming that published rationale (paper?) develops
standards? People developing the standard need not read the publication
to know their rationale. I do not know what could published rationale go
back in time for?
You said, and I quote: "It looks that ending words of rationale ... resulted in ambiguity of the std." The only way I can read this sentence is as suggesting that "ending words of rationale" is the cause, and "ambiguity of the std" is the effect. However, having the effect predate the cause would violate the principle of causality. We arrive at a contradiction. QED.
Post by Cezary H. Noweta
Post by Igor Tandetnik
If you want to hold the rationale as an authoritative document, then
please look at section 6.2.2 - it's rather devastating to your
"The Standard model is a combination of features of the strict
ref/def model and the initialization model. As in the strict ref/def
model, only a single translation unit contains the definition of a
given object because many environments cannot effectively or
efficiently support the “distributed definition” inherent in the
common or relaxed ref/def approaches. However, either an
initialization, or *an appropriate declaration without storage class
specifier* (see §6.9), serves as the external definition." (emphasis
mine)
Third, I do not know why are you referring to the rationale?
Because you brought it up, of course.
Post by Cezary H. Noweta
It is
non-normative, cannot go back in time, it is non authoritative, but I
can hold it as an authoritative doc at a pinch. The condition is that I
should look at the carefully selected sections and sentences.
That's precisely what you did, yes. I'm glad we are in agreement. Now, I wonder if you could find some text, either in the standard or in the rationale, that actually supports your case. Even those sections and sentences that you carefully selected appear to argue against your position.
Post by Cezary H. Noweta
Post by Igor Tandetnik
Curious how you chose to snip precisely the part inconvenient for
your argument.
I can snip precisely inconvenient parts - you can not-less-precisely
point convenient ones out. Everybody has some abilities ;))
I'm deeply honored by your recognition of my debating skills. After all, this is how a debate is normally conducted: one side cites references in support of its position, the other side is supposed to debunk them and in turn cite references in support of the opposing position, and so on, back and forth. What else did you expect?
Post by Cezary H. Noweta
To be serious, certainly, you are right if about the standard and
strictness. However the question was ,,Why does MSVC compile without
errors?''. In your opinion it is a bug, IMO it is a feature.
Yes, whether to call it a bug or an extension is a matter of opinion, as I've already stated elsewhere in this thread. This is the case whenever a compiler accepts an invalid program (as opposed to rejecting a valid one). Either way, if one cares about writing portable programs, one probably shouldn't rely on this {feature, extension, bug: choose one}. It's easy to do it right - why insist on doing it wrong?
Post by Cezary H. Noweta
UDB can work well
Out of curiosity - what's UDB? I'm not familiar with this particular TLA. None of the definitions on acronymfinder.com seem fitting.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-04 13:39:27 UTC
Permalink
Hello,
Post by Igor Tandetnik
Post by Cezary H. Noweta
To be serious, certainly, you are right if about the standard and
strictness. However the question was ,,Why does MSVC compile
without errors?''. In your opinion it is a bug, IMO it is a
feature.
Yes, whether to call it a bug or an extension is a matter of opinion,
as I've already stated elsewhere in this thread. This is the case
whenever a compiler accepts an invalid program (as opposed to
rejecting a valid one). Either way, if one cares about writing
portable programs, one probably shouldn't rely on this {feature,
extension, bug: choose one}. It's easy to do it right - why insist on
doing it wrong?
[Below is a few of my ,,blah, blah''s, so if you are interested in
merits of the case, then read p.6. only, please.]

1. I'm not insisting on that! (on writing non-portable code) I'm not a
devil's advocate nor a non-portable code's advocate. My belief is: write
a portable code, especially when there is no overhead. This is one of my
primary axioms. If you want to have an army to kick non-portable code
out, then you can count and rely on me.

2. By writing ,,bug'' you gave (if not then correct me) the following:
,,it is a more-or-less random accident with unpredictable results;
delete/rewrite that code, until you want to have a nuclear launch on
your desk''.

3. I answered the question (,,Why MSVC does not crash?'') by calling
this ,,feature, extension'', and providing rules which allow to predict
behavior. Not so fortuitously mixing it with the standard and discussion
about a rationale of the standard. OK - this is at the last point and
has nothing common with Robby's question.

4. According to the standard it is UDB and (according to 1.) it is the
best to avoid this. Looking literally at the standard and kicking all
rationale, common and canonical meanings of declarations and so on out
of mind, then it is clear. Thank you for catching my eye on this. *If
you can, then avoid this extension and explicitly point the place of
declaration and definition* - this is my relief.
Post by Igor Tandetnik
Post by Cezary H. Noweta
UDB can work well
Out of curiosity - what's UDB? I'm not familiar with this particular
TLA. None of the definitions on acronymfinder.com seem fitting.
5. Why? I do not understand your point of view. Which one of the
definitions prohibits the compiler/linker from working well? You are
looking too far. Look at the standard. Definition of UDB - note
3.4.3[2]: ,,Possible undefined behavior ranges from ignoring the
situation completely with unpredictable results, to behaving during
translation or program execution in a documented manner characteristic
of the environment (with or without the issuance of a diagnostic
message), to terminating a translation or execution (with the issuance
of a diagnostic message).'' The compiler/linker _can_ or _it is possible
for the compiler/linker to_ (not ,,must'', nor ,,should'', nor ,,need'')
work well (with strictly predictable and documented results).

6. There is no need to flame MS' linker for such behavior in this
particular case. As James wrote this is not so uncommon feature today
(though still being UDF). The real problem arrives when there is ,,char
count;'' in one unit, and ,,int count[0x1000];'' in other one.
Presumably (if not for sure) they are not the same variables if their
sizes are different. At least, in such case, the linker should give a
warning that there are commons which differ on sizes. Hopefully the
linker chooses a bigger common and not that one found as the first.

7. OK - ,,for the sake of consistency''. I'm not sure if a consistency
of the standard is not violated far better then leaving tentative
definitions for internal linkage external identifiers only. For internal
linkage ids it is obvious: there cannot be ,,external static int a;'' so
tentative definitions are introducing declarations of statics, without
defining it.

If about external linkage, then it is required that programmer should
ensure exactly one definition among units (all but one declarations
should be ,,extern'' without an initializer), but, on the other hand, it
is still possible (looking from perspective of one unit) to the
programmer being not sure if he is just writing declaration or
definition. What for? Thanks to ,,extern'', there are no ambiguities.

It is like saying: ,,you can be crazy but in a scope of one unit only -
when you go out of this unit, then you must remember to put ,,extern''
in all other units''.

If keeping a good structure of sources (one .h with ,,extern'', and one
one .c with definition) then tentatives are unnecessary. Otherwise, they
allow for a deeper rat's nest. So let's do a complete pig's breakfast
allowing commons among units or let's cut it down a bit by a strict
definition: no ,,extern'' == external definition - not the sole
declaration. That was my point.

Above is in a today's perspective. Certainly, I see that tentatives were
born years ago. Seeing the standard in that one perspective (considering
machines, compilers, software in C, a plurality of various definition
models) it was a good (if not the best) solution to ensure an optimal
balance between a portability, a flexibility and existing
implementations which had been already written in C.

--
Post by Igor Tandetnik
Post by Cezary H. Noweta
Post by Igor Tandetnik
Curious how you chose to snip precisely the part inconvenient for
your argument.
I can snip precisely inconvenient parts - you can
not-less-precisely point convenient ones out. Everybody has some
abilities ;))
I'm deeply honored by your recognition of my debating skills. After
all, this is how a debate is normally conducted: one side cites
references in support of its position, the other side is supposed to
debunk them and in turn cite references in support of the opposing
position, and so on, back and forth. What else did you expect?
Nothing. You placed your recognition of my debating skills then I have
placed my recognition of yours. That's all. Besides that, as we are
operating on finite sets == (inconvenient parts [IP] + convenient parts
[CP]), then snipping of inconvenient ones == citation of convenient
ones, as [IP] == ~[CP], so there is no matter if you are calling ,,to
snip precisely the part inconvenient for your argument'' (if me) or
,,cite references in support of the opposing position'' (if you). ;)

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-04 14:37:39 UTC
Permalink
Post by Cezary H. Noweta
,,it is a more-or-less random accident with unpredictable results;
delete/rewrite that code, until you want to have a nuclear launch on
your desk''.
No, by "bug" I mean "failure to follow specification".

Say, when you use a bug tracking system, do you only file critical (nuclear-launch-level) bugs there?
Post by Cezary H. Noweta
Post by Igor Tandetnik
Post by Cezary H. Noweta
UDB can work well
Out of curiosity - what's UDB? I'm not familiar with this particular
TLA. None of the definitions on acronymfinder.com seem fitting.
5. Why? I do not understand your point of view.
I just didn't realize that UDB stands for undefined behavior. I've never before seen it shortened this way. (TLA, by the way, stands for "three-letter acronym" and usually refers sarcastically to the other party's use of acronyms not in common practice, or overuse of acronyms in general).
Post by Cezary H. Noweta
6. There is no need to flame MS' linker for such behavior in this
particular case. As James wrote this is not so uncommon feature today
(though still being UDF).
And while we are on the topic of TLAs - what's UDF? I'm pretty sure you don't mean "user-defined function".
Post by Cezary H. Noweta
The real problem arrives when there is ,,char
count;'' in one unit, and ,,int count[0x1000];'' in other one.
Presumably (if not for sure) they are not the same variables if their
sizes are different.
Are you suggesting that having "int count;" in one unit, and "char count[4];" in the other, is perfectly OK? The program is clearly meaningless, even if the two declarations give the variable the same size.
Post by Cezary H. Noweta
If about external linkage, then it is required that programmer should
ensure exactly one definition among units (all but one declarations
should be ,,extern'' without an initializer), but, on the other hand, it
is still possible (looking from perspective of one unit) to the
programmer being not sure if he is just writing declaration or
definition. What for? Thanks to ,,extern'', there are no ambiguities.
It is like saying: ,,you can be crazy but in a scope of one unit only -
when you go out of this unit, then you must remember to put ,,extern''
in all other units''.
Isn't this exactly what the standard specifies, and MSVC fails to implement? You know, the part where the concept of tentative definition only exists until the end of translation unit, at which point any such declaration becomes external definition subject to one definition rule. So, which side are you arguing? You lost me here.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-10 16:40:51 UTC
Permalink
Hello,
Post by Igor Tandetnik
The real problem arrives when there is ,,char count;'' in one unit,
and ,,int count[0x1000];'' in other one. Presumably (if not for
sure) they are not the same variables if their sizes are different.
Are you suggesting that having "int count;" in one unit, and "char
count[4];" in the other, is perfectly OK? The program is clearly
meaningless, even if the two declarations give the variable the same
size.
Assuming that we want to keep permission to have the same identifier in
more then one object file, then an implementation of a warning when the
sizes are different would be nearly no-cost operation. BTW. ,,int a;'',
,,char a;'' is permitted even in the same file - it causes warning only
and both variables occupy the same storage in memory (like common def
among object files).
Post by Igor Tandetnik
If about external linkage, then it is required that programmer
should ensure exactly one definition among units (all but one
declarations should be ,,extern'' without an initializer), but, on
the other hand, it is still possible (looking from perspective of
one unit) to the programmer being not sure if he is just writing
declaration or definition. What for? Thanks to ,,extern'', there
are no ambiguities.
It is like saying: ,,you can be crazy but in a scope of one unit
only - when you go out of this unit, then you must remember to put
,,extern'' in all other units''.
Isn't this exactly what the standard specifies, and MSVC fails to
implement? You know, the part where the concept of tentative
definition only exists until the end of translation unit, at which
point any such declaration becomes external definition subject to one
definition rule. So, which side are you arguing? You lost me here.
In your opinion, is such duality pro the current state of the standard?
The question is: ,,Should I be aware of multiple external linkage
external declarations (ELEDs) for the same identifier without ,,extern''
specifier, and without an initializer?''. Now, the answer is beginning
with ,,It depends...''. I would like the answer ,,yes'' or ,,no'', like
in case of internal linkage external declarations (ILEDs) for an
identifier without an initializer.

By an analogy between ELEDs and ILEDs, in case of ELEDs, current state
of the standard is like permission of multiple ,,static int a;'', but in
one source file only, and saying that ,,#include''-ing of a file with
the same declaration for ,,a'' is UDB.

I am arguing that the current behavior of MSVC is better then a
sterilisation of the linker and prohibiting it from generating of an
EXE. Though: (1) such cases should be avoided; (2) a warning would be
useful.

Regarding MS' resources then if I had to choose between C's
typeinfo in objects (or some other solution taking any direction) and
MSVC's preprocessor working well, I'd choose the latter option.

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-10 17:03:19 UTC
Permalink
BTW. ,,int a;'', ,,char a;'' is permitted even in the same file
You are pulling my leg, aren't you? You can't possibly say such nonsense with a straight face, can you?

Have you actually tried this? I am - quite properly - getting error C2371: 'a' : redefinition; different basic types.
Post by Igor Tandetnik
Post by Cezary H. Noweta
If about external linkage, then it is required that programmer
should ensure exactly one definition among units (all but one
declarations should be ,,extern'' without an initializer), but, on
the other hand, it is still possible (looking from perspective of
one unit) to the programmer being not sure if he is just writing
declaration or definition. What for? Thanks to ,,extern'', there
are no ambiguities.
It is like saying: ,,you can be crazy but in a scope of one unit
only - when you go out of this unit, then you must remember to put
,,extern'' in all other units''.
Isn't this exactly what the standard specifies, and MSVC fails to
implement? You know, the part where the concept of tentative
definition only exists until the end of translation unit, at which
point any such declaration becomes external definition subject to one
definition rule. So, which side are you arguing? You lost me here.
In your opinion, is such duality pro the current state of the
standard?
What duality?
The question is: ,,Should I be aware of multiple external
linkage external declarations (ELEDs) for the same identifier without
,,extern'' specifier, and without an initializer?''.
And the answer is - no, because no such thing exists, according to the standard.
Now, the answer
is beginning with ,,It depends...''.
My answer doesn't. I'm not sure what your answer is anymore, nor why would you ask the question in the first place.
I would like the answer ,,yes''
or ,,no'', like in case of internal linkage external declarations
(ILEDs) for an identifier without an initializer.
Internal linkage external declarations? I have no idea what you are talking about, but it sounds like an oxymoron.
By an analogy between ELEDs and ILEDs
Since nothing like ILED exists to the best of my knowledge, no such analogy can be drawn.
in case of ELEDs, current state
of the standard is like permission of multiple ,,static int a;'', but
in one source file only, and saying that ,,#include''-ing of a file
with the same declaration for ,,a'' is UDB.
I don't understand a word you are saying, sorry. You lost me completely.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-10 17:49:44 UTC
Permalink
Post by Igor Tandetnik
BTW. ,,int a;'', ,,char a;'' is permitted even in the same file
You are pulling my leg, aren't you? You can't possibly say such
nonsense with a straight face, can you?
Have you actually tried this? I am - quite properly - getting error
C2371: 'a' : redefinition; different basic types.
Oops, certainly not - my mistypo - I've been suggested by your
,,int/char[4]'' pair. Should be ,,int a;'' and ,,unsigned a;''. It
causes: ,,warning C4142: benign redefinition of type'' (at least on VC
12.0, I'll try it on VC 14.0 soon).
Post by Igor Tandetnik
Internal linkage external declarations? I have no idea what you are
talking about, but it sounds like an oxymoron.
OK - I see that here is the problem with our misunderstanding of each
other. ,,external declaration'' is (according to 6.9 - especially p4)
declaration which appear out of any function's block scope. By
,,internal linkage external declaration'' - I mean ,,external
declaration'' with ,,internal linkage'' thus identifier declared out of
any function's block scope with ,,static'' storage specifier.

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-10 18:46:54 UTC
Permalink
Post by Cezary H. Noweta
Post by Igor Tandetnik
BTW. ,,int a;'', ,,char a;'' is permitted even in the same file
You are pulling my leg, aren't you? You can't possibly say such
nonsense with a straight face, can you?
Have you actually tried this? I am - quite properly - getting error
C2371: 'a' : redefinition; different basic types.
Oops, certainly not - my mistypo - I've been suggested by your
,,int/char[4]'' pair. Should be ,,int a;'' and ,,unsigned a;''. It
causes: ,,warning C4142: benign redefinition of type'' (at least on VC
12.0, I'll try it on VC 14.0 soon).
Such a program is still illegal under the standard. I presume VC accepts it as an extension.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-11 00:10:38 UTC
Permalink
Hello,
Post by Igor Tandetnik
Post by Cezary H. Noweta
Post by Igor Tandetnik
BTW. ,,int a;'', ,,char a;'' is permitted even in the same file
You are pulling my leg, aren't you? You can't possibly say such
nonsense with a straight face, can you? Have you actually tried
redefinition; different basic types.
Oops, certainly not - my mistypo - I've been suggested by your
,,int/char[4]'' pair. Should be ,,int a;'' and ,,unsigned a;''. It
causes: ,,warning C4142: benign redefinition of type'' (at least on
VC 12.0, I'll try it on VC 14.0 soon).
Such a program is still illegal under the standard. I presume VC
accepts it as an extension.
This is a bug (in a documentation at least).

http://msdn.microsoft.com/en-us/library/cbs8z6wh.aspx
Post by Igor Tandetnik
A type is redefined in a manner that has no effect on the generated
code.
It should appear when there is, for example, ,,int a;'' and ,,long a;''
(assuming that both are 32bits wide). ,,int/unsigned'' has a great
impact on the code, for example: ,,if ( a < 0 )''. Moreover, when there
is a pair of ,,char'' and ,,signed/unsigned char'' (it depends on using
of ,,/J'' option) then the compiler even will not squeal about different
types. This is present until MSVC 15.0 (VS2008).

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-10 18:45:34 UTC
Permalink
Post by Cezary H. Noweta
Post by Igor Tandetnik
Post by Cezary H. Noweta
If about external linkage, then it is required that programmer
should ensure exactly one definition among units (all but one
declarations should be ,,extern'' without an initializer), but, on
the other hand, it is still possible (looking from perspective of
one unit) to the programmer being not sure if he is just writing
declaration or definition. What for? Thanks to ,,extern'', there
are no ambiguities.
It is like saying: ,,you can be crazy but in a scope of one unit
only - when you go out of this unit, then you must remember to put
,,extern'' in all other units''.
Isn't this exactly what the standard specifies, and MSVC fails to
implement? You know, the part where the concept of tentative
definition only exists until the end of translation unit, at which
point any such declaration becomes external definition subject to one
definition rule. So, which side are you arguing? You lost me here.
In your opinion, is such duality pro the current state of the
standard?
Again, what duality?
Post by Cezary H. Noweta
The question is: ,,Should I be aware of multiple external
linkage external declarations (ELEDs) for the same identifier without
,,extern'' specifier, and without an initializer?''.
Yes, within a single translation unit. These are called tentative definitions by the standard.
Post by Cezary H. Noweta
Now, the answer
is beginning with ,,It depends...''.
Mine doesn't. Maybe yours does - why don't you state it?
Post by Cezary H. Noweta
I would like the answer ,,yes''
or ,,no'', like in case of internal linkage external declarations
(ILEDs) for an identifier without an initializer.
By an analogy between ELEDs and ILEDs, in case of ELEDs, current state
of the standard is like permission of multiple ,,static int a;'', but
in one source file only, and saying that ,,#include''-ing of a file
with the same declaration for ,,a'' is UDB.
I don't understand your point at all. It is legal to define the same name with internal linkage more than once in the same translation unit, under the same "tentative definition" rule, like this:

static int a;
static int a;

Further, it is legal to define the same name with internal linkage in multiple translation units (whether by #including the same header file or by any other means): such a name denotes a different object in each translation unit. However, it is illegal to have more than one definition of the same name with external linkage in the entire program. This is precisely the difference between internal and external linkage.

What precisely do you feel is wrong with this picture? Looks nicely symmetrical and consistent to me. Of course, the whole concept of tentative definition is rather inelegant, but apparently it was necessary for backward compatibility. C++ doesn't have it.
Post by Cezary H. Noweta
I am arguing that the current behavior of MSVC is better then a
sterilisation of the linker and prohibiting it from generating of an
EXE.
I don't see how your argument leads to this conclusion. What precisely is the advantage? What can you do with the current behavior that you can't do just as easily in a standard-conforming way?
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-11 02:40:15 UTC
Permalink
Hello,
Post by Igor Tandetnik
Post by Cezary H. Noweta
In your opinion, is such duality pro the current state of the
standard?
Again, what duality?
int a;
int a;

can occur, but in the same translation unit. Why? (I'm asking about a
reason). What is such limitation for? Or what is such flexibility for?
(Looking in today's perspective.)

I have two translation units:

== u1.c ==
int a;
void some_foo_operating_on_a(void) { ... }
==========

== u2.c ==
int a;
void other_foo_operating_on_the_same_a_at_least_by_intention(void) {...}
==========

Now the program causes UDB. But putting both of them together:

== both.c ==
#include "u1.c"
#include "u2.c"
============

is sufficient to make the program being perfectly valid. There is no
change in functionality: ,,a'' having external linkage is visible to
both of functions, regardless of they are in one or among two units.
Post by Igor Tandetnik
Post by Cezary H. Noweta
The question is: ,,Should I be aware of multiple external linkage
external declarations (ELEDs) for the same identifier without
,,extern'' specifier, and without an initializer?''.
Yes, within a single translation unit. These are called tentative
definitions by the standard.
Do you remember that we are still in the field of ELEDs? As I wrote, in
case of ILEDs, tentative definitions have solid justification: (1) there
is no way to explicitly declare statics, so we have to make do with
tentative definitions; (2) the consistency is preserved as across all
territory where static's explicit definition could occur, there can
be any number of tentative definitions for the same identifier.

However, in case of ELEDs, a territory suitable for grazing by tentative
definitions is limited to one unit. From ILED's point of view a
translation unit makes a border which cannot be crossed by any means.
Translation unit covers all the area where static can be accessed from.
But from ELED's point of view, a translation unit is nothing special.
Why is there a solid fence around for tentative definitions for the same
identifier? Why cannot tentative definitions for ELEDs cavort freely
across all the area where an explicit definition (i.e. havingi
nitializer) for a given identifier could occur?

Current state of the standard is like staying in a halfway (still
regarding ELEDs only - not ILEDs). Both: (1) unpinning tentative
definitions from ELEDs and (2) allowing ELEDs' tentative definitions to
occur at any place in a program are better then the current standard
(for above-mentioned reasons). Behaving like (1) is breaking the
standard as it prohibits what the standard allows to do, so (2) (what
MS' compiler/linker does) is the sole option which does not break the
standard (working well is a member of a set of UDBs - nothing said that
the compiler/linker shall not produce a valid program image, ready for
execution).

If regarding the standard, ensuring of one definition of external
linkage identifier is up to a programmer. To preserve one definition
rule, I postulated that a decision if tentative definition of extern is
a declaration or a definition shall be postponed until searching all
the area where explicit definition of a identifier can occur for stray
(tentative) definitions has been finished.

I cannot make myself more clear - that's all what I can write.

Acronyms:
ILED - internal linkage external declaration
ELED - external linkage external declaration

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-11 04:00:14 UTC
Permalink
Post by Cezary H. Noweta
Post by Igor Tandetnik
Post by Cezary H. Noweta
In your opinion, is such duality pro the current state of the standard?
Again, what duality?
int a;
int a;
can occur, but in the same translation unit. Why? (I'm asking about a
reason).
Apparently, so as not to break badly written programs predating the standard. No one argues that tentative definitions are good - presumably, they were a necessary evil 20 years ago.
Post by Cezary H. Noweta
What is such limitation for? Or what is such flexibility for?
(Looking in today's perspective.)
You probably don't need such "flexibility" today. C++ manages quite happily without. And nobody forces you to use them in C, either.
Post by Cezary H. Noweta
== u1.c ==
int a;
void some_foo_operating_on_a(void) { ... }
==========
== u2.c ==
int a;
void other_foo_operating_on_the_same_a_at_least_by_intention(void)
{...} ==========
== both.c ==
#include "u1.c"
#include "u2.c"
============
is sufficient to make the program being perfectly valid. There is no
change in functionality: ,,a'' having external linkage is visible to
both of functions, regardless of they are in one or among two units.
Similarly, if I have two translation units

== u1.c ==
static int a;
void some_foo_operating_on_a(void) { ... }
==========

== u2.c ==
static double a;
void other_foo_operating_on_different_a(void) {...}
==========

then they work fine on their own but become invalid when merged. So yes, you cannot arbitrarily merge or break up translation units forming a program: file organization matters. Is that what you were trying to demonstrate? I don't understand the point of the example.
Post by Cezary H. Noweta
Post by Igor Tandetnik
Post by Cezary H. Noweta
The question is: ,,Should I be aware of multiple external linkage
external declarations (ELEDs) for the same identifier without
,,extern'' specifier, and without an initializer?''.
Yes, within a single translation unit. These are called tentative
definitions by the standard.
Do you remember that we are still in the field of ELEDs? As I wrote,
in case of ILEDs, tentative definitions have solid justification
Ok, perhaps I misunderstood the question. If by "should I be aware" you mean "should I be using tentative definitions in programs I write today", I'd say "no, I don't see why you would ever want to". If by "should I be aware" you mean "would it be possible for me to encounter tentative definitions in an existing valid C program", I'd say "yes of course".

Having settled that, do we still have a disagreement? I kinda lost track of what we are arguing about.
Post by Cezary H. Noweta
(1) there is no way to explicitly declare statics, so we have to make do
with tentative definitions;
Correct, except I'm not sure why you would want to declare, but not define, a static variable in the first place. If you were given such a facility (imagine, for the sake of argument, that it's legal to write "extern static int x;"), what would you use it for?
Post by Cezary H. Noweta
However, in case of ELEDs, a territory suitable for grazing by
tentative definitions is limited to one unit.
If you are making an argument for not providing tentative definitions of identifers with external linkage in the programs you write, then I'm with you. In fact, I don't see why I would want to do that for identifiers with internal linkage, either. Like I said, C++ doesn't have this facility at all, and I never missed it.
Post by Cezary H. Noweta
From ILED's point of
view a translation unit makes a border which cannot be crossed by any
means. Translation unit covers all the area where static can be
accessed from. But from ELED's point of view, a translation unit is
nothing special.
Not true, according to the standard. A tentative definition turns into an "actual" definition at the end of the translation unit, so the translation unit boundary is very real.

As to why, the rationale document has this to say, at 6.2.2: "As in the strict ref/def model, only a single translation unit contains the definition of a given object because many environments cannot effectively or efficiently support the “distributed definition” inherent in the common or relaxed ref/def approaches... This composite approach was chosen to accommodate as wide a range of environments and existing implementations as possible."

In other words, there exist (or existed 20 years ago) systems with linkers that don't know how to merge multiple definitions from several object files. Hence the one definition rule.
Post by Cezary H. Noweta
Current state of the standard is like staying in a halfway (still
regarding ELEDs only - not ILEDs).
Ok, you seem to be arguing for the repeal of one definition rule. Why do you want that? Suppose it happened: what would you do with your newfound freedom that you can't do without? Are you opposed to it purely for aesthetic reasons?
Post by Cezary H. Noweta
Both: (1) unpinning tentative
definitions from ELEDs and (2) allowing ELEDs' tentative definitions
to occur at any place in a program are better then the current
standard (for above-mentioned reasons). Behaving like (1) is breaking
the standard as it prohibits what the standard allows to do, so (2)
(what MS' compiler/linker does) is the sole option which does not
break the standard (working well is a member of a set of UDBs -
nothing said that the compiler/linker shall not produce a valid
program image, ready for execution).
I don't believe I ever argued against any of that. Personally, I don't think it's a good idea to rely on non-standard behavior of a particular compiler/linker, but de gustibus non est disputandum.
Post by Cezary H. Noweta
If regarding the standard, ensuring of one definition of external
linkage identifier is up to a programmer.
Well, the linker could easily help with that if it wanted to. After all, it does produce an error on duplicate definitions when in C++ mode.
Post by Cezary H. Noweta
To preserve one definition
rule, I postulated that a decision if tentative definition of extern
is a declaration or a definition shall be postponed until searching
all the area where explicit definition of a identifier can occur for
stray (tentative) definitions has been finished.
Who should do the searching - compiler or linker? Compiler can't see beyond a single translation unit, while linker doesn't have type information and can't prevent nonsensical programs from building successfully.

Consider my earlier example with "int count;" in one file and "char count[4];" in the other. Do you believe that having such a program compile and link without error is a desirable outcome? What makes feel that the system where such programs are valid (or, at least, undetectable at compile/link time) is beneficial?
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Robby
2009-12-02 22:03:01 UTC
Permalink
Sorry guys, I didn't mean to spark a dispute based on my silly question!
Interestingly, if you say "int count=0;" explicitly in both places, then the linker does >complain.
Yes it does. So I gather that I should do it right anyways... and still
provide the "extern" specifier like this:

int count; ------------ in one .c file, and
extern int count; --------- in all the other .c files

<or>

In the case where I just want a global variable to the whole project I can
simply declare count in a header file and include that header file in every
.c file.

I can explain why "extern" was always confusing for me!

You see, the thing is that, the non compliant compiler obliged me to include
files like this:

================mian.c
#include "a.h"
#include "b.h"
#include "c.h"

#include "a.c"
#include "b.c"
#include "c.c"

int main(void)
{.....return 0;}
===============================

and whatever variables I would declare in any of the headers, they were
visible in any of the .c files without having to include any of the header
files in the .c files themselves. So I never really bothered with "extern".
But now, in a C compliant compiler, I don't need to include the .c files
anymore so I don't know exactly what problems I will encounter in the new C
compliant compiler when I do the port. I know I am in for a long and
frustrating time. Just to realize that in my 8 bit processor, my ints were 8
bits long. Now, in the 32 bit processor, my ints are 32 bits long and
wouldn't you know it in some places I relied on integers to reset themselves
automatically based on the variable's maximum value of 256 and adding one
would reset it to 0. Now, I either have to use a char type instead, which is
odd using a char type to do integer calculations. Or I have to change some
logic. My bools where called shorts, now my shorts are 16 bit wide!!! I may
just leave it as 16 bits wide or I might have to do bit variables or
something like that ????

Anyways back to the original issue, what I am thinking of doing now, is to
structure my project files/headers/global variables the way this forum had
proposed me to do so a long time ago... but could not have done it due to my
old compiler limitations. So now I might need to post another question about
this when I get there.

Here's the scenario, in my old compiler, I have *all* the .h and .c files
included at the top of a main.c file. So for example my main.c file looks
something like this:

==============================main.c
#include <Dv4685.h> // DEVICE INNITIALIZATIONS
#include <stdlib.h> // C libs
#include <string.h> // C libs

#include <MAIN.h>
#include <MYLIB.h>
#include <API.h>
#include <API_DRAWBUTT.h>
#include <PC.h>
.... and many more .h files!!!!

#include <MYLIB.c>
#include <API.c>
#include <API_DRAWBUTT.c>
#include <PC.c>
... and many more .c files
====================================

and if I needed any project wide global variables, I would declare them in
main.h.

Now, lets suppose we look at the PC.h and PC.c files. I always declared my
global variables that pertained to the PC.c file in my PC.h file.
However, **all*** the .c files were able to see any global variables that
were declared in any of the .h files. I never really needed extern! I did not
know that file scope existed! Yeah I know... sad ay!

The way I see this now with a C compliant compiler is that we can declare an
integer variable called "xxx" to have file scope for a particular .c file. So
suppose we have a C file called motors.c, and we declare an integer called
xxx in motors.h, we should include motors.h at the top of motors.c. That's
fine! But, if we want xxx to have scope in another .c and .h files called
lights.c/lights.h, then we can code "extern int xxx;" at the top of lights.h
to provide xxx scope for the lights.h file. Am I seeing this right?

Thanks for all your responses!
--
Best regards
Roberto
Post by Robby
================================F1.c
#include <stdio.h>
int count;
void f1();
int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================
============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
===============================
According to the book, the above example is not supposed to work as it
"If you declare count a second time, many linkers will report a
duplicate symbol error, which means that count is defined twice and
the linker doesn't know which to use."
Now, I tried this in VC++ as a simple .c program as shown above and I
get no errors or warnings and VC++ promts a build succeeded in the
output box ???
Looks like a bug to me. The program is in fact invalid, and the linker should have complained.
6.9p5 An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. 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.
6.9.2p1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.
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.
In your example, "int count;" is a tentative definition, and should behave the same as "int count=0;" (in both source files), which is an external definition, of which there must be exactly one in the entire program, but the example has two. Interestingly, if you say "int count=0;" explicitly in both places, then the linker does complain.
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
.
David Wilkinson
2009-12-02 22:44:20 UTC
Permalink
Post by Robby
Sorry guys, I didn't mean to spark a dispute based on my silly question!
Robby:

Not silly at all. I'm sure glad I decided not to answer this question :-).

For what it's worth, I did think the code looked wrong, but all my knowledge (or
lack thereof) is for C++, not C, and in C++ I very rarely use global variables.
--
David Wilkinson
Visual C++ MVP
Robby
2009-12-03 04:11:01 UTC
Permalink
I hear you David...

I try not to use globals too much either.

I didn't think that there was such much to know about a variable specifier.
--
Best regards
Roberto
Igor Tandetnik
2009-12-02 23:07:12 UTC
Permalink
Post by Robby
In the case where I just want a global variable to the whole project
I can simply declare count in a header file and include that header
file in every .c file.
Common practice goes like this:

// globalvar.h
extern int globalvar;


// exactly one .c file
#include "globalvar.h"
int globalvar;
// or
int globalvar = 42;


// All other .c files
#include "globalvar.h"
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Robby
2009-12-03 04:21:01 UTC
Permalink
Post by Igor Tandetnik
Post by Robby
In the case where I just want a global variable to the whole project
I can simply declare count in a header file and include that header
file in every .c file.
// globalvar.h
extern int globalvar;
// exactly one .c file
#include "globalvar.h"
int globalvar;
// or
int globalvar = 42;
// All other .c files
#include "globalvar.h"
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Thanks Igor.

I am sorry if I put you or anyone else in conflict, it really wasn't my
intentions.

Sincere regards
Roberto
James Kanze
2009-12-03 11:36:11 UTC
Permalink
Post by Igor Tandetnik
Post by Robby
================================F1.c
#include <stdio.h>
int count;
void f1();
int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================
============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
===============================
According to the book, the above example is not supposed to
"If you declare count a second time, many linkers will
report a duplicate symbol error, which means that count is
defined twice and the linker doesn't know which to use."
Now, I tried this in VC++ as a simple .c program as shown
above and I get no errors or warnings and VC++ promts a
build succeeded in the output box ???
Looks like a bug to me. The program is in fact invalid, and
the linker should have complained.
It's undefined behavior. The compiler is not required to
complain. (This is the case for all errors which involve more
than one file, I believe.)

Historically, most early C compilers (late 1970's/early 1980's)
implemented global definitions as above more or less as if they
were named common blocks in Fortran---just overlay all of the
definitions with the same name. The C standard decided to
require the extern, in order to allow more rigorous
implementations, but many C compilers continue to support the
older semantics.

--
James Kanze
David Webber
2009-12-02 18:27:05 UTC
Permalink
Post by Robby
Hello,
I am in the process of porting a large C program from a non- ansi C
compilant embedded compiler to an ansi C compliant emebedded compiler. I see
that the rules are not quite the same...
It's a while since I worried about this but here goes:

1. You can define different variables with the same name - eg

int count;

in different source (.c or .cpp) files. No problem. They are local to the
file containing them and completely independent.

2. If you define them like that in header files however, and then include
both header files in the same .c or .cpp file, then you're in trouble.

3. If you put

int count;

in one .c or cpp file and

extern int count;

in a number of others, then they'll all use the same count variable.

4. If you put

int count;

in two or more c/cpp files and use

extern int count;

in others, then I imagine it won't know which one you're talking about, so
don't.

5. In fact declaring global variables and accessing them through extern is
very bad style and best avoided.

If you really want global variables then why not declare them in a single
file and have get/set functions for them, also in that file, but with the
functions' prototypes declared in a header file available throughout the
program? No 'extern' needed.

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mozartists/mailinglist.htm
Victor Bazarov
2009-12-02 18:42:15 UTC
Permalink
Post by David Webber
Post by Robby
Hello,
I am in the process of porting a large C program from a non- ansi C
compilant embedded compiler to an ansi C compliant emebedded compiler. I see
that the rules are not quite the same...
1. You can define different variables with the same name - eg
int count;
in different source (.c or .cpp) files. No problem. They are local to
the file containing them and completely independent.
They are local, but they have external linkage.
Post by David Webber
2. If you define them like that in header files however, and then
include both header files in the same .c or .cpp file, then you're in
trouble.
What's the difference? Inclusion is just text substitution. Either you
do it in a text editor or let the preprocessor do it for you.
Post by David Webber
3. If you put
int count;
in one .c or cpp file and
extern int count;
in a number of others, then they'll all use the same count variable.
That's true.
Post by David Webber
4. If you put
int count;
in two or more c/cpp files and use
extern int count;
in others, then I imagine it won't know which one you're talking about,
so don't.
Uh... How is that different from the case in which you don't write
'extern in count;' anywhere else? How would "it" know?
Post by David Webber
5. In fact declaring global variables and accessing them through extern
is very bad style and best avoided.
Style really have nothing to do with the rules of the language, does it?
Post by David Webber
If you really want global variables then why not declare them in a
single file and have get/set functions for them, also in that file, but
with the functions' prototypes declared in a header file available
throughout the program? No 'extern' needed.
What's the difference between object's being extern and a function's
being extern? A symbol is a symbol, a name is a name. It does not
matter whether you access the value directly or indirectly.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Igor Tandetnik
2009-12-02 18:44:01 UTC
Permalink
Post by David Webber
1. You can define different variables with the same name - eg
int count;
in different source (.c or .cpp) files. No problem. They are local
to the file containing them and completely independent.
Not true. If you actually try the example, you'll see that the change to count in F2.c is visible in F1.c
Post by David Webber
If you really want global variables then why not declare them in a
single file and have get/set functions for them, also in that file,
but with the functions' prototypes declared in a header file
available throughout the program? No 'extern' needed.
Only because function declarations are 'extern' by default.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
David Webber
2009-12-02 19:46:37 UTC
Permalink
Post by Igor Tandetnik
Post by David Webber
1. You can define different variables with the same name - eg
int count;
in different source (.c or .cpp) files. No problem. They are local
to the file containing them and completely independent.
Not true. If you actually try the example, you'll see that the change to
count in F2.c is visible in F1.c
Really? I'm more than surprised - my apologies if I misled. So how does
having

int count;

in two files differ from having

int count;

in one and

extern int count;

in the other. It would seem that "extern" is completely redundant
(although I can see that having "extern int count" in a block of limited
scope would make it unnecessary to have a global int count; in the same
file).
Post by Igor Tandetnik
Post by David Webber
If you really want global variables then why not declare them in a
single file and have get/set functions for them, also in that file,
but with the functions' prototypes declared in a header file
available throughout the program? No 'extern' needed.
Only because function declarations are 'extern' by default.
Yes but that's sort of essential to linking files containing different
functions. :-)

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mozartists/mailinglist.htm
Igor Tandetnik
2009-12-02 20:32:17 UTC
Permalink
Post by David Webber
Post by Igor Tandetnik
Post by David Webber
1. You can define different variables with the same name - eg
int count;
in different source (.c or .cpp) files. No problem. They are
local to the file containing them and completely independent.
Not true. If you actually try the example, you'll see that the
change to count in F2.c is visible in F1.c
Really? I'm more than surprised - my apologies if I misled. So
how does having
int count;
in two files differ from having
int count;
in one and
extern int count;
in the other.
The former is invalid, VC accepts it as an extension (or due to a bug, it's a matter of opinion). The latter is valid.
Post by David Webber
Post by Igor Tandetnik
Post by David Webber
If you really want global variables then why not declare them in a
single file and have get/set functions for them, also in that file,
but with the functions' prototypes declared in a header file
available throughout the program? No 'extern' needed.
Only because function declarations are 'extern' by default.
Yes but that's sort of essential to linking files containing different
functions. :-)
You seem to imply that there's some fundamental difference between variable names and function names with respect to linkage, and that the necessity of spelling "extern", or lack thereof, is the manifestation of that difference. I'm trying to point out that the difference is purely in the way syntactic sugar is sprinkled on them. You can write

extern void f();

if you are so inclined, it won't change anything.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-02 22:05:02 UTC
Permalink
Hello,
Post by David Webber
Really? I'm more than surprised - my apologies if I misled. So how
does having
int count;
in two files differ from having
int count;
in one and
extern int count;
int count; - declaration or definition, depending on requirements.

extern int count; - declaration.

int count = ...sth...; - definition.

Look at the 8th post of
http://groups.google.com/group/microsoft.public.vc.language/browse_frm/thread/10b1ace144aa1203/125d03d6c02117d0?tvc=1&q=multiple+definitions#125d03d6c02117d0
- there are generated entities.

-- best regards

Cezary Noweta
Victor Bazarov
2009-12-02 18:33:47 UTC
Permalink
Post by Robby
I am in the process of porting a large C program from a non- ansi C
compilant embedded compiler to an ansi C compliant emebedded compiler. I see
that the rules are not quite the same.
I am reviewing some specifics of extern in a book called "Teach yourself C"
3rd edition from Herbert Schildt and there is a short sample code on p.339
================================F1.c
#include <stdio.h>
int count;
void f1();
int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================
============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
Try replacing this with

count = 42;

and see if the loop inside 'main' actually executes 42 times.
Post by Robby
===============================
According to the book, the above example is not supposed to work as it
"If you declare count a second time, many linkers will report a duplicate
symbol error, which means that count is defined twice and the linker doesn't
know which to use."
Now, I tried this in VC++ as a simple .c program as shown above and I get no
errors or warnings and VC++ promts a build succeeded in the output box ???
But the book goes on to suggest that the correct way to do this is by using
============================F2.c
#include <stdlib.h>
extern int count;
void f1(void)
{ count = rand(); }
===============================
This also compiles without errors or warnings. But why does the original
sample code (F1.c and F2.c) still work when the book says I should get an
duplicate symbol error?
It compiles, but does it work? Do you actually see your loop iterate or
does your program just quit (because the 'int count' defined in the 'F1'
module has the value 0)?

In C the rule is that your declaration is a "tentative definition". Had
you attempted to do it in a C++ program, you'd get a multiple definition
error (violation of ODR), and it is resolved by putting 'extern' in
front of one of the declarations.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Igor Tandetnik
2009-12-02 18:48:18 UTC
Permalink
Post by Victor Bazarov
Post by Robby
I am in the process of porting a large C program from a non- ansi C
compilant embedded compiler to an ansi C compliant emebedded
compiler. I see that the rules are not quite the same.
I am reviewing some specifics of extern in a book called "Teach
yourself C" 3rd edition from Herbert Schildt and there is a short
sample code on p.339 which explains the use of the extern specifier.
================================F1.c
#include <stdio.h>
int count;
void f1();
int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================
============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
Try replacing this with
count = 42;
and see if the loop inside 'main' actually executes 42 times.
Indeed it does.
Post by Victor Bazarov
It compiles, but does it work? Do you actually see your loop iterate
Yes.
Post by Victor Bazarov
or does your program just quit (because the 'int count' defined in
the 'F1' module has the value 0)?
No.
Post by Victor Bazarov
In C the rule is that your declaration is a "tentative definition".
Tentative definitions become real definitions at the end of translation unit. They don't affect linkage.

At least, that's the way it's specified in C99. I don't have C90 handy, perhaps it says differently.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Victor Bazarov
2009-12-02 18:57:06 UTC
Permalink
Post by Igor Tandetnik
Post by Victor Bazarov
Post by Robby
I am in the process of porting a large C program from a non- ansi C
compilant embedded compiler to an ansi C compliant emebedded
compiler. I see that the rules are not quite the same.
I am reviewing some specifics of extern in a book called "Teach
yourself C" 3rd edition from Herbert Schildt and there is a short
sample code on p.339 which explains the use of the extern specifier.
[..]
In C the rule is that your declaration is a "tentative definition".
Tentative definitions become real definitions at the end of translation unit. They don't affect linkage.
Yes, I realise that. I am not sure VC++ doesn't extend the
"tentativeness" of those definitions until linking.
Post by Igor Tandetnik
At least, that's the way it's specified in C99. I don't have C90 handy, perhaps it says differently.
That's the big grey area to me as well. I used to have C89/C90 on the
shelf, long time ago. MS decided not to do much about C99 for some
reason, so it's all open to guesses.

To the OP: be skeptical when reading books by Schildt. By many experts'
opinions (see www.accu.org for reviews) those books contain relatively
large number of errors.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
James Kanze
2009-12-03 11:45:36 UTC
Permalink
[...]
Post by Victor Bazarov
It compiles, but does it work?
Probably. It's a frequent extension to C to support this.
Post by Victor Bazarov
Do you actually see your loop iterate or does your program
just quit (because the 'int count' defined in the 'F1' module
has the value 0)?
In C the rule is that your declaration is a "tentative
definition". Had you attempted to do it in a C++ program,
you'd get a multiple definition error (violation of ODR), and
it is resolved by putting 'extern' in front of one of the
declarations.
In C, if you arrive at the end of a translation unit with any
tentative definitions still tentative, the compiler converts
them into ordinary definitions. In C++, violations of the ODR
are undefined behavior, and in C, multiple definitions in
different files are also undefined behavior, so although the way
the standards get there is different, the ultimate effect is
exactly the same: it's undefined behavior.

In practice, you'll almost always get an error in C++, because
they'll use the same technique here as they would for variables
with constructors, which will result in the linker complaining
about multiple definitions. In C, it depends---a lot of
compilers will support this, because historically, that was the
way the earliest C compilers worked.

--
James Kanze
Cezary H. Noweta
2009-12-02 20:45:50 UTC
Permalink
Post by Robby
================================F1.c
#include <stdio.h>
int count;
void f1();
int main(void)
{
int i;
f1();
for(i=0; i<count; i++) printf("%d ", i);
return 0;
}
==============================
============================F2.c
#include <stdlib.h>
int count;
void f1(void)
{ count = rand(); }
===============================
According to the book, the above example is not supposed to work as it
"If you declare count a second time, many linkers will report a duplicate
symbol error, which means that count is defined twice and the linker doesn't
know which to use."
Indeed, according to the standard ,,int count;'' means that count has
storage in F1 and in F2 and is initialized with value 0 in both units.
However the standard says that there shall be not more then one
_external definition_ in the program. ,,int count;'' is tentative
definion. External definition is ,,int count = 0;'' or equivalent
,,extern int count = 0;'' (both are the same).
Post by Robby
Now, I tried this in VC++ as a simple .c program as shown above and I get no
errors or warnings and VC++ promts a build succeeded in the output box ???
Do ,,int count = 0;'' in both units - you will have what you want.
Microsoft treats declaration ,,int count;'' as common variable. Look at
http://groups.google.com/group/microsoft.public.vc.language/browse_frm/thread/10b1ace144aa1203/125d03d6c02117d0?tvc=1&q=multiple+definitions#125d03d6c02117d0
post 8.
Post by Robby
But the book goes on to suggest that the correct way to do this is by using
============================F2.c
#include <stdlib.h>
extern int count;
void f1(void)
{ count = rand(); }
===============================
This also compiles without errors or warnings. But why does the original
sample code (F1.c and F2.c) still work when the book says I should get an
duplicate symbol error?
Look at the following table:

F1.c F2.c
int count; int count; OK - both are tentative, and both

reference to the same object
int count = 0; int count; OK - at most one external definition
(in F1) both reference to the same
object
int count = 0; int count = 0; ERROR - two external definitions
extern int count; int count; OK - one tentative definition (in F2)
extern int count; int count = 0; OK - one external definition (in
F2)
extern int count; extern int count; ERROR - no definition for
count.

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-02 21:02:21 UTC
Permalink
Post by Cezary H. Noweta
Indeed, according to the standard ,,int count;'' means that count has
storage in F1 and in F2 and is initialized with value 0 in both units.
However the standard says that there shall be not more then one
_external definition_ in the program. ,,int count;'' is tentative
definion. External definition is ,,int count = 0;'' or equivalent
,,extern int count = 0;'' (both are the same).
The standard also says, in effect, that a tentative definition turns into an external definition at the end of translation unit. Basically, "tentative definition" should be a compile-time concept, not a link-time one.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Cezary H. Noweta
2009-12-02 22:43:02 UTC
Permalink
Hello,
Post by Igor Tandetnik
The standard also says, in effect, that a tentative definition turns
into an external definition at the end of translation unit.
Basically, "tentative definition" should be a compile-time concept,
not a link-time one.
Not exactly. Why are you guessing that declaration of external linkage
object is a definition at the end of unit translation? What about ,,void
foo(void);'' - should translator core-dump with ,,foo not found''
message or should it place ,,ret'' in OBJ file? Certainly, the question
is rhetorical as there is no doubt ,,void foo(void);'' is a declaration.

So, if (externally linked) ,,int count;'' can be a declaration as well
as a definition, then why it should be decided after translation of one
unit? Why it should be assumed that there will not be a definition in
other translation unit? If a programmer wants to decide then must place
,,extern'' before or ,,= sth'' after a declaration.

Thus ,,tentative definition'' should be a link-time concept (considering
external linkage objects) as a translator has too few info for deciding
about externals. A translator calculates CALL's displacement when
translating ,,foo();'' if ,,foo()'' is in a currently translated unit,
and lets the linker calculate CALL's displacement if ,,foo()'' is out of
a currently translated unit. A translator decides about ,,static int
count;'' and lets the linker decide about ,,int count;''.

-- best regards

Cezary Noweta
Igor Tandetnik
2009-12-03 00:29:14 UTC
Permalink
Post by Cezary H. Noweta
Post by Igor Tandetnik
The standard also says, in effect, that a tentative definition turns
into an external definition at the end of translation unit.
Basically, "tentative definition" should be a compile-time concept,
not a link-time one.
Not exactly. Why are you guessing that declaration of external linkage
object is a definition at the end of unit translation?
The standard says so. There is no guesswork involved.
Post by Cezary H. Noweta
What about
,,void foo(void);'' - should translator core-dump with ,,foo not
found'' message or should it place ,,ret'' in OBJ file?
Nice strawman. 6.9.2 applies to object defitions. 6.9.1 describes function definitions - rules are different there.
Post by Cezary H. Noweta
So, if (externally linked) ,,int count;'' can be a declaration as well
as a definition, then why it should be decided after translation of
one unit?
To make this legal (presumably for backward compatibility):

int count;
int count = 42;

int arr[];
int arr[100];
Post by Cezary H. Noweta
Why it should be assumed that there will not be a
definition in other translation unit?
Indeed, why? I never claimed it should. If there is, the program is invalid under 6.9p5.
Post by Cezary H. Noweta
If a programmer wants to decide
then must place ,,extern'' before or ,,= sth'' after a declaration.
I don't see how this follows.
Post by Cezary H. Noweta
Thus ,,tentative definition'' should be a link-time concept
I don't see how this follows.
Post by Cezary H. Noweta
(considering external linkage objects) as a translator has too few
info for deciding about externals. A translator calculates CALL's
displacement when translating ,,foo();'' if ,,foo()'' is in a
currently translated unit, and lets the linker calculate CALL's
displacement if ,,foo()'' is out of a currently translated unit.
I don't see the relevance of this statement, even if true. Note that there ain't no such thing as tentative definition of a function.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Continue reading on narkive:
Loading...