i am new in programming and i started with c . i am learning in the point How Linkers Resolve Global Symbols Defined at Multiple Places. so i made a small program which says:
//main.c
#include <stdio.h>
void b(void);
int x;
int a=10;
int y =500;
int x1 = 2016;
int main()
{
b();
printf("x = %1f y = %d\n",x , y);
return 0;
}
//second_file.c
double x=100.0;
int g=100;
extern int y;
void b()
{
x = -100.0;
}
i know from the rules that linkers follow to solve this case that, the linker will choose the strong symbol which in this case (double x=100.0;) not the integer one defined in main.c. so i expected the output to be x=-100.0 y=500, but the output was x=0.000000 y=89 can any one explain why did i find this output or where is my wrong?
i tried to use objdump command in cmd (for windows) to view the symbol table may i find something make my understand but i found another thing weird i found the addresses of the variables as follow:
a------ 0x00000000
y------ 0x00000004
x1----- 0x00000008
x------ 0x00000010
g------ 0x00000018
i thought that x should start from 0x0000000c why not?
The declaration in main.c
int x;
has nothing to do with the one in second_file.c
double x=100.0;
because their scope (visibility) is limited to its compilation unit. That is, their are globals but (roughly) only visible along functions in the same file.
In case you wanted both files to share the same global variable you have to specify it by using extern
keyword:
extern int x;
in one of the files, and that will force the linker to match a symbol at linking time that implements that declaration.
Don't forget: you can declare a variable (consistently in type) multiple times but only define it once. To avoid taking this answer too long, try to search in stackoverflow for the best approach for using globals in multiple files; it's much better to work via *.h for declarations an of course implement it in one *.c file.
Finally, in this example anyway the linker will not match the int x
declaration with the double x
definition, and it will throw an error at linking time.
As you may or may not know, you need to be careful with multiple definitions. Ideally, every global symbol will be defined precisely once. There are various circumstances under which you can get away with multiple definitions, but you have to be careful.
In this case, you're relying on the "common allocation model", which was (and still is) widespread among C compiler because of the early influence of, believe it or not, FORTRAN. This model says that you can have multiple definitions as long as at most one of gives an initializing value. But -- and this is the biggie -- all the definitions must have the same type. (I'm not sure I've ever seen this rule stated; I'm not sure I'd even thought about it explicitly, because it ends up being pretty obvious that it has to be that way.)
C uses the concept of separate compilation. Each .c file compiles down to a separate, standalone "object file". Later, a separate program called the linker links the object files together. It's the linker that assigns the final address to global variables, and ends up resolving common allocation.
But the linker only resolves addresses. It will arrange that x
in your main.c
and x
in your second_file.c
are at the same address. But if main.c
thinks there's an int
located at location x
, that's how it's going to interpret it. And if second_file.c
thinks there's a double
at location x
, and indeed stores a value of type double
there, it's going to be gibberish when main.c
tries to interpret that bit pattern as an int
.
As @user58697 discusses in another answer, you've got additional problems in your printf
call, trying to use %f
to print (what the compiler thinks are) int
values, which is never going to work properly, either.
First, in the line
printf("x = %1f y = %d\n",x , y);
you lied to printf
. You told the compiler to push an int
(apparently 4 bytes worth), but instructed printf
to pull a double
(apparently 8 bytes). This is an UB. All bets are off. Nobody knows where printf
would pull y
from.
And this is what happens with x
:
vnp$ nm -n main.o
U _b
U _printf
0000000000000000 T _main
0000000000000004 C _x
0000000000000040 D _a
0000000000000044 D _y
0000000000000048 D _x1
You can see that _x
is annotated with C
(for Common
). The compiler took int x;
as a definition, realized that x
is an uninitialized global, and placed it in BSS
. By the time it compiled the
printf("x = %1f y = %d\n",x , y);
line, it generated a relocation record relative to the .bss
.
The linker, however, selected x
from the second file, and which is placed in .data
, but performed the relocation as directed, that is relative to .bss
. This is why x
prints as 0
.
Disclaimer: since there is an UB, the above is a pure (however educated) speculation.
PS: the address of x
is due to the alignment requirement or double
.
User contributions licensed under CC BY-SA 3.0