Search code examples
cextern

Trying to demonstrate a case where extern for variables is necessary in C


I am trying to understand extern. According to one of the top answers to How to correctly use the extern keyword in C

it's in order to solve the problem of multiple inclusions of a header file resulting in multiple copies of the same variable and thus leading to, presumably, a linking error.

So I tried it by creating the following files:

count.h

int count;

count.c

int count = 0;

add.h

int sum(int x, int y);

add.c

#include "count.h"
int sum(int x, int y){
count = count + 1;
return x+y;}

sub.h

int sub(int x, int y);

sub.c

#include "count.h"
int sub(int x, int y){
count = count + 1;
return x - y;
}

main.c

#include "count.h"
#include "add.h"
#include "sub.h"
#include <stdio.h>

int main(){
  printf("%d\n", count);
  printf("%d\n", sub(100,1));
  printf("%d\n", count);
  printf("%d\n", add(100,1));
  printf("%d\n", count);
}

This compiles and runs fine with output:

0
99
1
101
2

I get the same output with or without extern in the original count.h file. So what am I missing in the answer?

Now, I thought the answer is that "I'm just declaring multiple copies of count" since there are no header guards, and that is OK because multiple declarations are OK, whereas multiple definitions are not. But then if that's the case, I would expect the following to compile, but it does not since I am "redefining count."

int main(){
   int count;
   int count; 
   int count = 0;
}

According to this answer, int count does count as a definition. In C, is it valid to declare a variable multiple times?


Solution

  • Your

    int count;
    

    in count.h is actually a definition, not a mere declaration. It is a tentative definition, but as every tentative definition it gives birth to a normal full-blown definition of int count at the end of each translation unit that contains the above.

    So, by including your count.h into multiple translation units you produced multiple definitions of external object int count in your program, which is formally illegal in standard C. It "compiles and runs fine" only because of a compiler extension implemented by many modern C compilers.

    According to Rationale for International Standard — Programming Languages — C (see section 6.2.2 Linkages of identifiers), this used to be OK in the relaxed legacy declaration/definition model employed by Unix compilers back in the day (so called "Relaxed Ref/Def" model). However, more formal K&R C rejected Unix model and replaced it with so called "Strict Ref/Def" model, which already allowed only one definition. Later, the standardization committee settled on a mix of "Strict Ref/Def" model and "Initialization" model, which still allows only one definition. Yet, popularity of "Relaxed Ref/Def" in old Unix compilers is the reason many modern compilers continue to support this model as an extension.

    In order to make your program compliant with the requirements of standard C you have to remove the definition of int count from the header file and replace it with a non-defining declaration

    extern int count;
    

    This is what makes extern useful with variable declarations.