extern and linker error

B

barcaroller

I have come across the following linker error but I'm unable to explain
it. Assume I have three little source files:


// foo.h
extern int x;

// foo.c
int x = 5;

// bar.c
#include foo.h
int y = x; // <--- linker error here


This compiles fine but the linker complains:

c++ foo.o bar.o -o myexe
Linker error: undefined reference to x'


However, this linker error goes aways when I add one line to foo.c:

// foo.c
#include "foo.h" // <--- new line
int x = 5;


Why would the linker not see 'x' unless I add an 'extern x' to the
source file that defines 'x'?
 
I

Ian Collins

I have come across the following linker error but I'm unable to explain
it. Assume I have three little source files:


// foo.h
extern int x;

// foo.c
int x = 5;

// bar.c
#include foo.h
int y = x; // <--- linker error here


This compiles fine but the linker complains:

c++ foo.o bar.o -o myexe
Linker error: undefined reference to x'


However, this linker error goes aways when I add one line to foo.c:

// foo.c
#include "foo.h" // <--- new line
int x = 5;


Why would the linker not see 'x' unless I add an 'extern x' to the
source file that defines 'x'?

Because it has local linkage in foo.c
 
B

barcaroller

Because it has local linkage in foo.c

I'm not sure I understand what you mean. If the variable is defined in
foo.c, how would the extern benefit the linker? When I do a 'nm foo.o'
I can see the symbol defined (with or without the extern); why doesn't
the linker see it too?
 
G

Geoff

I'm not sure I understand what you mean. If the variable is defined in
foo.c, how would the extern benefit the linker? When I do a 'nm foo.o'
I can see the symbol defined (with or without the extern); why doesn't
the linker see it too?

Because a variable at file scope only has file scope unless it is
declared extern. Only then can other translation units see it. The
header declares it extern, the foo.c allocates the storage for it and
the inclusion of the header tells the compiler that that variable x is
the one that is extern x.
 
B

barcaroller

Because a variable at file scope only has file scope unless it is
declared extern.

I thought a variable has file scope (and only file scope) if it is
defined as static:

// foo.c
static int x = 5;

Only then can other translation units see it. The
header declares it extern, the foo.c allocates the storage for it and
the inclusion of the header tells the compiler that that variable x is
the one that is extern x.

If I understood you correctly, you are saying that a variable is static
by default and that only an explicit extern (in the same unit that
defines the variable) exports the variable. If that is the case (and I
may have misunderstood you), what is the purpose of using static at
all; I could have just defined the variable without an extern?
 
J

Joshua Maurice

I have come across the following linker error but I'm unable to explain
it.  Assume I have three little source files:

    // foo.h
    extern int x;

    // foo.c
    int x = 5;

    // bar.c
    #include foo.h
    int y = x;    // <--- linker error here

This compiles fine but the linker complains:

    c++ foo.o bar.o -o myexe
    Linker error: undefined reference to x'

However, this linker error goes aways when I add one line to foo.c:

    // foo.c
    #include "foo.h"  // <--- new line
    int x = 5;

Why would the linker not see 'x' unless I add an 'extern x' to the
source file that defines 'x'?

With respect to the other posters in this thread, that is valid C++
code. It should compile and link. (Well, ignoring the missing
"main".)

C++03, 3.5 Programs and linkage / 3 and 4
3 A name having namespace scope (3.3.5) has internal linkage if it is
the name of
— an object, reference, function or function template that is
explicitly declared static or,
— an object or reference that is explicitly declared const and neither
explicitly declared extern nor
previously declared to have external linkage; or
— a data member of an anonymous union.

4 A name having namespace scope has external linkage if it is the name
of
— an object or reference, unless it has internal linkage; or
— a function, unless it has internal linkage; or
— a named class (clause 9), or an unnamed class defined in a typedef
declaration in which the class has the
typedef name for linkage purposes (7.1.3); or
— a named enumeration (7.2), or an unnamed enumeration defined in a
typedef declaration in which the
enumeration has the typedef name for linkage purposes (7.1.3); or
— an enumerator belonging to an enumeration with external linkage; or
— a template, unless it is a function template that has internal
linkage (clause 14); or
— a namespace (7.3), unless it is declared within an unnamed
namespace.

A C++ translation unit consisting entirely of:
int x = 5;
is a declaration and a definition. It declares and defines a variable
with name "x" with namespace scope, and that name has external
linkage. Adding "extern" should be a noop.

However, I note that your files have a ".c" suffix. I don't recall
offhand, but it's possible that the C rules in this regard are
sufficiently different, and it's possible that your compiler
automatically treats any file with the ".c" suffix as C source code
and not C++ source code. Try changing the suffixes to ".cpp" and try
again?

I did just try this with g++ 4.1.2, and it takes it and compiles and
links fine. Why does it not work with yours? It sounds like a non-
conforming C++ compiler (assuming it's not treating it as C code, and
assuming the C rules are sufficiently different, as mentioned above).
 
J

Joshua Maurice

With respect to the other posters in this thread, that is valid C++
code. It should compile and link. (Well, ignoring the missing
"main".)

Also ignoring the bad include. The filename should be in parenthesis,
e.g.
#include "foo.hpp"
not
#include foo.hpp

See the C++ faq on how to post.
www.parashift.com/c++-faq/
Specifically, please post complete actual compilable code which
demonstrates your problem. (Or at least complete code which
demonstrates your problem.)
 
B

barcaroller

With respect to the other posters in this thread, that is valid C++
code. It should compile and link. (Well, ignoring the missing
"main".)

These are just code snippets. I too believe that this is valid C++
code but the linker (not the compiler) disagrees.

A C++ translation unit consisting entirely of:
int x = 5;
is a declaration and a definition. It declares and defines a variable
with name "x" with namespace scope, and that name has external
linkage. Adding "extern" should be a noop.

I agree 100% with that statement but I don't think the other posters do.

However, I note that your files have a ".c" suffix. I don't recall
offhand, but it's possible that the C rules in this regard are
sufficiently different, and it's possible that your compiler
automatically treats any file with the ".c" suffix as C source code
and not C++ source code. Try changing the suffixes to ".cpp" and try
again?

They are in fact *.cpp files

I did just try this with g++ 4.1.2, and it takes it and compiles and
links fine. Why does it not work with yours? It sounds like a non-
conforming C++ compiler (assuming it's not treating it as C code, and
assuming the C rules are sufficiently different, as mentioned above).

I'm using gcc 4.4.x on Linux. Would it be possible for you to post
your code here? I will then compile on my machine.
 
I

Ian Collins

These are just code snippets. I too believe that this is valid C++ code
but the linker (not the compiler) disagrees.



I agree 100% with that statement but I don't think the other posters do.

Joshua is correct, I was wrong.
 
B

barcaroller

Also ignoring the bad include. The filename should be in parenthesis,
e.g.
#include "foo.hpp"
not
#include foo.hpp

See the C++ faq on how to post.
www.parashift.com/c++-faq/
Specifically, please post complete actual compilable code which
demonstrates your problem. (Or at least complete code which
demonstrates your problem.)

I will post the compilable code here tomorrow I didn't want to
clutter the original post with lots of code; my aim was to understand
extern and linkage.
 
J

James Kanze

Because it has local linkage in foo.c

It shouldn't.

It's good practice to have the include in foo.c, in order to
get a compiler error if the declaration in foo.h is somehow
incompatible with the definition in foo.c. But it's not
a language requirement, and a variable defined at namespace
scope has global linkage unless it is declared static.
 
J

James Kanze

Because a variable at file scope only has file scope unless it is
declared extern.

That is completely wrong. A name at namespace scope has
external linkage unless it is explicitly declared static or
const.
 
B

barcaroller

Specifically, please post complete actual compilable code which
demonstrates your problem. (Or at least complete code which
demonstrates your problem.)

Here is the code, as I compiled and linked it (note that I'm using 'const').

Scenario 1: Linker error

// foo.cpp
const int x = 5;

// foo.h
extern const int x;

// bar.cpp
#include "foo.h"

int main()
{
int y = x; // linker error here
}

g++ -c foo.cpp bar.cpp ; g++ foo.o bar.o -o myexec
bar.o: In function `main':
bar.cpp:(.text+0xd): undefined reference to `x'
collect2: ld returned 1 exit status



Scenario 2: Clean build

If I now add one line to foo.cpp:

// foo.cpp
#include "foo.h" // <--- contains extern
const int x = 5;

g++ -c foo.cpp bar.cpp ; g++ foo.o bar.o -o myexec
<no error>


This problem goes away when I don't use 'const'. This leads me to
believe that 'const int' and 'int' are treated differently by the
linker. I believe James mentioned this earlier.
 
I

Ian Collins

Here is the code, as I compiled and linked it (note that I'm using
'const').

If you'd said this on your first post, a lot of wasted bandwidth would
have been saved.
 
J

Joshua Maurice

Here is the code, as I compiled and linked it (note that I'm using 'const').

Scenario 1: Linker error

// foo.cpp
const int x = 5;

// foo.h
extern const int x;

// bar.cpp
#include "foo.h"

int main()
{
    int y = x;  // linker error here

}

g++ -c foo.cpp bar.cpp ; g++ foo.o bar.o -o myexec
bar.o: In function `main':
bar.cpp:(.text+0xd): undefined reference to `x'
collect2: ld returned 1 exit status

Scenario 2:  Clean build

If I now add one line to foo.cpp:

// foo.cpp
#include "foo.h"  // <--- contains extern
const int x = 5;

g++ -c foo.cpp bar.cpp ; g++ foo.o bar.o -o myexec
<no error>

This problem goes away when I don't use 'const'.  This leads me to
believe that 'const int' and 'int' are treated differently by the
linker.  I believe James mentioned this earlier.

Indeed. Specifically:

int x = 5;
and
extern int x;
int x = 5;
and
extern const int x;
const int x = 5;
define a variable at namespace scope with name "x". The name "x" has
external linkage.

However,
const int x = 5;
here the name "x" has internal linkage. This is according to the rules
of the standard, quoted in my first post else-thread.

Also, it's not the linker which treats "const int" and "int"
differently - it's the compiler. By default, the compiler makes
namespace scope names of "const int" variables have internal linkage.
The linker may never even see it. In your case, it appears that the
linker in fact does not even see it, and then the linker complains
because the name "x" cannot be found.

This is explained in the quote of the standard in my first post else-
thread. I'm not sure if you did not read it, or if you didn't
understand it. I hope that you read it, as that might have answered
your question.

This is just as James Kanze has said more recently else-thread.

Finally, this would have been explicitly answered earlier if your
original post had actual complete compile'able code which demonstrated
your linker error.
 
B

barcaroller

Indeed. Specifically:

int x = 5;
and
extern int x;
int x = 5;
and
extern const int x;
const int x = 5;
define a variable at namespace scope with name "x". The name "x" has
external linkage.

However,
const int x = 5;
here the name "x" has internal linkage. This is according to the rules
of the standard, quoted in my first post else-thread.

Also, it's not the linker which treats "const int" and "int"
differently - it's the compiler. By default, the compiler makes
namespace scope names of "const int" variables have internal linkage.
The linker may never even see it. In your case, it appears that the
linker in fact does not even see it, and then the linker complains
because the name "x" cannot be found.

This is explained in the quote of the standard in my first post else-
thread. I'm not sure if you did not read it, or if you didn't
understand it. I hope that you read it, as that might have answered
your question.

This is just as James Kanze has said more recently else-thread.

Finally, this would have been explicitly answered earlier if your
original post had actual complete compile'able code which demonstrated
your linker error.

Thanks for the explanantion. I couldn't have posted the original code
as that is thousands of lines of C++ code, spread over hundreds of
files. I was getting a linker error and posted just the lines that I
thought relevant. I missed the const (and a lot of other code).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,965
Messages
2,570,148
Members
46,710
Latest member
FredricRen

Latest Threads

Top