1226

I know that global variables in C sometimes have the extern keyword. What is an extern variable? What is the declaration like? What is its scope?

This is related to sharing variables across source files, but how does that work precisely? Where do I use extern?

Jason
  • 36,170
  • 5
  • 26
  • 60

19 Answers19

2137

Using extern is only of relevance when the program you're building consists of multiple source files linked together, where some of the variables defined, for example, in source file file1.c need to be referenced in other source files, such as file2.c.

It is important to understand the difference between defining a variable and declaring a variable:

  • A variable is declared when the compiler is informed that a variable exists (and this is its type); it does not allocate the storage for the variable at that point.

  • A variable is defined when the compiler allocates the storage for the variable.

You may declare a variable multiple times (though once is sufficient); you may only define it once within a given scope. A variable definition is also a declaration, but not all variable declarations are definitions.

Best way to declare and define global variables

The clean, reliable way to declare and define global variables is to use a header file to contain an extern declaration of the variable.

The header is included by the one source file that defines the variable and by all the source files that reference the variable. For each program, one source file (and only one source file) defines the variable. Similarly, one header file (and only one header file) should declare the variable. The header file is crucial; it enables cross-checking between independent TUs (translation units — think source files) and ensures consistency.

Although there are other ways of doing it, this method is simple and reliable. It is demonstrated by file3.h, file1.c and file2.c:

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

#include "file3.h"  /* Declaration made available here */
#include "prog1.h"  /* Function declarations */

/* Variable defined here */
int global_variable = 37;    /* Definition checked against declaration */

int increment(void) { return global_variable++; }

file2.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

That's the best way to declare and define global variables.


The next two files complete the source for prog1:

The complete programs shown use functions, so function declarations have crept in. Both C99 and C11 require functions to be declared or defined before they are used (whereas C90 did not, for good reasons). I use the keyword extern in front of function declarations in headers for consistency — to match the extern in front of variable declarations in headers. Many people prefer not to use extern in front of function declarations; the compiler doesn't care — and ultimately, neither do I as long as you're consistent, at least within a source file.

prog1.h

extern void use_it(void);
extern int increment(void);

prog1.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

int main(void)
{
    use_it();
    global_variable += 19;
    use_it();
    printf("Increment: %d\n", increment());
    return 0;
}
  • prog1 uses prog1.c, file1.c, file2.c, file3.h and prog1.h.

The file prog1.mk is a makefile for prog1 only. It will work with most versions of make produced since about the turn of the millennium. It is not tied specifically to GNU Make.

prog1.mk

# Minimal makefile for prog1

PROGRAM = prog1
FILES.c = prog1.c file1.c file2.c
FILES.h = prog1.h file3.h
FILES.o = ${FILES.c:.c=.o}

CC      = gcc
SFLAGS  = -std=c11
GFLAGS  = -g
OFLAGS  = -O3
WFLAG1  = -Wall
WFLAG2  = -Wextra
WFLAG3  = -Werror
WFLAG4  = -Wstrict-prototypes
WFLAG5  = -Wmissing-prototypes
WFLAGS  = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5}
UFLAGS  = # Set on command line only

CFLAGS  = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS}
LDFLAGS =
LDLIBS  =

all:    ${PROGRAM}

${PROGRAM}: ${FILES.o}
    ${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}

prog1.o: ${FILES.h}
file1.o: ${FILES.h}
file2.o: ${FILES.h}

# If it exists, prog1.dSYM is a directory on macOS
DEBRIS = a.out core *~ *.dSYM
RM_FR  = rm -fr

clean:
    ${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}


Guidelines

Rules to be broken by experts only, and only with good reason:

  • A header file only contains extern declarations of variables — never static or unqualified variable definitions.

  • For any given variable, only one header file declares it (SPOT — Single Point of Truth).

  • A source file never contains extern declarations of variables — source files always include the (sole) header that declares them.

  • For any given variable, exactly one source file defines the variable, preferably initializing it too. (Although there is no need to initialize explicitly to zero, it does no harm and can do some good, because there can be only one initialized definition of a particular global variable in a program).

  • The source file that defines the variable also includes the header to ensure that the definition and the declaration are consistent.

  • A function should never need to declare a variable using extern.

  • Avoid global variables whenever possible — use functions instead.

The source code and text of this answer are available in my SOQ (Stack Overflow Questions) repository on GitHub in the src/so-0143-3204 sub-directory.

If you're not an experienced C programmer, you could (and perhaps should) stop reading here.

Not so good way to define global variables

With some (indeed, many) C compilers, you can get away with what's called a 'common' definition of a variable too. 'Common', here, refers to a technique used in Fortran for sharing variables between source files, using a (possibly named) COMMON block. What happens here is that each of a number of files provides a tentative definition of the variable. As long as no more than one file provides an initialized definition, then the various files end up sharing a common single definition of the variable:

file10.c

#include "prog2.h"

long l;   /* Do not do this in portable code */

void inc(void) { l++; }

file11.c

#include "prog2.h"

long l;   /* Do not do this in portable code */

void dec(void) { l--; }

file12.c

#include "prog2.h"
#include <stdio.h>

long l = 9;   /* Do not do this in portable code */

void put(void) { printf("l = %ld\n", l); }

This technique does not conform to the letter of the C standard and the 'one definition rule' — it is officially undefined behaviour:

J.2 Undefined behavior

An identifier with external linkage is used, but in the program there does not exist exactly one external definition for the identifier, or the identifier is not used and there exist multiple external definitions for the identifier (6.9).

§6.9 External definitions ¶5

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.161)

161) Thus, if an identifier declared with external linkage is not used in an expression, there need be no external definition for it.

However, the C standard also lists it in informative Annex J as one of the Common extensions.

J.5.11 Multiple external definitions

There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).

Because this technique is not always supported, it is best to avoid using it, especially if your code needs to be portable. Using this technique, you can also end up with unintentional type punning.

If one of the files above declared l as a double instead of as a long, C's type-unsafe linkers probably would not spot the mismatch. If you're on a machine with 64-bit long and double, you'd not even get a warning; on a machine with 32-bit long and 64-bit double, you'd probably get a warning about the different sizes — the linker would use the largest size, exactly as a Fortran program would take the largest size of any common blocks.

Note that GCC 10.1.0, which was released on 2020-05-07, changes the default compilation options to use -fno-common, which means that by default, the code above no longer links unless you override the default with -fcommon (or use attributes, etc — see the link).


The next two files complete the source for prog2:

prog2.h

extern void dec(void);
extern void put(void);
extern void inc(void);

prog2.c

#include "prog2.h"
#include <stdio.h>

int main(void)
{
    inc();
    put();
    dec();
    put();
    dec();
    put();
}
  • prog2 uses prog2.c, file10.c, file11.c, file12.c, prog2.h.

Warning

As noted in comments here, and as stated in my answer to a similar question, using multiple definitions for a global variable leads to undefined behaviour (J.2; §6.9), which is the standard's way of saying "anything could happen". One of the things that can happen is that the program behaves as you expect; and J.5.11 says, approximately, "you might be lucky more often than you deserve". But a program that relies on multiple definitions of an extern variable — with or without the explicit 'extern' keyword — is not a strictly conforming program and not guaranteed to work everywhere. Equivalently: it contains a bug which may or may not show itself.

Violating the guidelines

There are, of course, many ways in which these guidelines can be broken. Occasionally, there may be a good reason to break the guidelines, but such occasions are extremely unusual.

faulty_header.h

int some_var;    /* Do not do this in a header!!! */

Note 1: if the header defines the variable without the extern keyword, then each file that includes the header creates a tentative definition of the variable. As noted previously, this will often work, but the C standard does not guarantee that it will work.

broken_header.h

int some_var = 13;    /* Only one source file in a program can use this */

Note 2: if the header defines and initializes the variable, then only one source file in a given program can use the header. Since headers are primarily for sharing information, it is a bit silly to create one that can only be used once.

seldom_correct.h

static int hidden_global = 3;   /* Each source file gets its own copy  */

Note 3: if the header defines a static variable (with or without initialization), then each source file ends up with its own private version of the 'global' variable.

If the variable is actually a complex array, for example, this can lead to extreme duplication of code. It can, very occasionally, be a sensible way to achieve some effect, but that is very unusual.


Summary

Use the header technique I showed first. It works reliably and everywhere. Note, in particular, that the header declaring the global_variable is included in every file that uses it — including the one that defines it. This ensures that everything is self-consistent.

Similar concerns arise with declaring and defining functions — analogous rules apply. But the question was about variables specifically, so I've kept the answer to variables only.

End of Original Answer

If you're not an experienced C programmer, you probably should stop reading here.


Late Major Addition

Avoiding Code Duplication

One concern that is sometimes (and legitimately) raised about the 'declarations in headers, definitions in source' mechanism described here is that there are two files to be kept synchronized — the header and the source. This is usually followed up with an observation that a macro can be used so that the header serves double duty — normally declaring the variables, but when a specific macro is set before the header is included, it defines the variables instead.

Another concern can be that the variables need to be defined in each of a number of 'main programs'. This is normally a spurious concern; you can simply introduce a C source file to define the variables and link the object file produced with each of the programs.

A typical scheme works like this, using the original global variable illustrated in file3.h:

file3a.h

#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable;

file1a.c

#define DEFINE_VARIABLES
#include "file3a.h"  /* Variable defined - but not initialized */
#include "prog3.h"

int increment(void) { return global_variable++; }

file2a.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

The next two files complete the source for prog3:

prog3.h

extern void use_it(void);
extern int increment(void);

prog3.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

int main(void)
{
    use_it();
    global_variable += 19;
    use_it();
    printf("Increment: %d\n", increment());
    return 0;
}
  • prog3 uses prog3.c, file1a.c, file2a.c, file3a.h, prog3.h.

Variable initialization

The problem with this scheme as shown is that it does not provide for initialization of the global variable. With C99 or C11 and variable argument lists for macros, you could define a macro to support initialization too. (With C89 and no support for variable argument lists in macros, there is no easy way to handle arbitrarily long initializers.)

file3b.h

#ifdef DEFINE_VARIABLES
#define EXTERN                  /* nothing */
#define INITIALIZER(...)        = __VA_ARGS__
#else
#define EXTERN                  extern
#define INITIALIZER(...)        /* nothing */
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable INITIALIZER(37);
EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 });

Reverse contents of #if and #else blocks, fixing bug identified by Denis Kniazhev

file1b.c

#define DEFINE_VARIABLES
#include "file3b.h"  /* Variables now defined and initialized */
#include "prog4.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file2b.c

#include "file3b.h"
#include "prog4.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}

Clearly, the code for the oddball structure is not what you'd normally write, but it illustrates the point. The first argument to the second invocation of INITIALIZER is { 41 and the remaining argument (singular in this example) is 43 }. Without C99 or similar support for variable argument lists for macros, initializers that need to contain commas are very problematic.

Correct header file3b.h included (instead of fileba.h) per Denis Kniazhev


The next two files complete the source for prog4:

prog4.h

extern int increment(void);
extern int oddball_value(void);
extern void use_them(void);

prog4.c

#include "file3b.h"
#include "prog4.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}
  • prog4 uses prog4.c, file1b.c, file2b.c, prog4.h, file3b.h.

Header Guards

Any header should be protected against reinclusion, so that type definitions (enum, struct or union types, or typedefs generally) do not cause problems. The standard technique is to wrap the body of the header in a header guard such as:

#ifndef FILE3B_H_INCLUDED
#define FILE3B_H_INCLUDED

...contents of header...

#endif /* FILE3B_H_INCLUDED */

The header might be included twice indirectly. For example, if file4b.h includes file3b.h for a type definition that isn't shown, and file1b.c needs to use both header file4b.h and file3b.h, then you have some more tricky issues to resolve. Clearly, you might revise the header list to include just file4b.h. However, you might not be aware of the internal dependencies — and the code should, ideally, continue to work.

Further, it starts to get tricky because you might include file4b.h before including file3b.h to generate the definitions, but the normal header guards on file3b.h would prevent the header being reincluded.

So, you need to include the body of file3b.h at most once for declarations, and at most once for definitions, but you might need both in a single translation unit (TU — a combination of a source file and the headers it uses).

Multiple inclusion with variable definitions

However, it can be done subject to a not too unreasonable constraint. Let's introduce a new set of file names:

  • external.h for the EXTERN macro definitions, etc.

  • file1c.h to define types (notably, struct oddball, the type of oddball_struct).

  • file2c.h to define or declare the global variables.

  • file3c.c which defines the global variables.

  • file4c.c which simply uses the global variables.

  • file5c.c which shows that you can declare and then define the global variables.

  • file6c.c which shows that you can define and then (attempt to) declare the global variables.

In these examples, file5c.c and file6c.c directly include the header file2c.h several times, but that is the simplest way to show that the mechanism works. It means that if the header was indirectly included twice, it would also be safe.

The restrictions for this to work are:

  1. The header defining or declaring the global variables may not itself define any types.

  2. Immediately before you include a header that should define variables, you define the macro DEFINE_VARIABLES.

  3. The header defining or declaring the variables has stylized contents.

external.h

/*
** This header must not contain header guards (like <assert.h> must not).
** Each time it is invoked, it redefines the macros EXTERN, INITIALIZE
** based on whether macro DEFINE_VARIABLES is currently defined.
*/
#undef EXTERN
#undef INITIALIZE

#ifdef DEFINE_VARIABLES
#define EXTERN              /* nothing */
#define INITIALIZE(...)     = __VA_ARGS__
#else
#define EXTERN              extern
#define INITIALIZE(...)     /* nothing */
#endif /* DEFINE_VARIABLES */

file1c.h

#ifndef FILE1C_H_INCLUDED
#define FILE1C_H_INCLUDED

struct oddball
{
    int a;
    int b;
};

extern void use_them(void);
extern int increment(void);
extern int oddball_value(void);

#endif /* FILE1C_H_INCLUDED */

file2c.h


/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2C_H_DEFINITIONS)
#undef FILE2C_H_INCLUDED
#endif

#ifndef FILE2C_H_INCLUDED
#define FILE2C_H_INCLUDED

#include "external.h"   /* Support macros EXTERN, INITIALIZE */
#include "file1c.h"     /* Type definition for struct oddball */

#if !defined(DEFINE_VARIABLES) || !defined(FILE2C_H_DEFINITIONS)

/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });

#endif /* !DEFINE_VARIABLES || !FILE2C_H_DEFINITIONS */

/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */

#endif /* FILE2C_H_INCLUDED */

file3c.c

#define DEFINE_VARIABLES
#include "file2c.h"  /* Variables now defined and initialized */

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file4c.c

#include "file2c.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}

file5c.c


#include "file2c.h"     /* Declare variables */

#define DEFINE_VARIABLES
#include "file2c.h"  /* Variables now defined and initialized */

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file6c.c


#define DEFINE_VARIABLES
#include "file2c.h"     /* Variables now defined and initialized */

#include "file2c.h"     /* Declare variables */

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }


The next source file completes the source (provides a main program) for prog5, prog6 and prog7:

prog5.c

#include "file2c.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}
  • prog5 uses prog5.c, file3c.c, file4c.c, file1c.h, file2c.h, external.h.

  • prog6 uses prog5.c, file5c.c, file4c.c, file1c.h, file2c.h, external.h.

  • prog7 uses prog5.c, file6c.c, file4c.c, file1c.h, file2c.h, external.h.


This scheme avoids most problems. You only run into a problem if a header that defines variables (such as file2c.h) is included by another header (say file7c.h) that defines variables. There isn't an easy way around that other than "don't do it".

You can partially work around the problem by revising file2c.h into file2d.h:

file2d.h

/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS)
#undef FILE2D_H_INCLUDED
#endif

#ifndef FILE2D_H_INCLUDED
#define FILE2D_H_INCLUDED

#include "external.h"   /* Support macros EXTERN, INITIALIZE */
#include "file1c.h"     /* Type definition for struct oddball */

#if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS)

/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });

#endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */

/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2D_H_DEFINITIONS
#undef DEFINE_VARIABLES
#endif /* DEFINE_VARIABLES */

#endif /* FILE2D_H_INCLUDED */

The issue becomes 'should the header include #undef DEFINE_VARIABLES?' If you omit that from the header and wrap any defining invocation with #define and #undef:

#define DEFINE_VARIABLES
#include "file2c.h"
#undef DEFINE_VARIABLES

in the source code (so the headers never alter the value of DEFINE_VARIABLES), then you should be clean. It is just a nuisance to have to remember to write the the extra line. An alternative might be:

#define HEADER_DEFINING_VARIABLES "file2c.h"
#include "externdef.h"

externdef.h

/*
** This header must not contain header guards (like <assert.h> must not).
** Each time it is included, the macro HEADER_DEFINING_VARIABLES should
** be defined with the name (in quotes - or possibly angle brackets) of
** the header to be included that defines variables when the macro
** DEFINE_VARIABLES is defined.  See also: external.h (which uses
** DEFINE_VARIABLES and defines macros EXTERN and INITIALIZE
** appropriately).
**
** #define HEADER_DEFINING_VARIABLES "file2c.h"
** #include "externdef.h"
*/

#if defined(HEADER_DEFINING_VARIABLES)
#define DEFINE_VARIABLES
#include HEADER_DEFINING_VARIABLES
#undef DEFINE_VARIABLES
#undef HEADER_DEFINING_VARIABLES
#endif /* HEADER_DEFINING_VARIABLES */

This is getting a tad convoluted, but seems to be secure (using the file2d.h, with no #undef DEFINE_VARIABLES in the file2d.h).

file7c.c

/* Declare variables */
#include "file2d.h"

/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"

/* Declare variables - again */
#include "file2d.h"

/* Define variables - again */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file8c.h

/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS)
#undef FILE8C_H_INCLUDED
#endif

#ifndef FILE8C_H_INCLUDED
#define FILE8C_H_INCLUDED

#include "external.h"   /* Support macros EXTERN, INITIALIZE */
#include "file2d.h"     /* struct oddball */

#if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS)

/* Global variable declarations / definitions */
EXTERN struct oddball another INITIALIZE({ 14, 34 });

#endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */

/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE8C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */

#endif /* FILE8C_H_INCLUDED */

file8c.c

/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"

/* Define variables */
#define HEADER_DEFINING_VARIABLES "file8c.h"
#include "externdef.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }


The next two files complete the source for prog8 and prog9:

prog8.c

#include "file2d.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}

file9c.c

#include "file2d.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}

  • prog8 uses prog8.c, file7c.c, file9c.c.

  • prog9 uses prog8.c, file8c.c, file9c.c.


However, the problems are relatively unlikely to occur in practice, especially if you take the standard advice to

Avoid global variables


Does this exposition miss anything?

_Confession_: The 'avoiding duplicated code' scheme outlined here was developed because the issue affects some code I work on (but don't own), and is a niggling concern with the scheme outlined in the first part of the answer. However, the original scheme leaves you with just two places to modify to keep variable definitions and declarations synchronized, which is a big step forward over having exernal variable declarations scattered throughout the code base (which really matters when there are thousands of files in total). However, the code in the files with the names `fileNc.[ch]` (plus `external.h` and `externdef.h`) shows that it can be made to work. Clearly, it would not be hard to create a header generator script to give you the standardized template for a variable defining and declaring header file.

NB These are toy programs with just barely enough code to make them marginally interesting. There is repetition within the examples that could be removed, but isn't to simplify the pedagogical explanation. (For example: the difference between prog5.c and prog8.c is the name of one of the headers that are included. It would be possible to reorganize the code so that the main() function was not repeated, but it would conceal more than it revealed.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Are you sure that having tentative definitions spread across multiple translation units is blessed by C? The C99 TC3 draft says " If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0." – Johannes Schaub - litb Sep 16 '09 at 15:03
  • That seems to mean that each such translation unit contains an external definition for it, and violate "somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise [if the identifier isn't used], there shall be no more than one.". As i understood the COMMON blocks, they are non-standard extensions. – Johannes Schaub - litb Sep 16 '09 at 15:05
  • 4
    @litb: see Annex J.5.11 for the common definition - it is a common extension. – Jonathan Leffler Sep 16 '09 at 15:19
  • 3
    @litb: and I agree it should be avoided - that's why it is in the section on 'Not so good way to define global variables'. – Jonathan Leffler Sep 16 '09 at 15:20
  • 3
    Indeed it's a common extension, but it's undefined behavior for a program to rely on it. I just wasn't clear whether you were saying that this is allowed by C's own rules. Now i see you are saying it's just a common extension and to avoid it if you need your code to be portable. So i can upvote you without doubts. Really great answer IMHO :) – Johannes Schaub - litb Sep 16 '09 at 15:30
  • In your example of `file3a.h`, should the `extern` keyword come on the `if` instead of the `else`? – Zak Jan 16 '13 at 00:59
  • 3
    @Zak: No. The conditional code in `file3a.h` is `#ifdef DEFINE_VARIABLES / #define EXTERN / #else / #define EXTERN extern / #endif`, removing comments and using slashes to mark the ends of lines. If `DEFINE_VARIABLES` is specified, then the variables should not have the `extern` prefix which would mark them as declarations instead of definitions. That, in turn, means that the compiler will allocate space for the variables, rather than simply recording their existence. – Jonathan Leffler Jan 16 '13 at 02:14
  • 45
    If you stop at the top, it keeps simple things simple. As you read further down, it deals with more nuances, complications and details. I've just added two 'early stopping points' for less experienced C programmers — or C programmers who already know the subject. There's no need to read it all if you already know the answer (but let me know if you find a technical fault). – Jonathan Leffler Aug 05 '14 at 03:28
  • 2
    Is there any reasonable pattern for a scenario in which code in numerous modules needs to make use of the same initialized array *and know its size*? At least some compilers will regard `extern int foo[] = {1,2,3};` as equivalent to `extern int foo[3];`, so it's possible to use some preprocessor logic to selectively omit the "extern" when the file is included from within its main source file. I haven't figured out any way to make that not look really ugly, though. – supercat Sep 18 '14 at 18:23
  • 1
    @supercat: I would create a file `foo.c` to contain the definition of the array and a variable to hold its size: `#include "foo.h"` plus `int foo[] = { 1, 2, 3}; size_t foo_size = sizeof(foo) / sizeof(foo[0]);`, and a header `foo.h` which would contain `#include – Jonathan Leffler Sep 19 '14 at 00:28
  • 1
    @supercat: I'd add `const` to the `size_t` 'variable' since the size of the array doesn't change. The major downside of this is that you don't have an integer constant (as opposed to a constant integer) which you can use in contexts where an integer constant is needed. Realistically, it is unlikely to be a problem. – Jonathan Leffler Sep 19 '14 at 00:39
  • 2
    @JonathanLeffler: For embedded systems, the differences between constants and never-written variables can be significant. It's really too bad that C never defined a means of defining link-time constants other than addresses, since I think linker systems even when C was designed could support such a concept at least for things that weren't larger than `int`. – supercat Sep 19 '14 at 02:27
  • 1
    @JonathanLeffler: BTW, on many embedded systems, there can also be a substantial speed and code size penalty for separating parts of a program into different compilation unit. For example, on a typical ARM, `void setVariables(int a,b,c) {x=a;y=b;z=c;}` could be 14 bytes if x-z are in the same compilation unit as the method, but would require 26 bytes if they're in another compilation unit. Some people may despise the idea of using `#include` to join together C files that could also run as separate compilation units, but there can be some *major* efficiency advantages to doing so. – supercat Sep 19 '14 at 02:45
  • 4
    @supercat: It occurs to me that you can use C99 array literals to get an enumeration value for the array size, exemplified by (`foo.h`): `#define FOO_INITIALIZER { 1, 2, 3, 4, 5 }` to define the initializer for the array, `enum { FOO_SIZE = sizeof((int [])FOO_INITIALIZER) / sizeof(((int [])FOO_INITIALIZER)[0]) };` to get the size of the array, and `extern int foo[];` to declare the array. Clearly, the definition should be just `int foo[FOO_SIZE] = FOO_INITIALIZER;`, though the size doesn't really have to be included in the definition. This gets you a integer constant, `FOO_SIZE`. – Jonathan Leffler Sep 19 '14 at 08:37
  • +1 for the idea of using an `enum`, though I'm not sure how compilers would handle the aforementioned syntax. I've used great big monster macros for a variety of purposes in a style similar to this, but I think using `[extern] int foo[] = {..data..}` is a bit cleaner since it uses the size of the *actual* created array. BTW, one really nasty hack which works on some embedded compilers would be to have `foo.c` contain the array and then `int[] FOO_SIZE_AS_ADDR @(sizeof(foo))`, and then have the `.h` file `#define foo_size ((int)(FOO_SIZE_AS_ADDR))`. The thing the hack is doing... – supercat Sep 20 '14 at 18:27
  • ...(allowing an `int` value to be used as a link-time constant) would be the cleanest way to share the array content and size among different compilation units [note that the FOO_SIZE_AS_ADDR doesn't point to anything meaningful, but on some platforms its *address* can be used as a link-time constant]. Of course, many platforms don't provide an `@` syntax for forcing addresses, so the only way to declare such addresses would be in an assembly-language file. Further, on many platforms there would be no guarantee that all `int` values will be accepted by the linker as addresses. – supercat Sep 20 '14 at 18:28
  • 1
    @supercat: You need to be careful to decide when you give up on using what the standard promises will work. It is a legitimate, but in my experience fraught, decision to make. The fraughtness comes because people don't realize they're going outside the standard and the information is not documented in the code, and when you move it to a new environment or a new version of the compiler or whatever, the behaviour changes because it was not standard behaviour. Your judgement call. My judgement errs on the side of caution and following the standard for maximum portability. – Jonathan Leffler Sep 20 '14 at 18:31
  • @JonathanLeffler: I would not use the aformentioned hack with array sizes, because there is a standard-compliant means of handling the concept that, while clunky, is workable. I have used that sort of hack for some other purposes, however--typically with constants that are defined in an assembly-language file. For example, in one case an assembly-language method needed to be passed an array of a certain size, but the assembly code could be adapted to change that size (the code would need to be modified according to the array size, but such modification would not be difficult). In that case... – supercat Sep 20 '14 at 18:53
  • @JonathanLeffler: I figured that having the `.h` file contain a hacky expression to convert a pointer to an integer was safer than having it specify a number. If the code needed to be ported to a platform where that wouldn't work, the assembly file would almost certainly need to be changed for other reasons anyhow. PS--I wonder how much "portable" code would work on a platform where `int` was 64 bits? I would expect a lot of code which is thought to be portable would end up working most of the time but end up with obscure little bugs because of C's unfortunate type-promotion rules. – supercat Sep 20 '14 at 18:54
  • 3
    This is a good answer to return to as you learn C/Objective-C/C++ but header guards should be noted much earlier. Reason being, if you do anything with any *nix it's unavoidably standard practice. – uchuugaka Feb 20 '16 at 14:20
  • 2
    why do you use extern when declaring functions? – daniel Apr 04 '17 at 00:45
  • 1
    Because I only ever write such declarations in headers, and any variables declared in headers must be prefixed with `extern`, so for symmetry, I also declare functions prefixed with `extern`. Other people don't do it — it's a point of difference in style. – Jonathan Leffler Apr 04 '17 at 00:50
  • I am a complete amateur to this. How do I write a makefile for the very first section before the guidelines? Can you provide a sample makefile? Thanks. Edit: I realized that we can use gcc file1.c file2.c prog1.c -o final_working to make it work. But if I were to write a formal make file, how do I do it for this case? I wrote one, but it doesn't seem to work. Your response will be quite helpful to check against mine. – thegreatcoder Jun 18 '18 at 22:56
  • 2
    @Shubashree: Hmmm – interesting. OK; I've added `prog1.mk` to show you the 'minimal' makefile. It isn't completely minimal, of course. That allows me to tune the build if necessary, but covers most of the bases, leaving a very stringent set of compilation options. Not that some of the `xFLAGS` names used have other uses in standard Make (notably `GFLAGS` — related to SCCS, but you probably don't use SCCS, so it probably doesn't matter). Beware! – Jonathan Leffler Jun 19 '18 at 01:19
  • Hello, Sir. I have one confusion. My question is you said "we may declare a variable multiple times (though once is sufficient);" So, Can we write "extern int bar;" multiple times? I like to beg pardon for my ignorance. I'm here for clearing my confusion over "variable defining". – Naasif Dec 22 '19 at 16:00
  • 1
    @naasif: Yes; you may use: `extern int bar; extern int bar; …other code…; extern int bar; extern int bar;` and all you're doing is wasting the compiler's time processing unnecessary declarations, and causing people to doubt your sanity. You could even have `int bar = 97;` somewhere to define the variable; you can only have one such initialized definition anywhere in your program. Writing `int bar;` several times is also legitimate but extremely unorthodox; these tentative definitions are finalized at the end of the translation unit (TU) into a single uninitialized variable definition. – Jonathan Leffler Dec 22 '19 at 16:07
  • 1
    @naasif: See also C11 [§6.9.2 External object definitions](http://port70.net/~nsz/c/c11/n1570.html#6.9.2) which includes an example with annotations on what's allowed and not allowed. – Jonathan Leffler Dec 22 '19 at 16:09
  • @JonathanLeffler Sir one more thing to be cleared. "extern" keyword is suitable while using header file because if we declare a variable "int i" without "extern" keyword that would conflict in coding if we use "int i" in our program. So, it is wise to always use extern while declaring a golbal variable inside a header file. Am I right, sir? – Naasif Dec 22 '19 at 16:19
  • 1
    @naasif: Almost invariably, when written inside a header, variable declarations should be prefixed with `extern` so as not to define the variable in multiple TUs. There are options on some systems (`-Wl,-relax` on Linux, for example) to allow multiple definitions — but it isn't good practice to need it. The standard has a fairly strict 'one definition' rule, but some (many) compilers relax the rules (see the 'common extensions' stuff — C11 [Annex J.5 Common extensions](http://port70.net/~nsz/c/c11/n1570.html#J.5) etc.). – Jonathan Leffler Dec 22 '19 at 16:26
  • @JonathanLeffler Sir, Last thing to know. When I run prog3.c(containing main) it first runs "use_it()" there is no chance to get initialized I see. But it some somehow initialized with value 37 which was defined in file1.c . But when the function was invoked there were no chance to meet file1.c . I think it has been done because of "extern keyword" and someone during compiler processing was looking for the value of "global_variable" and stored it. This could be the reason of perfectly working of "use_it();" variable. I'm just wondering who did this? I'm sorry for bothering you but I need it. – Naasif Dec 22 '19 at 16:53
  • 1
    @naasif — in `prog3`, the `global_variable` is default initialized to 0 when at program startup. When `use_it()` is called, it is printed (as 0) then incremented. Then the main code adds 19, to save 20. The next call to `use_it()` therefore prints 20 and increments the variable to 21. The call to `increment()` returns 21 (but increments the variable to 22, too). That's what I get from the GitHub [SOQ](https://github.com/jleffler/soq/tree/master/src/so-0143-3204) repository code for this question. That links `progr3.o`, `file1a.o` and `file2a.o`, though; it doesn't use `file1.c` at all. – Jonathan Leffler Dec 23 '19 at 09:13
  • Why does the "safe" way actually work? The conventional pattern for declaring and defining global_variable is as shown in the first example, where file3.h declares `extern global_variable`, and then file1.c defines the variable with `int global_variable = 37;` As noted in the comment, file1.c also _#includes_ file3.h to allow the compiler to check the declaration. But why does the compiler not detect this as an error? Having seen in the included header that global_variable is supposed to be extern, here it is being defined, clearly not extern. What rule makes this OK? – gwideman Feb 13 '23 at 13:59
  • 1
    @gwideman: What is the error that the compiler should be detecting? That the TU contains `extern int global_variable;` and also (later) `int global_variable = 37;`? There's no error because the first says "there is global variable of type `int` called `global_variable` that is defined somewhere", and the second says "this is the definition of the global variable of type `int` called `global_variable` and it is initialized to `37`. The compiler will report an error if one type is `int` and the other `long`, for example. But there's no harm done with the two self-consistent declarations. – Jonathan Leffler Feb 13 '23 at 19:19
  • @gwideman: See also [§6.9.2 External object definitions](http://port70.net/~nsz/c/c11/n1570.html#6.9.2) and the example provided there. – Jonathan Leffler Feb 13 '23 at 19:19
  • @JonathanLeffler thanks for the link -- I had seen that page before and found it inscrutable. Your explanation makes sense -- evidently extern does not literally mean "external", but rather it's a forward declaration to be resolved by the linker. With error or other consequences if the linker finds more than one actual definition, or definition that doesn't match the extern declaration. – gwideman Feb 19 '23 at 22:38
  • Old stuff, but my take on the global variables are that by the time you're passing a puck around from one function to another to avoid having a global configuration variable, you might as well as give up and make it a global. I like using global structs instead of global variables, you're far less likely to reuse struct.variable naming accidentally than you'd be reusing just "variable". And of course use functions to get/set the values. Enums with a switch statement work fine here.. Globals are very useful when you've got 64kB uCU or something. – Barleyman Jun 28 '23 at 13:16
140

An extern variable is a declaration (thanks to sbi for the correction) of a variable which is defined in another translation unit. That means the storage for the variable is allocated in another file.

Say you have two .c-files test1.c and test2.c. If you define a global variable int test1_var; in test1.c and you'd like to access this variable in test2.c you have to use extern int test1_var; in test2.c.

Complete sample:

$ cat test1.c 
int test1_var = 5;
$ cat test2.c
#include <stdio.h>

extern int test1_var;

int main(void) {
    printf("test1_var = %d\n", test1_var);
    return 0;
}
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
jww
  • 97,681
  • 90
  • 411
  • 885
Johannes Weiss
  • 52,533
  • 16
  • 102
  • 136
  • 24
    There's no "pseudo-definitions". It's a declaration. – sbi Sep 16 '09 at 14:18
  • 4
    In the above example, if I change the `extern int test1_var;` to `int test1_var;`, the linker (gcc 5.4.0) still passes. So, is `extern` really needed in this case? – radiohead Mar 24 '18 at 03:15
  • 3
    @radiohead: In my [answer](https://stackoverflow.com/a/1433387/15168), you will find the information that dropping the `extern` is a common extension that often works — and specifically works with GCC (but GCC is far from being the only compiler that supports it; it is prevalent on Unix systems). You can look for "J.5.11" or the section "Not so good way" in my answer (I know — it _is_ long) and the text near that explains it (or tries to do so). – Jonathan Leffler Jun 16 '18 at 19:44
  • An extern declaration certainly doesn't have to be defined in another translation unit (and commonly is not). In fact, declaration and definition can be one and the same. – Remember Monica Dec 28 '19 at 00:35
43

Extern is the keyword you use to declare that the variable itself resides in another translation unit.

So you can decide to use a variable in a translation unit and then access it from another one, then in the second one you declare it as extern and the symbol will be resolved by the linker.

If you don't declare it as extern you'll get 2 variables named the same but not related at all, and an error of multiple definitions of the variable.

Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105
  • 5
    In other words the translation unit where extern is used knows about this variable, its type etc. and hence allows the source code in the underlying logic to use it, but it does not _allocate_ the variable, another translation unit will do that. If both translation units were to declare the variable normally, there would be effectily two physical locations for the variable, with the associated "wrong" references within the compiled code, and with the resulting ambiguity for the linker. – mjv Sep 16 '09 at 14:19
34
                 declare | define   | initialize |
                ----------------------------------

extern int a;    yes          no           no
-------------
int a = 2019;    yes          yes          yes
-------------
int a;           yes          yes          no
-------------

Declaration won't allocate memory (the variable must be defined for memory allocation) but the definition will. This is just another simple view on the extern keyword since the other answers are really great.

Lucian
  • 794
  • 1
  • 9
  • 21
31

I like to think of an extern variable as a promise that you make to the compiler.

When encountering an extern, the compiler can only find out its type, not where it "lives", so it can't resolve the reference.

You are telling it, "Trust me. At link time this reference will be resolvable."

Buggieboy
  • 4,636
  • 4
  • 55
  • 79
  • 1
    More generally, a *declaration* is a promise that the name will be resolvable to a exactly one definition at link time. An extern declares a variable without defining. – Lie Ryan Nov 30 '10 at 02:16
18

extern tells the compiler to trust you that the memory for this variable is declared elsewhere, so it doesnt try to allocate/check memory.

Therefore, you can compile a file that has reference to an extern, but you can not link if that memory is not declared somewhere.

Useful for global variables and libraries, but dangerous because the linker does not type check.

BenB
  • 10,300
  • 7
  • 32
  • 30
  • The memory isn't declared. See the answers to this question: http://stackoverflow.com/questions/1410563/ for more details. – sbi Sep 16 '09 at 14:37
17

Adding an extern turns a variable definition into a variable declaration. See this thread as to what's the difference between a declaration and a definition.

Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • What difference between `int foo` and `extern int foo` (file scope)? Both are declaration, isn't it? –  Nov 08 '12 at 19:07
  • @user14284: They are both declaration only in the sense that every definition is a declaration, too. But I linked to an explanation of this. ("See this thread as to what's the difference between a declaration and a definition.") Why don't you simple follow the link and read? – sbi Nov 09 '12 at 09:12
12

The correct interpretation of extern is that you tell something to the compiler. You tell the compiler that, despite not being present right now, the variable declared will somehow be found by the linker (typically in another object (file)). The linker will then be the lucky guy to find everything and put it together, whether you had some extern declarations or not.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
8

In C a variable inside a file say example.c is given local scope. The compiler expects that the variable would have its definition inside the same file example.c and when it does not find the same , it would throw an error.A function on the other hand has by default global scope . Thus you do not have to explicitly mention to the compiler "look dude...you might find the definition of this function here". For a function including the file which contains its declaration is enough.(The file which you actually call a header file). For example consider the following 2 files :
example.c

#include<stdio.h>
extern int a;
main(){
       printf("The value of a is <%d>\n",a);
}

example1.c

int a = 5;

Now when you compile the two files together, using the following commands :

step 1)cc -o ex example.c example1.c step 2)./ex

You get the following output : The value of a is <5>

bobby
  • 2,629
  • 5
  • 30
  • 56
Phoenix225
  • 167
  • 5
  • 12
8

extern keyword is used with the variable for its identification as a global variable.

It also represents that you can use the variable declared using extern keyword in any file though it is declared/defined in other file.

Anup H
  • 549
  • 7
  • 10
8

GCC ELF Linux implementation

Other answers have covered the language usage side of view, so now let's have a look at how it is implemented in this implementation.

main.c

#include <stdio.h>

int not_extern_int = 1;
extern int extern_int;

void main() {
    printf("%d\n", not_extern_int);
    printf("%d\n", extern_int);
}

Compile and decompile:

gcc -c main.c
readelf -s main.o

Output contains:

Num:    Value          Size Type    Bind   Vis      Ndx Name
 9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 not_extern_int
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND extern_int

The System V ABI Update ELF spec "Symbol Table" chapter explains:

SHN_UNDEF This section table index means the symbol is undefined. When the link editor combines this object file with another that defines the indicated symbol, this file's references to the symbol will be linked to the actual definition.

which is basically the behavior the C standard gives to extern variables.

From now on, it is the job of the linker to make the final program, but the extern information has already been extracted from the source code into the object file.

Tested on GCC 4.8.

C++17 inline variables

In C++17, you might want to use inline variables instead of extern ones, as they are simple to use (can be defined just once on header) and more powerful (support constexpr). See: What does 'const static' mean in C and C++?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 3
    It's not my down-vote, so I don't know. However, I'll proffer an opinion. Although looking at the output of `readelf` or `nm` can be helpful, you've not explained the fundamentals of how to make use of `extern`, nor completed the first program with the actual definition. Your code doesn't even use `notExtern`. There's a nomenclature problem, too: although `notExtern` is defined here rather than declared with `extern`, it is an external variable that could be accessed by other source files if those translation units contained a suitable declaration (which would need `extern int notExtern;`!). – Jonathan Leffler Aug 30 '15 at 14:57
  • 1
    @JonathanLeffler thanks for the feedback! The standard behavior and usage recommendations have already been done in other answers, so I decided to show the implementation a bit as that really helped me grasp what is going on. Not using `notExtern` was ugly, fixed it. About nomenclature, let me know if you have a better name. Of course that would not be a good name for an actual program, but I think it fits the didactic role well here. – Ciro Santilli OurBigBook.com Sep 02 '15 at 14:52
  • As to names, what about `global_def` for the variable defined here, and `extern_ref` for the variable defined in some other module? Would they have suitably clear symmetry? You still end up with `int extern_ref = 57;` or something like that in the file where it is defined, so the name isn't quite ideal, but within the context of the single source file, it is a reasonable choice. Having `extern int global_def;` in a header isn't as much of a problem, it seems to me. Entirely up to you, of course. – Jonathan Leffler Sep 02 '15 at 14:56
5

extern allows one module of your program to access a global variable or function declared in another module of your program. You usually have extern variables declared in header files.

If you don't want a program to access your variables or functions, you use static which tells the compiler that this variable or function cannot be used outside of this module.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
loganaayahee
  • 809
  • 2
  • 8
  • 13
4

First off, the extern keyword is not used for defining a variable; rather it is used for declaring a variable. I can say extern is a storage class, not a data type.

extern is used to let other C files or external components know this variable is already defined somewhere. Example: if you are building a library, no need to define global variable mandatorily somewhere in library itself. The library will be compiled directly, but while linking the file, it checks for the definition.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
user1270846
  • 126
  • 1
  • 6
4

extern simply means a variable is defined elsewhere (e.g., in another file).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Geremia
  • 4,745
  • 37
  • 43
3

extern is used so one first.c file can have full access to a global parameter in another second.c file.

The extern can be declared in the first.c file or in any of the header files first.c includes.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
shoham
  • 293
  • 1
  • 9
  • 3
    Note that the `extern` declaration should be in a header, not in `first.c`, so that if the type changes, the declaration will change too. Also, the header that declares the variable should be included by `second.c` to ensure that the definition is consistent with the declaration. The declaration in the header is the glue that holds it all together; it allows the files to be compiled separately but ensures they have a consistent view of the type of the global variable. – Jonathan Leffler Sep 02 '15 at 15:09
2

With xc8 you have to be careful about declaring a variable as the same type in each file as you could , erroneously, declare something an int in one file and a char say in another. This could lead to corruption of variables.

This problem was elegantly solved in a microchip forum some 15 years ago /* See "http:www.htsoft.com" / / "forum/all/showflat.php/Cat/0/Number/18766/an/0/page/0#18766"

But this link seems to no longer work...

So I;ll quickly try to explain it; make a file called global.h.

In it declare the following

#ifdef MAIN_C
#define GLOBAL
 /* #warning COMPILING MAIN.C */
#else
#define GLOBAL extern
#endif
GLOBAL unsigned char testing_mode; // example var used in several C files

Now in the file main.c

#define MAIN_C 1
#include "global.h"
#undef MAIN_C

This means in main.c the variable will be declared as an unsigned char.

Now in other files simply including global.h will have it declared as an extern for that file.

extern unsigned char testing_mode;

But it will be correctly declared as an unsigned char.

The old forum post probably explained this a bit more clearly. But this is a real potential gotcha when using a compiler that allows you to declare a variable in one file and then declare it extern as a different type in another. The problems associated with that are if you say declared testing_mode as an int in another file it would think it was a 16 bit var and overwrite some other part of ram, potentially corrupting another variable. Difficult to debug!

user50619
  • 325
  • 5
  • 14
0

A very short solution I use to allow a header file to contain the extern reference or actual implementation of an object. The file that actually contains the object just does #define GLOBAL_FOO_IMPLEMENTATION. Then when I add a new object to this file it shows up in that file also without me having to copy and paste the definition.

I use this pattern across multiple files. So in order to keep things as self contained as possible, I just reuse the single GLOBAL & GLOBALINIT macros in each header. My headers look like this:

//file foo_globals.h
#pragma once  
#include "foo.h"  //contains Foo typedef
#include <atomic>

#ifdef GLOBAL  
#undef GLOBAL  
#endif  

#ifdef GLOBALINIT
#undef GLOBALINIT
#endif

#ifdef GLOBAL_FOO_IMPLEMENTATION  
#define GLOBAL  
#define GLOBALINIT(x) = x
#else  
#define GLOBAL extern  
#define GLOBALINIT(x)
#endif  

GLOBAL Foo foo1 GLOBALINIT({2, 3, 4})
GLOBAL std::atomic_bool flag1 GLOBALINIT(true);
GLOBAL std::atomic_uint counter1 GLOBALINIT(5);


//file main.cpp
#define GLOBAL_FOO_IMPLEMENTATION
#include "foo_globals.h"

//file uses_extern_foo.cpp
#include "foo_globals.h
muusbolla
  • 637
  • 7
  • 20
0

In short extern means that variable is defined in other module and its address will be known at link time. The compiler does not reserve memory in current module and knows the variable type. To understand extern is good to have at least little experience with assembler.

i486
  • 6,491
  • 4
  • 24
  • 41
0

extern keyword before a symbol (a var or function) tells the linker that it(the source file) uses an external symbol. This can be seen by running nm -a on such an object file (.o) which uses or assigns a value to a extern var (remember to declare a extern symbol on top like this extern int x or still better, use a header file with extern before vars and functions can be without extern; then in main assign a value to it like this x=5;), i find undefined bss info (letter B written) against such an extern var(symbol). This means x is still unresolved and will be resolved when ld is run (during link-time).

why always use extern in headers? If i don't use extern, just declare int x, the declaration becomes sort-of strong and without extern, and this redifines the same variable in every source that includes the header, effectively shadowing the original variable. therefore with just int x in a.h header, I redefine a new global variable x in every source that include this a.h. This var in the source, this without-extern var decl in headers shadows(it doesn't shadow exactly, it's redifining a global variable x in every source code that includes the header with just int x, without extern, when i include such header and try to compile .o from such files, every .o has its own definition of this global variable x which was included in the header without extern, and at the time of linking, I get the error multiple definition of variable or symbol x) an important variable defined somewhere of somewhere else in the source files. Important! it is necessary to use extern before vars in headers. Functions are already extern by-default.

Fitohe
  • 13
  • 1
  • 3