1

I understand that file-scope variables in C are by default extern - even if this isn't specified explicitly (as explained in this answer to Global variables in C are static or not?).

However, I also know that if a.h declared extern int x = 10;, and b.c wants to access x, it needs to declare it as extern.

Why is it so? Why not just access x without any additional extern stuff? What is the technical explanation of this mechanism?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Aviv Cohn
  • 15,543
  • 25
  • 68
  • 131
  • *I understand that file-scope variables in C are by default extern* - no they are not. They *can* be `extern`ed in other translation units unless they are `static`. `h` files have a little to do with it as they simply textually pasted into the sources. If you have `extern int x` in it, it is the same as you would put it in the including c file. – Eugene Sh. May 17 '19 at 15:53
  • See also [How do I use `extern` to share variables between source files?](https://stackoverflow.com/questions/1433204/) – Jonathan Leffler May 17 '19 at 18:00
  • Note that when set to fussy (with `-Werror`), GCC (9.1.0) says: ```ext59.c:1:12: error: ‘x’ initialized and declared ‘extern’ [-Werror]``` when asked to compile `extern int x = 10;` — you should not combine `extern` with an initializer. (Without `-Werror`, its just a warning that appears even with no requests for warnings.) – Jonathan Leffler May 17 '19 at 18:08

3 Answers3

1

Because the variable is external to the file; it is defined in another file, and you just tell that file that it exists, but it won't find it there. Then it's the job of the linker to solve that.

Example:

// a.c
int x = 7;

a.c has the variable defined

// a.h
extern int x;

a.h has knowledge that the variable exists, but it doesn't know where. Any file that includes a.h will gain that knowledge

// b.c
#include "a.h"

b.c, because it has included a.h, now has knowledge that the variable x exists, but doesn't know where.

The linker will resolve those different usages of the same variable x. They better be the same, or there will be problems.

You could lie to a.h, and write a float instead of int, and only the linker may notice that, because the compiler literally has no knowledge about a.c (well, a.c should include a.h, so it will notice, but you could lie to it if you don't include it)

In the whole project there must be one and only one non-extern definition of each variable. 0 or 2 or more, and the linker will report an error.

1

Storage for a global variable is only allocated once. For example, in main.c:

int gMyCounter;

In any other module (C-file) where you want to use or acess this variable, you must tell the compiler about its existence and its type. You do that with the extern keyword, for example in mymodule.c:

extern int gMyCounter;

During link time, the linker sees that mymodule.o needs variable gMyCounter and searches for it in the other .o files and libraries. It finds it in main.o.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • So if I understand correctly - declaring a variable with `extern` means "this variable is defined somewhere else". And the place where it *is* defined *doesn't* need to say `extern`? – Aviv Cohn May 17 '19 at 16:05
  • 1
    @AvivCohn For `gMyCounter` declared at file-scope with external linkage: (1) `extern int gMyCounter;` (no initializer) means it is defined elsewhere; (2) `int gMyCounter;` (no initializer) means it is _tentatively defined_ here, but might be fully defined elsewhere; (3) `extern int gMyCounter = 0;` and `int gMyCounter = 0;` (with initializer) both mean it is fully defined here. The `extern` keyword is redundant if there is an initializer. – Ian Abbott May 17 '19 at 16:23
  • @IanAbbott, so `extern int gMyCounter=1;` in `mymodule.c` would allocate storage for it _again_? (As `extern int gMyCounter;` in main.c would also allocate storage?). Will that lead to a linker complaint about a multiply defined symbol? – Paul Ogilvie May 17 '19 at 16:33
  • @PaulOgilvie `extern int gMyCounter;` doesn't allocate storage, but `extern int gMyCounter=1;` does. If a variable is defined in more than 1 translation unit, the linker will complain except on systems that place a translation unit's unresolved, tentatively defined variables (which will be automatically initialized to 0) in a BSS (block shared storage) section and collapse multiple definitions of the same unresolved, tentatively defined variable from different translation units to a single variable. That is outside the scope of the C standard, but is pretty commonplace, inherited from Fortran. – Ian Abbott May 17 '19 at 17:01
  • @IanAbbott: Typically multiple tentative definitions and one or zero normal definitions will be resolved, but multiple normal definitions will result in an error. – Eric Postpischil May 17 '19 at 17:08
  • @EricPostpischil Yes, that's correct. Thanks for clarifying. My excuse is that I ran out of space in the comment. To be clear, the linker may collapse any automatically initialized variables with external linkage with the same name from different translation units to a single variable per name. – Ian Abbott May 17 '19 at 17:10
  • @EricPostpischil It appears you are correct about combining tentative and normal definitions of the same variable in different translation units too. Thanks again for the clarification. (Now I need to work out how that works behind the scenes.) – Ian Abbott May 17 '19 at 17:23
0

The header file a.h should not contain

extern int x = 10;

but just

extern int x;

Then the file a.c should define

int x = 10;

and when file b.c wants to use x it should simply

#include "a.h"

and the compiler, when compiling b.c will know that x is defined somewhere else, and the linker will find the reference and fix it up.


From the added link edit: if the definition is in a compilation unit which is not seen by the compiler then the variable does not magically become available: you need to declare it as extern where the compiler can see, so it knows that it will be available. It serves somewhat the same purpose as a function prototype: it's a delaration, not a definition.
Weather Vane
  • 33,872
  • 7
  • 36
  • 56