4

I'm trying to understand the ways in which a C global variable can be shared between multiple files (compilation units). I've read the excellent question and answer here. However after doing a few tests I'm still left with some stuff I don't get:

Basically my question would be: if there's a variable declared (but not defined) in a header WITHOUT the extern keyword, is it ok to simply include that header in various compilation units in order to make available that variable to all those compilation units? In this scenario it's implied that one (and only one) compilation unit contains the code for initializing (defining?) that variable, and it will be called first before the other compilation units try to do anything with that variable. If all this is true, is this procedure what's called "implicit extern" ?

I'll illustrate my question with an example:

Header "MyCommonHeader.h" contains:

//MyCommonHeader.h
int* i; //pointer to an int

File MyFirstHeader.h contains:

//MyFirstHeader.h
void changeIt(int newValue);

File MyFirstSource.c contains:

//MyFirstSource.c
#include "MyFirstHeader.h"

 void changeIt(int newValue) {
    *i = newValue;
 }

File MySecondSource.c contains:

//MySecondSource.c
#include "MyCommonHeader.h"
#include "MyFirstHeader.h"

void main() {
   i = malloc(sizeof(int));
   changeIt(10);
   *i = 23;
}

Does the above code operates with the same i variable everywhere? Do I need to add externanywhere?

Community
  • 1
  • 1
Shivan Dragon
  • 15,004
  • 9
  • 62
  • 103

4 Answers4

6
/* file.h */
int* i;

is a tentative definition of the i variable. This means that if there is no other (external) definition for that variable in the translation unit, it will be defined just once (initialized to 0). If there is exactly one matching (external) definition of i elsewhere in the translation unit, that definition will be used, and the tentative definition above will behave as a declaration.

As a common extension, compilers extend this behavior across translation units. This means, for such compilers, you can safely include that header file in as many translation units as you want, and there would still be only one definition of i.

It would have been different if you had also explicitly initialized i in the header file :

/* file.h */
int* i = 0;

This is an actual definition (not tentative), and you can only include that header file in one compilation unit, or you'd get a multiple definition error.

The better way, is to define the variable in a .c file, and use extern in the header file :

/* file.h */
extern int* i;

/* file.c */
int* i = 0;

This makes it absolutely clear that there is only one definition (the one in the .c file), and that every compilation unit where the header file is included will refer to that definition.

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
  • Thank you, now I get it. Also this explains (to me) what "tentative definition" means. – Shivan Dragon May 14 '13 at 12:08
  • Just keep in mind that this is not standard - it's just a common extension added by C compilers (I'll add this to the body of my answer to avoid confusion). – Sander De Dycker May 14 '13 at 12:17
  • A ok, so if I were do use a standard C compiler, i'd have to use extern on the shared variable, no matter where and how I define it? – Shivan Dragon May 14 '13 at 12:19
  • 1
    If you don't want to rely on an extension of your compiler, and want to keep your code portable, then yes : add the `extern` keyword. I'd recommend doing so. – Sander De Dycker May 14 '13 at 12:27
1

If you declare your variable in a header and have any source file declaring this same variable too, each compilation unit will have a different instance of this variable.

Declaring it in one single compilation unit, and adding an "extern ..." in your header will make all compilation units access the same global variable.

yoones
  • 2,394
  • 1
  • 16
  • 20
  • Ok, so if I understand you correctlly, the example code above, because it delcares a non external i in the header, then initializes it in one compilation unit but modifies it in another is wrong? And following on that, the fact that when I run this code printing the value of i in each compilation unit and get the same value everywhere is just a lucky coincidence and in fact will eventually get me into trouble (because each .c file has in fact it's own i)? – Shivan Dragon May 14 '13 at 11:53
1
Does the above code operates with the same i variable everywhere?

Yes.

Do I need to add externanywhere?

In this example, not needed. Because MyCommonHeader.h is included only once where i is defined.

In the following example, extern is more useful.

//main.c

#include <stdio.h>
int globaldata;
int main()
{
..
}

//Includes.h    
extern int globaldata;


//Feature1.c
#include "Includes.h"

int func()
{
   globaldata++;
}

//Feature2.c
#include "Includes.h"

int func_new()
{
   globaldata = globaldata * 100;
}

globaldata is a global variable used by feature1.c and feature2.c. If u define that variable, then it will be error, like multiple declaration error.

so always extern is used in this scnario and that header only will be included.

Jeyaram
  • 9,158
  • 7
  • 41
  • 63
1

Declaring in a header file which is then included in multiple places, you'll end up with multiple instances of i (and potentially compile or link problems).so it's a better idea to use extern in header and define it in one .c file .After this you can include header without repeting variables definition.

Dayal rai
  • 6,548
  • 22
  • 29
  • It's correct and normal to declare in a header file and include the header file in multiple places. And the way to declare a variable is: extern int i; – Adrian Liu Jul 28 '17 at 05:55