linking problem? | extern keyword

S

Stanley Rice

Dear all

I got a question about the key word 'extern', In f1.c, I define a
variable as below:
--------------f1.c--------------
float var = 3.0F;

but in file main.c, I declare it as type double
---------------main.c-------------
extern double var;
......
printf("%f\n", var);
.....

At first, I guess that the compiler(gcc) will shout to me, as 'var' in
f1.c is defined in type float, but I declare the var in main.c with
type double, which is inconsistant. However, the compiler happily
accept it. But the result isn't what I expect. It prints 0.000000
instead of 3.0, and I couldn't find the reason here.

For another try, I define a variable as below:
--------------f2.c------------------
unsigned int var_int = 4;

in another file, say, main.c, I declare it as below
-------------main.c----------------
extern int var_int;
.....
printf("%d\n", var_int);

This time, define var_int in type 'unsigned int' but declare it in
type 'int' in another file, the printed result is 4, which is
consistant with what it is previously defined.

Could anyone what happens? why doesn't the compiler complains, and why
the two results differs. Thanks in advance.
 
I

Ike Naar

I got a question about the key word 'extern', In f1.c, I define a
variable as below:
--------------f1.c--------------
float var = 3.0F;

but in file main.c, I declare it as type double
---------------main.c-------------
extern double var;
.....
printf("%f\n", var);
....

At first, I guess that the compiler(gcc) will shout to me, as 'var' in
f1.c is defined in type float, but I declare the var in main.c with
type double, which is inconsistant. However, the compiler happily
accept it. But the result isn't what I expect. It prints 0.000000
instead of 3.0, and I couldn't find the reason here.

The usual way to solve this problem is to use a header file,
say f1.h, that contains the external declarations of f1.c .

/* f1.h */
extern float var;

Then #include f1.h in f1.c and in every file that uses var.
So, in main.c, instead of having

extern double var;

use

#include "f1.h"
 
J

Jens Thoms Toerring

Stanley Rice said:
I got a question about the key word 'extern', In f1.c, I define a
variable as below:
--------------f1.c--------------
float var = 3.0F;
but in file main.c, I declare it as type double
---------------main.c-------------
extern double var;
.....
printf("%f\n", var);
....
At first, I guess that the compiler(gcc) will shout to me, as 'var' in
f1.c is defined in type float, but I declare the var in main.c with
type double, which is inconsistant. However, the compiler happily
accept it.

That was to be expected. When the compiler compiles main.c
it has no idea what is in f1.c and vice versa. Remember,
the compiler is always working on a single C file and it
has to trust you if you tell it that you defined a vari-
able in another file and that it has the correct type.
But the result isn't what I expect. It prints 0.000000
instead of 3.0, and I couldn't find the reason here.

Well, when the compiler is finished the linker got to work
out things. And the compiler tells the linker that for
main.o it needs a variable with the name 'var' from some-
where else, but it doesn't tell the linker about the type,
that's something you must have gotten right. And since the
linker finds a variable named 'var' it's quite happy and
also can't detect this error. But when the program finally
is run it will try to access 'var' in main.c as a double
despite it being only a float. The result is anyones guess
since the sizes of float and double are rather likely to
be different as are their bit representations.
For another try, I define a variable as below:
--------------f2.c------------------
unsigned int var_int = 4;
in another file, say, main.c, I declare it as below
-------------main.c----------------
extern int var_int;
....
printf("%d\n", var_int);
This time, define var_int in type 'unsigned int' but declare it in
type 'int' in another file, the printed result is 4, which is
consistant with what it is previously defined.

Here's the same problem but since 'int' and 'unsigned int'
have the same size and more or less the same bit represen-
tation (i.e. if you compare the bit pattern for an int set
to 4 and that of an unsigned int, also set to 4 they will
be the same) you just get away with it

If you would have done it the other way round, i.e define
'var' as an int and set it to a negative value and then
declare 'var' in main.c as unsigned int you would have
gotten some strange number printed out.
Could anyone what happens? why doesn't the compiler complains, and why
the two results differs. Thanks in advance.

The compiler can't complain because it doesn't know any-
thing about the "real" 'var' variable while its com-
piling main.c (if it would know about it the 'extern'
declaration would be rather redundant). And this leads
to your program using a float variable as if it were a
double, which it isn't.
Regards, Jens
 
J

James Kuyper

Dear all

I got a question about the key word 'extern', In f1.c, I define a
variable as below:
--------------f1.c--------------
float var = 3.0F;

but in file main.c, I declare it as type double
---------------main.c-------------
extern double var;
.....
printf("%f\n", var);
....

At first, I guess that the compiler(gcc) will shout to me, as 'var' in
f1.c is defined in type float, but I declare the var in main.c with
type double, which is inconsistant. However, the compiler happily
accept it. But the result isn't what I expect. It prints 0.000000
instead of 3.0, and I couldn't find the reason here.

The reason is that such a mis-match makes the behavior of your program
undefined. When you have separate compile-link phases, it's not possible
for the compiler to notice this discrepancy, it doesn't have enough
information. In principle, a linker could generate such a message, but
the C standard does not require it, and most will not provide one.
For another try, I define a variable as below:
--------------f2.c------------------
unsigned int var_int = 4;

in another file, say, main.c, I declare it as below
-------------main.c----------------
extern int var_int;
....
printf("%d\n", var_int);

This time, define var_int in type 'unsigned int' but declare it in
type 'int' in another file, the printed result is 4, which is
consistant with what it is previously defined.

Could anyone what happens? why doesn't the compiler complains, and why
the two results differs. Thanks in advance.

In principle, the behavior is just as undefined in this case as in the
other. However, when the behavior is undefined, that means that anything
is permitted by the C standard, including having the program do exactly
what you incorrectly thought it was required to do. In this case, since
the C standard mandates that positive integer values have exactly the
same representation in the corresponding unsigned type, it is very
likely to work on most implementations. No such requirement applied in
the double/float case.
 
Q

Quentin Carbonneaux

Dear all

I got a question about the key word 'extern', In f1.c, I define a
variable as below:
--------------f1.c--------------
float var = 3.0F;

but in file main.c, I declare it as type double
---------------main.c-------------
extern double var;
.....
printf("%f\n", var);
....

At first, I guess that the compiler(gcc) will shout to me, as 'var' in
f1.c is defined in type float, but I declare the var in main.c with
type double, which is inconsistant. However, the compiler happily
accept it. But the result isn't what I expect. It prints 0.000000
instead of 3.0, and I couldn't find the reason here.

For the sake of separate compilation no tests are done regarding
interfaces during linkage of the separate objects of your program.

The behavior of your program is unspecified as stated in the standard
(6.2.7p2):

All declarations that refer to the same object shall have compatible
type; otherwise, the behavior is undefined.

Thus, it is no use to interpret the results of your program.
 
S

Stanley Rice

The usual way to solve this problem is to use a header file,
say f1.h, that contains the external declarations of f1.c .

/* f1.h */
extern float var;

Then #include f1.h in f1.c and in every file that uses var.
So, in main.c, instead of having

  extern double var;

use

  #include "f1.h"

Yeah, you are right. And I know how to solve the problem as you said
above. Here I declare 'var' deliberately in type double, and want to
know why the compiler happily accept it and how it works.
 
J

James Kuyper

....
Yeah, you are right. And I know how to solve the problem as you said
above. Here I declare 'var' deliberately in type double, and want to
know why the compiler happily accept it and how it works.

I thought that your question had already been answered, but you wrote
"want to know" in the present tense, implying that you're still looking
for something more than the answers you've already received. Here's
another explanation, I hope it may help:

The compiler happily accepts your modules because it never sees the
discrepancy; it only sees f1.c, or main.c; it never looks at both at the
same time. That's why the solution is to put the declaration in a header
file that is shared by both translation units.

The linker could have enough information to detect the problem, if the
object file format permits the storage of the relevant information, and
if the compiler provides it. However, such information is not needed for
the linker to do it's main job, and is therefore not necessarily
available to it. That's part of the reason why the C standard does not
require generation of any diagnostic message for this kind of problem.

As for how it works - the definition of var in f1.c causes a piece of
memory to be set aside that is sufficiently large and correctly aligned
to store an object of type 'float'. That piece of memory is extremely
unlikely to be large enough to store an object of type 'double', and
might not be correctly aligned to store one.

The declaration of var as a double with external linkage in main.c
causes the linker to determine the address reserved for an identifier
with external linkage named 'var' and to insert that address into the
locations in the code implementing main() that need the address of 'var'
(I speak of the way linkers work on the systems I'm most familiar with -
there probably are other ways that can be used to achieve the same
effect). It doesn't need to know the type of thing stored at that
address in order to do this.

When executing the instructions implementing main() that are supposed to
refer to value of 'var', the program tries to retrieve the value of an
object of type 'double' that it assumes is stored that address. Since
there is no such object, this is where things are likely to go wrong.
The most likely situation is that it looked at sizeof(double) bytes of
memory, starting with the bytes storing the f1.c 'var', but also
including additional bytes of memory that may have been allocated for
some other purpose entirely. Those bytes might not even be ones that
your process has permission to access. If it does have such permissions,
it will interpret those bytes as if they contained a representation of a
'double' value; they need not contain a valid representation, and are
extremely unlikely to contain a valid representation of the same value
that was stored by f1.c in 'var'.

Does that answer your question?
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top