1

I'm reading a textbook which says:

we saw how the compiler assigns symbols to COMMON and .bss using a seemingly arbitrary convention. Actually, this convention is due to the fact that in some cases the linker allows multiple modules to define global symbols with the same name.

But isn't that you can only define a variable once? in which cases linker allow you to define a variable more than once?

  • Linkers can support features that C does not. COMMON sections have existed for a long time with FORTRAN, so linkers support them. That does not mean the C standard fully supports this. The C standard makes some allowance for this by saying it does not define what happens when you define something more than once, including via *tentative definitions*. Individual compilers and linkers may fill in this opportunity left by the C standard to define the behavior. – Eric Postpischil Aug 29 '20 at 02:11

2 Answers2

0

This comes around all the time, so here is an actual build which covers the permutations of this:

#- Makefile -----
all: 1 2 3 12 13 23
    -./1
    -./2
    -./3
    -./12
    -./13
    -./23
1: main.o 1.o; $(CC) main.o 1.o -o $@
2: main.o 2.o; $(CC) main.o 2.o -o $@
3: main.o 3.o; $(CC) main.o 3.o -o $@
12: main.o 1.o 2.o; $(CC) main.o 1.o 2.o -o $@
13: main.o 1.o 3.o; $(CC) main.o 1.o 3.o -o $@
23: main.o 2.o 3.o; $(CC) main.o 2.o 3.o -o $@
/* ----  1.c ----- */
int num;
/* ----  2.c ----- */
extern int num;
/* ----  3.c ----- */
int num = 2;
/* ---- main.c --- */
#include <stdio.h>

extern int num;

int main() {
    printf("%d\n", num);
    return 0;
}

Ok, and after you have surgically deconstructed this SO-archive, and run:

make -i all
You should get an output something like:
cc main.o 2.o -o 2
Undefined symbols for architecture x86_64:
  "_num", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: [2] Error 1 (ignored)
./1
0
./2
make: ./2: No such file or directory
make: [all] Error 1 (ignored)
./3
2
./12
0
./13
2
./23
2

So, first up, the program 2 doesn't build. Why not? Because 2.c contains an explicit declaration of an external variable (num); so when linked with main.c, both have expressed an interest in num; but neither have defined it.

In contrast, both ./1 and ./12 have managed to define it, but have not assigned it a value, thus it is defined as zero. The lack of extern in 1.c provides the impetus for the linker to shrug and say, ok, I will just allocate space for it in the zero section (bss).

./3 ./13 ./23 demonstrate that in the presence of a defining statement (int num = 2;) we see the expected result.

What is left out, but may be a good exercise, is what happens if you: $(CC) main.c 1.c 1.c 1.c -o 111? Do you get an error? Why not?

mevets
  • 10,070
  • 1
  • 21
  • 33
-1

This might shed some light. Short version: You can "tentatively declare" a variable as many times as you'd like. I.E
int i;
But you can only "externally declare" it ONCE
int i = 1;
You'd have to read up on the documentation or wait for a more detailed answer for more information. This was just what i found on the topic right now.

joachimbf
  • 106
  • 8
  • `int i;` , this is uninitialized global variable , can uninitializing a global variable called a definition of a variable? –  Aug 29 '20 at 02:06