Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

compile error in C++, not C

19 views
Skip to first unread message

Tim H

unread,
Aug 23, 2008, 6:44:44 AM8/23/08
to
The following program compiles just fine in C, but not in C++. Can
anyone explain why? I have a chunk of code that defines stuff like
this in headers (without the extern) that I can not easily change.

The C compiler recognizes the first foo and second foo as the same.
The C++ compiler not so much.

Is there a way to get this to compile in C++ without changing all the
headers?


int foo;
int foo=1;

int
main(void)
{
return foo;
}

Salt_Peter

unread,
Aug 23, 2008, 7:20:12 AM8/23/08
to
On Aug 23, 2:44 am, Tim H <thoc...@gmail.com> wrote:
> The following program compiles just fine in C, but not in C++. Can
> anyone explain why? I have a chunk of code that defines stuff like
> this in headers (without the extern) that I can not easily change.
>
> The C compiler recognizes the first foo and second foo as the same.
> The C++ compiler not so much.

A declaration is a declaration and these must be unique. C++ enforces
this with no exceptions whatsoever. You are suggesting that its the
programmer that should sift through all statements to figure out which
(and where) a given variable is declared.

consider:
int foo;
double foo(0);

also, how do you expect the client (the user of your code) to figure
out what you wrote above means? What if the client sees int foo = 1;
and doesn't realize that foo is in another scope? poof goes your
theory.

Juha Nieminen

unread,
Aug 23, 2008, 8:46:54 AM8/23/08
to
Tim H wrote:
> The C compiler recognizes the first foo and second foo as the same.

This is just a personal opinion, but IMO the C language specification
is broken. I clearly see two variables being declared there, both with
the same name.

> int foo;
> int foo=1;

Jerry Coffin

unread,
Aug 23, 2008, 3:26:26 PM8/23/08
to
In article <be0fbae6-1e9e-4d31-b55d-
69e2fa...@s20g2000prd.googlegroups.com>, tho...@gmail.com says...

In C, each of these is (separately) a "tentative definition". You can
have as many tentative definitions for a variable as you like, as long
as none of them directly conflicts with any other. After reading all the
tentative definitions, the compiler creates a complete definition of the
variable based on 1) the defaults from where those definitions are
placed, and 2) all the storage classes, qualifiers, etc. you put into
all the tentative definitions. As I said, as long as nothing you put
into one tentative definition directly conflicts with what you put in
another (e.g. defining foo as an int in one place but a double in
another) this is allowed. For example:

int foo;
extern int foo;
volatile int foo;
int foo = 1;

In this case, the overall definition is equivalent to:
extern volatile int foo = 1;

In C++, there's no such thing as a tentative definition. There's a one
definition rule that says you have to define the variable exactly once
and that's it. There's almost certainly not going to be a way to
persuade a C++ compiler to accept your definitions as they stand right
now.

As to how to make things work: I'm pretty sure you're not going to be
able to persuade a C++ compiler to accept the source code as it stands,
so it's basically going to come down to whether you can find a tool to
generate what you need. Offhand I'm not sure whether it can do the job
or not, but the first one I'd look at would probably be Doxygen.

--
Later,
Jerry.

The universe is a figment of its own imagination.

Greg Herlihy

unread,
Aug 23, 2008, 4:46:59 PM8/23/08
to

A simple change that would make the above program valid in both C and C
++ would be to add an "extern" to "foo's" initial declaration:

extern int foo;
int foo = 1;

int main()
{
...

}

Greg

Jack Klein

unread,
Aug 24, 2008, 4:16:21 AM8/24/08
to
On Sat, 23 Aug 2008 09:26:26 -0600, Jerry Coffin <jco...@taeus.com>
wrote in comp.lang.c++:

> In article <be0fbae6-1e9e-4d31-b55d-
> 69e2fa...@s20g2000prd.googlegroups.com>, tho...@gmail.com says...
> > The following program compiles just fine in C, but not in C++. Can
> > anyone explain why? I have a chunk of code that defines stuff like
> > this in headers (without the extern) that I can not easily change.
> >
> > The C compiler recognizes the first foo and second foo as the same.
> > The C++ compiler not so much.
> >
> > Is there a way to get this to compile in C++ without changing all the
> > headers?
> >

An excellent explanation, Jerry, but not quite right in terms of C.
Even allowing for the fact that this is only allowed at file scope in
C, a concept that has been replaced and expanded by namespace scope in
C++. Multiple definitions of the same identifier in any block scope
in C is not allowed.

> > int foo;
> > int foo=1;
> In C, each of these is (separately) a "tentative definition". You can
> have as many tentative definitions for a variable as you like, as long
> as none of them directly conflicts with any other. After reading all the
> tentative definitions, the compiler creates a complete definition of the
> variable based on 1) the defaults from where those definitions are
> placed, and 2) all the storage classes, qualifiers, etc. you put into
> all the tentative definitions. As I said, as long as nothing you put
> into one tentative definition directly conflicts with what you put in
> another (e.g. defining foo as an int in one place but a double in
> another) this is allowed. For example:

Actually, at file scope in C:

int foo;

...is a tentative definition.

int foo = 1;

...is a complete definition, nothing tentative about it. If it were
only a tentative definition, the following would be valid C code:

/* file clc.c */


int foo;
int foo = 1;

int foo;


int foo;
int foo = 1;

/* end clc.c */

Here's the diagnostic issued by Visual C++ 6.0 (I know it's terribly
obsolete as a C++ compiler, but it is actually a reasonably good
conforming C90 compiler):

clc.c(6) : error C2374: 'foo' : redefinition; multiple initialization

And finally:

extern int foo;

...is not any sort of definition, tentative or otherwise. It is
merely an external declaration, if it does not have an initializer. If
there is no actual or even tentative declaration of foo in the
translation unit, but it is accessed or its address taken, that
translation unit will not define it, but will require it to be defined
elsewhere in the program.

A tentative definition in C is one at file scope for an object without
a storage class specifier, or with the storage class specifier static,
and without an initializer. If one or more tentative definitions
exist for an object in a translation, and there is no actual
definition with an initializer for the object in the translation unit,
then the compiler is required to behave as though a definition with
any equalizer of 0 (or perhaps { 0 }) were present.

> int foo;
> extern int foo;
> volatile int foo;
> int foo = 1;
>
> In this case, the overall definition is equivalent to:
> extern volatile int foo = 1;

You are correct in that various qualifiers on multiple definitions
contribute to the composite type of the final definition of the
object, although extern does not really fit in here, as "extern int
foo" is an external declaration and not a tentative definition, and
the object has external linkage by default due to the lack of the
static keyword.

> In C++, there's no such thing as a tentative definition. There's a one
> definition rule that says you have to define the variable exactly once
> and that's it. There's almost certainly not going to be a way to
> persuade a C++ compiler to accept your definitions as they stand right
> now.

Actually, C++ could have allowed this for built-in types, but it could
have resulted and in complications for user-defined types. Its
removal from C++ was deliberate, with the rationale:

"This avoids having different initialization rules for built-in types
and user-defined types."

> As to how to make things work: I'm pretty sure you're not going to be
> able to persuade a C++ compiler to accept the source code as it stands,
> so it's basically going to come down to whether you can find a tool to
> generate what you need. Offhand I'm not sure whether it can do the job
> or not, but the first one I'd look at would probably be Doxygen.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html

Jack Klein

unread,
Aug 24, 2008, 4:24:01 AM8/24/08
to
On Sat, 23 Aug 2008 08:46:54 GMT, Juha Nieminen
<nos...@thanks.invalid> wrote in comp.lang.c++:

There is occasional use for this in C, here is the entire text from
Appendix C of the C++ standard:

--------
Change: C++ does not have “tentative definitions” as in C
E.g., at file scope,

int i;
int i;

is valid in C, invalid in C++. This makes it impossible to define
mutually referential file-local static objects, if initializers are
restricted to the syntactic forms of C. For example,

struct X { int i; struct X *next; };
static struct X a;
static struct X b = { 0, &a };
static struct X a = { 1, &b };

Rationale: This avoids having different initialization rules for


built-in types and user-defined types.

Effect on original feature: Deletion of semantically well-defined
feature.

Difficulty of converting: Semantic transformation. In C++, the
initializer for one of a set of mutually-referential file-local static
objects must invoke a function call to achieve the initialization.

How widely used: Seldom.
--------

Without the tentative definitions allowed by C, it would be impossible
to define the pair 'a' and 'b' in C, since initializers for objects
with static storage duration must be compile-time constants in the
language.

Admittedly, the:

int i;
/* other stuff */
int i = 0; /* or even just another "int i;" */

...is just sloppy programming.

Jerry Coffin

unread,
Aug 24, 2008, 3:56:00 PM8/24/08
to
In article <fdm1b4p0s8hqtb41i...@4ax.com>,
jack...@spamcop.net says...

[ ... ]

> > > int foo;
> > > int foo=1;
> > In C, each of these is (separately) a "tentative definition". You can
> > have as many tentative definitions for a variable as you like, as long
> > as none of them directly conflicts with any other. After reading all the
> > tentative definitions, the compiler creates a complete definition of the
> > variable based on 1) the defaults from where those definitions are
> > placed, and 2) all the storage classes, qualifiers, etc. you put into
> > all the tentative definitions. As I said, as long as nothing you put
> > into one tentative definition directly conflicts with what you put in
> > another (e.g. defining foo as an int in one place but a double in
> > another) this is allowed. For example:
>
> Actually, at file scope in C:
>
> int foo;
>
> ...is a tentative definition.
>
> int foo = 1;
>
> ...is a complete definition, nothing tentative about it.

Oops -- you're quite right. My code was correct, but the description was
a bit off -- since the last included an initializer, it clearly was NOT
a tentative definition. I knew I should have reread that part of the
standard before I posted that...

Old Wolf

unread,
Aug 25, 2008, 2:46:46 AM8/25/08
to
On Aug 23, 6:44 pm, Tim H <thoc...@gmail.com> wrote:
> The following program compiles just fine in C, but not in C++.
>
> Is there a way to get this to compile in C++ without changing all the
> headers?

Why would you want to convert working
C code to C++ ?

Juha Nieminen

unread,
Aug 25, 2008, 12:20:21 PM8/25/08
to

Good question, given that C code can usually be compiled as C code,
and the resulting object files linked into the final C++ program without
problems. Only 'extern "C"' needs to be used on the C++ side when
including the C header files.

Greg Herlihy

unread,
Aug 26, 2008, 10:25:44 AM8/26/08
to

Because C++ is a better C than C.

Greg


Matthias Buelow

unread,
Aug 26, 2008, 5:02:53 PM8/26/08
to
Greg Herlihy wrote:

> Because C++ is a better C than C.

Famous last words...

0 new messages