How do I use extern to share variables between source files? The Next CEO of Stack OverflowHow do I share variables between different .c files?Variable declaration in a header fileUse of 'extern' KeywordWhy use the extern keyword in header in C?Where can I declare global variable in c program , whether in header or source fileAre there any drawbacks to declaring variables in header files without the “extern” keyword?C++ How to properly initialize global variables?C globally shared variable vs extern variableGlobal variable in csame static variable in different filesHow to declare global variables in Android?Improve INSERT-per-second performance of SQLite?Why won't extern link to a static variable?Global variables in Javascript across multiple filesshared global variables in CShare variables between files in Node.js?What does the C ??!??! operator do?Global variables in AngularJSHow to declare and use global external variable across multiple source files?Are global variables by default extern? if yes, then why they have default value “ 0 ”?
Why can't we say "I have been having a dog"?
My boss doesn't want me to have a side project
Can I cast Thunderwave and be at the center of its bottom face, but not be affected by it?
Would a grinding machine be a simple and workable propulsion system for an interplanetary spacecraft?
How does a dynamic QR code work?
How can I separate the number from the unit in argument?
Shortening a title without changing its meaning
Can you teleport closer to a creature you are Frightened of?
Direct Implications Between USA and UK in Event of No-Deal Brexit
Free fall ellipse or parabola?
What did the word "leisure" mean in late 18th Century usage?
How to unfasten electrical subpanel attached with ramset
Horror film about a man brought out of cryogenic suspension without a soul, around 1990
The sum of any ten consecutive numbers from a fibonacci sequence is divisible by 11
Find a path from s to t using as few red nodes as possible
What difference does it make matching a word with/without a trailing whitespace?
Physiological effects of huge anime eyes
Is the offspring between a demon and a celestial possible? If so what is it called and is it in a book somewhere?
Simplify trigonometric expression using trigonometric identities
Find the majority element, which appears more than half the time
Gauss' Posthumous Publications?
Are British MPs missing the point, with these 'Indicative Votes'?
Salesforce opportunity stages
Is a linearly independent set whose span is dense a Schauder basis?
How do I use extern to share variables between source files?
The Next CEO of Stack OverflowHow do I share variables between different .c files?Variable declaration in a header fileUse of 'extern' KeywordWhy use the extern keyword in header in C?Where can I declare global variable in c program , whether in header or source fileAre there any drawbacks to declaring variables in header files without the “extern” keyword?C++ How to properly initialize global variables?C globally shared variable vs extern variableGlobal variable in csame static variable in different filesHow to declare global variables in Android?Improve INSERT-per-second performance of SQLite?Why won't extern link to a static variable?Global variables in Javascript across multiple filesshared global variables in CShare variables between files in Node.js?What does the C ??!??! operator do?Global variables in AngularJSHow to declare and use global external variable across multiple source files?Are global variables by default extern? if yes, then why they have default value “ 0 ”?
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?
c global-variables extern
add a comment |
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?
c global-variables extern
add a comment |
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?
c global-variables extern
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?
c global-variables extern
c global-variables extern
edited May 22 '17 at 7:01
Lundin
112k17163271
112k17163271
asked Sep 16 '09 at 14:08
shilpa
add a comment |
add a comment |
16 Answers
16
active
oldest
votes
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: %dn", 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: %dn", increment());
return 0;
prog1usesprog1.c,file1.c,file2.c,file3.handprog1.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
externdeclarations of variables — neverstaticor unqualified variable definitions. - For any given variable, only one header file declares it (SPOT —
Single Point of Truth). - A source file never contains
externdeclarations 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"
int i; /* Do not do this in portable code */
void inc(void) i++;
file11.c
#include "prog2.h"
int i; /* Do not do this in portable code */
void dec(void) i--;
file12.c
#include "prog2.h"
#include <stdio.h>
int i = 9; /* Do not do this in portable code */
void put(void) printf("i = %dn", i);
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 asizeofor
_Alignofoperator 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 declared i as a double instead of as an int,
C's type-unsafe linkers probably would not spot the mismatch.
If you're on a machine with 64-bit int and double, you'd not even
get a warning; on a machine with 32-bit int 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.
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();
prog2usesprog2.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: %dn", 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: %dn", increment());
return 0;
prog3usesprog3.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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog4usesprog4.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, iffile4b.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.hfor the EXTERN macro definitions, etc.file1c.hto define types (notably,struct oddball, the type ofoddball_struct).file2c.hto define or declare the global variables.file3c.cwhich defines the global variables.file4c.cwhich simply uses the global variables.file5c.cwhich shows that you can declare and then define the global variables.file6c.cwhich shows that you can define and then (attempt to) declare the global variables.
In these examples, file5c.c and file6c.c directly include the headerfile2c.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:
- The header defining or declaring the global variables may not itself
define any types. - Immediately before you include a header that should define variables,
you define the macro DEFINE_VARIABLES. - 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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog5usesprog5.c,file3c.c,file4c.c,file1c.h,file2c.h,external.h.prog6usesprog5.c,file5c.c,file4c.c,file1c.h,file2c.h,external.h.prog7usesprog5.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 intofile2d.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 ofDEFINE_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 thefile2d.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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
file9c.c
#include "file2d.h"
#include <stdio.h>
void use_them(void)
printf("Global variable: %dn", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
prog8usesprog8.c,file7c.c,file9c.c.prog9usesprog8.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.)
3
@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
3
@Zak: No. The conditional code infile3a.his#ifdef DEFINE_VARIABLES / #define EXTERN / #else / #define EXTERN extern / #endif, removing comments and using slashes to mark the ends of lines. IfDEFINE_VARIABLESis specified, then the variables should not have theexternprefix 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 2:14
11
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 5 '14 at 3:28
|
show 22 more comments
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 = %dn", test1_var);
return 0;
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
1
In the above example, if I change theextern int test1_var;toint test1_var;, the linker (gcc 5.4.0) still passes. So, isexternreally needed in this case?
– radiohead
Mar 24 '18 at 3:15
2
@radiohead: In my answer, you will find the information that dropping theexternis 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
add a comment |
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.
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
add a comment |
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."
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 2:16
add a comment |
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.
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
add a comment |
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.
What difference betweenint fooandextern int foo(file scope)? Both are declaration, isn't it?
– user1150105
Nov 8 '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 9 '12 at 9:12
add a comment |
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.
add a comment |
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>
add a comment |
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.
add a comment |
GCC ELF Linux implementation
main.c:
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main()
printf("%dn", not_extern_int);
printf("%dn", 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++?
3
It's not my down-vote, so I don't know. However, I'll proffer an opinion. Although looking at the output ofreadelfornmcan be helpful, you've not explained the fundamentals of how to make use ofextern, nor completed the first program with the actual definition. Your code doesn't even usenotExtern. There's a nomenclature problem, too: althoughnotExternis defined here rather than declared withextern, it is an external variable that could be accessed by other source files if those translation units contained a suitable declaration (which would needextern int notExtern;!).
– Jonathan Leffler
Aug 30 '15 at 14:57
@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 usingnotExternwas 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 新疆改造中心 六四事件 法轮功
Sep 2 '15 at 14:52
As to names, what aboutglobal_deffor the variable defined here, andextern_reffor the variable defined in some other module? Would they have suitably clear symmetry? You still end up withint 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. Havingextern 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 2 '15 at 14:56
add a comment |
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.
add a comment |
extern simply means a variable is defined elsewhere (e.g., in another file).
add a comment |
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.
add a comment |
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.
add a comment |
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.
3
Note that theexterndeclaration should be in a header, not infirst.c, so that if the type changes, the declaration will change too. Also, the header that declares the variable should be included bysecond.cto 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 2 '15 at 15:09
add a comment |
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!
add a comment |
protected by Lundin Aug 26 '15 at 14:18
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
16 Answers
16
active
oldest
votes
16 Answers
16
active
oldest
votes
active
oldest
votes
active
oldest
votes
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: %dn", 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: %dn", increment());
return 0;
prog1usesprog1.c,file1.c,file2.c,file3.handprog1.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
externdeclarations of variables — neverstaticor unqualified variable definitions. - For any given variable, only one header file declares it (SPOT —
Single Point of Truth). - A source file never contains
externdeclarations 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"
int i; /* Do not do this in portable code */
void inc(void) i++;
file11.c
#include "prog2.h"
int i; /* Do not do this in portable code */
void dec(void) i--;
file12.c
#include "prog2.h"
#include <stdio.h>
int i = 9; /* Do not do this in portable code */
void put(void) printf("i = %dn", i);
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 asizeofor
_Alignofoperator 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 declared i as a double instead of as an int,
C's type-unsafe linkers probably would not spot the mismatch.
If you're on a machine with 64-bit int and double, you'd not even
get a warning; on a machine with 32-bit int 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.
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();
prog2usesprog2.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: %dn", 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: %dn", increment());
return 0;
prog3usesprog3.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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog4usesprog4.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, iffile4b.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.hfor the EXTERN macro definitions, etc.file1c.hto define types (notably,struct oddball, the type ofoddball_struct).file2c.hto define or declare the global variables.file3c.cwhich defines the global variables.file4c.cwhich simply uses the global variables.file5c.cwhich shows that you can declare and then define the global variables.file6c.cwhich shows that you can define and then (attempt to) declare the global variables.
In these examples, file5c.c and file6c.c directly include the headerfile2c.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:
- The header defining or declaring the global variables may not itself
define any types. - Immediately before you include a header that should define variables,
you define the macro DEFINE_VARIABLES. - 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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog5usesprog5.c,file3c.c,file4c.c,file1c.h,file2c.h,external.h.prog6usesprog5.c,file5c.c,file4c.c,file1c.h,file2c.h,external.h.prog7usesprog5.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 intofile2d.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 ofDEFINE_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 thefile2d.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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
file9c.c
#include "file2d.h"
#include <stdio.h>
void use_them(void)
printf("Global variable: %dn", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
prog8usesprog8.c,file7c.c,file9c.c.prog9usesprog8.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.)
3
@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
3
@Zak: No. The conditional code infile3a.his#ifdef DEFINE_VARIABLES / #define EXTERN / #else / #define EXTERN extern / #endif, removing comments and using slashes to mark the ends of lines. IfDEFINE_VARIABLESis specified, then the variables should not have theexternprefix 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 2:14
11
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 5 '14 at 3:28
|
show 22 more comments
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: %dn", 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: %dn", increment());
return 0;
prog1usesprog1.c,file1.c,file2.c,file3.handprog1.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
externdeclarations of variables — neverstaticor unqualified variable definitions. - For any given variable, only one header file declares it (SPOT —
Single Point of Truth). - A source file never contains
externdeclarations 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"
int i; /* Do not do this in portable code */
void inc(void) i++;
file11.c
#include "prog2.h"
int i; /* Do not do this in portable code */
void dec(void) i--;
file12.c
#include "prog2.h"
#include <stdio.h>
int i = 9; /* Do not do this in portable code */
void put(void) printf("i = %dn", i);
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 asizeofor
_Alignofoperator 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 declared i as a double instead of as an int,
C's type-unsafe linkers probably would not spot the mismatch.
If you're on a machine with 64-bit int and double, you'd not even
get a warning; on a machine with 32-bit int 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.
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();
prog2usesprog2.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: %dn", 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: %dn", increment());
return 0;
prog3usesprog3.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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog4usesprog4.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, iffile4b.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.hfor the EXTERN macro definitions, etc.file1c.hto define types (notably,struct oddball, the type ofoddball_struct).file2c.hto define or declare the global variables.file3c.cwhich defines the global variables.file4c.cwhich simply uses the global variables.file5c.cwhich shows that you can declare and then define the global variables.file6c.cwhich shows that you can define and then (attempt to) declare the global variables.
In these examples, file5c.c and file6c.c directly include the headerfile2c.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:
- The header defining or declaring the global variables may not itself
define any types. - Immediately before you include a header that should define variables,
you define the macro DEFINE_VARIABLES. - 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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog5usesprog5.c,file3c.c,file4c.c,file1c.h,file2c.h,external.h.prog6usesprog5.c,file5c.c,file4c.c,file1c.h,file2c.h,external.h.prog7usesprog5.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 intofile2d.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 ofDEFINE_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 thefile2d.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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
file9c.c
#include "file2d.h"
#include <stdio.h>
void use_them(void)
printf("Global variable: %dn", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
prog8usesprog8.c,file7c.c,file9c.c.prog9usesprog8.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.)
3
@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
3
@Zak: No. The conditional code infile3a.his#ifdef DEFINE_VARIABLES / #define EXTERN / #else / #define EXTERN extern / #endif, removing comments and using slashes to mark the ends of lines. IfDEFINE_VARIABLESis specified, then the variables should not have theexternprefix 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 2:14
11
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 5 '14 at 3:28
|
show 22 more comments
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: %dn", 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: %dn", increment());
return 0;
prog1usesprog1.c,file1.c,file2.c,file3.handprog1.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
externdeclarations of variables — neverstaticor unqualified variable definitions. - For any given variable, only one header file declares it (SPOT —
Single Point of Truth). - A source file never contains
externdeclarations 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"
int i; /* Do not do this in portable code */
void inc(void) i++;
file11.c
#include "prog2.h"
int i; /* Do not do this in portable code */
void dec(void) i--;
file12.c
#include "prog2.h"
#include <stdio.h>
int i = 9; /* Do not do this in portable code */
void put(void) printf("i = %dn", i);
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 asizeofor
_Alignofoperator 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 declared i as a double instead of as an int,
C's type-unsafe linkers probably would not spot the mismatch.
If you're on a machine with 64-bit int and double, you'd not even
get a warning; on a machine with 32-bit int 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.
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();
prog2usesprog2.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: %dn", 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: %dn", increment());
return 0;
prog3usesprog3.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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog4usesprog4.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, iffile4b.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.hfor the EXTERN macro definitions, etc.file1c.hto define types (notably,struct oddball, the type ofoddball_struct).file2c.hto define or declare the global variables.file3c.cwhich defines the global variables.file4c.cwhich simply uses the global variables.file5c.cwhich shows that you can declare and then define the global variables.file6c.cwhich shows that you can define and then (attempt to) declare the global variables.
In these examples, file5c.c and file6c.c directly include the headerfile2c.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:
- The header defining or declaring the global variables may not itself
define any types. - Immediately before you include a header that should define variables,
you define the macro DEFINE_VARIABLES. - 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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog5usesprog5.c,file3c.c,file4c.c,file1c.h,file2c.h,external.h.prog6usesprog5.c,file5c.c,file4c.c,file1c.h,file2c.h,external.h.prog7usesprog5.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 intofile2d.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 ofDEFINE_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 thefile2d.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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
file9c.c
#include "file2d.h"
#include <stdio.h>
void use_them(void)
printf("Global variable: %dn", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
prog8usesprog8.c,file7c.c,file9c.c.prog9usesprog8.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.)
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: %dn", 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: %dn", increment());
return 0;
prog1usesprog1.c,file1.c,file2.c,file3.handprog1.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
externdeclarations of variables — neverstaticor unqualified variable definitions. - For any given variable, only one header file declares it (SPOT —
Single Point of Truth). - A source file never contains
externdeclarations 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"
int i; /* Do not do this in portable code */
void inc(void) i++;
file11.c
#include "prog2.h"
int i; /* Do not do this in portable code */
void dec(void) i--;
file12.c
#include "prog2.h"
#include <stdio.h>
int i = 9; /* Do not do this in portable code */
void put(void) printf("i = %dn", i);
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 asizeofor
_Alignofoperator 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 declared i as a double instead of as an int,
C's type-unsafe linkers probably would not spot the mismatch.
If you're on a machine with 64-bit int and double, you'd not even
get a warning; on a machine with 32-bit int 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.
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();
prog2usesprog2.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: %dn", 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: %dn", increment());
return 0;
prog3usesprog3.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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog4usesprog4.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, iffile4b.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.hfor the EXTERN macro definitions, etc.file1c.hto define types (notably,struct oddball, the type ofoddball_struct).file2c.hto define or declare the global variables.file3c.cwhich defines the global variables.file4c.cwhich simply uses the global variables.file5c.cwhich shows that you can declare and then define the global variables.file6c.cwhich shows that you can define and then (attempt to) declare the global variables.
In these examples, file5c.c and file6c.c directly include the headerfile2c.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:
- The header defining or declaring the global variables may not itself
define any types. - Immediately before you include a header that should define variables,
you define the macro DEFINE_VARIABLES. - 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: %dn", 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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
prog5usesprog5.c,file3c.c,file4c.c,file1c.h,file2c.h,external.h.prog6usesprog5.c,file5c.c,file4c.c,file1c.h,file2c.h,external.h.prog7usesprog5.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 intofile2d.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 ofDEFINE_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 thefile2d.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: %dn", increment());
printf("Oddball: %dn", oddball_value());
return 0;
file9c.c
#include "file2d.h"
#include <stdio.h>
void use_them(void)
printf("Global variable: %dn", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
prog8usesprog8.c,file7c.c,file9c.c.prog9usesprog8.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.)
edited Jul 3 '18 at 16:04
answered Sep 16 '09 at 14:37
Jonathan LefflerJonathan Leffler
573k956881041
573k956881041
3
@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
3
@Zak: No. The conditional code infile3a.his#ifdef DEFINE_VARIABLES / #define EXTERN / #else / #define EXTERN extern / #endif, removing comments and using slashes to mark the ends of lines. IfDEFINE_VARIABLESis specified, then the variables should not have theexternprefix 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 2:14
11
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 5 '14 at 3:28
|
show 22 more comments
3
@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
3
@Zak: No. The conditional code infile3a.his#ifdef DEFINE_VARIABLES / #define EXTERN / #else / #define EXTERN extern / #endif, removing comments and using slashes to mark the ends of lines. IfDEFINE_VARIABLESis specified, then the variables should not have theexternprefix 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 2:14
11
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 5 '14 at 3:28
3
3
@litb: see Annex J.5.11 for the common definition - it is a common extension.
– Jonathan Leffler
Sep 16 '09 at 15:19
@litb: see Annex J.5.11 for the common definition - it is a common extension.
– Jonathan Leffler
Sep 16 '09 at 15:19
3
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
@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
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
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
3
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 2:14
@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 2:14
11
11
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 5 '14 at 3:28
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 5 '14 at 3:28
|
show 22 more comments
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 = %dn", test1_var);
return 0;
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
1
In the above example, if I change theextern int test1_var;toint test1_var;, the linker (gcc 5.4.0) still passes. So, isexternreally needed in this case?
– radiohead
Mar 24 '18 at 3:15
2
@radiohead: In my answer, you will find the information that dropping theexternis 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
add a comment |
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 = %dn", test1_var);
return 0;
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
1
In the above example, if I change theextern int test1_var;toint test1_var;, the linker (gcc 5.4.0) still passes. So, isexternreally needed in this case?
– radiohead
Mar 24 '18 at 3:15
2
@radiohead: In my answer, you will find the information that dropping theexternis 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
add a comment |
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 = %dn", test1_var);
return 0;
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
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 = %dn", test1_var);
return 0;
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
edited Feb 18 '17 at 20:49
jww
54.1k41234513
54.1k41234513
answered Sep 16 '09 at 14:12
Johannes WeissJohannes Weiss
39.7k1483120
39.7k1483120
18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
1
In the above example, if I change theextern int test1_var;toint test1_var;, the linker (gcc 5.4.0) still passes. So, isexternreally needed in this case?
– radiohead
Mar 24 '18 at 3:15
2
@radiohead: In my answer, you will find the information that dropping theexternis 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
add a comment |
18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
1
In the above example, if I change theextern int test1_var;toint test1_var;, the linker (gcc 5.4.0) still passes. So, isexternreally needed in this case?
– radiohead
Mar 24 '18 at 3:15
2
@radiohead: In my answer, you will find the information that dropping theexternis 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
18
18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
There's no "pseudo-definitions". It's a declaration.
– sbi
Sep 16 '09 at 14:18
1
1
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 3:15
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 3:15
2
2
@radiohead: In my answer, 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
@radiohead: In my answer, 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
add a comment |
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.
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
add a comment |
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.
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
add a comment |
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.
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.
edited Sep 16 '09 at 14:21
answered Sep 16 '09 at 14:11
Arkaitz JimenezArkaitz Jimenez
16.2k86395
16.2k86395
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
add a comment |
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
5
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
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
add a comment |
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."
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 2:16
add a comment |
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."
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 2:16
add a comment |
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."
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."
answered Sep 16 '09 at 14:50
BuggieboyBuggieboy
2,84544375
2,84544375
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 2:16
add a comment |
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 2:16
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 2:16
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 2:16
add a comment |
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.
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
add a comment |
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.
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
add a comment |
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.
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.
answered Sep 16 '09 at 14:18
BenBBenB
5,99062530
5,99062530
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
add a comment |
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
The memory isn't declared. See the answers to this question: stackoverflow.com/questions/1410563 for more details.
– sbi
Sep 16 '09 at 14:37
add a comment |
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.
What difference betweenint fooandextern int foo(file scope)? Both are declaration, isn't it?
– user1150105
Nov 8 '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 9 '12 at 9:12
add a comment |
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.
What difference betweenint fooandextern int foo(file scope)? Both are declaration, isn't it?
– user1150105
Nov 8 '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 9 '12 at 9:12
add a comment |
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.
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.
edited May 23 '17 at 12:18
Community♦
11
11
answered Sep 16 '09 at 14:16
sbisbi
167k40217400
167k40217400
What difference betweenint fooandextern int foo(file scope)? Both are declaration, isn't it?
– user1150105
Nov 8 '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 9 '12 at 9:12
add a comment |
What difference betweenint fooandextern int foo(file scope)? Both are declaration, isn't it?
– user1150105
Nov 8 '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 9 '12 at 9:12
What difference between
int foo and extern int foo (file scope)? Both are declaration, isn't it?– user1150105
Nov 8 '12 at 19:07
What difference between
int foo and extern int foo (file scope)? Both are declaration, isn't it?– user1150105
Nov 8 '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 9 '12 at 9:12
@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 9 '12 at 9:12
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Jun 20 '12 at 23:43
Alex LockwoodAlex Lockwood
75.3k34184233
75.3k34184233
add a comment |
add a comment |
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>
add a comment |
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>
add a comment |
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>
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>
edited Aug 20 '12 at 10:45
jogabonito
1,74952039
1,74952039
answered Jul 2 '12 at 9:11
Phoenix225Phoenix225
122511
122511
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Aug 20 '12 at 10:19
AnupAnup
40955
40955
add a comment |
add a comment |
GCC ELF Linux implementation
main.c:
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main()
printf("%dn", not_extern_int);
printf("%dn", 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++?
3
It's not my down-vote, so I don't know. However, I'll proffer an opinion. Although looking at the output ofreadelfornmcan be helpful, you've not explained the fundamentals of how to make use ofextern, nor completed the first program with the actual definition. Your code doesn't even usenotExtern. There's a nomenclature problem, too: althoughnotExternis defined here rather than declared withextern, it is an external variable that could be accessed by other source files if those translation units contained a suitable declaration (which would needextern int notExtern;!).
– Jonathan Leffler
Aug 30 '15 at 14:57
@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 usingnotExternwas 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 新疆改造中心 六四事件 法轮功
Sep 2 '15 at 14:52
As to names, what aboutglobal_deffor the variable defined here, andextern_reffor the variable defined in some other module? Would they have suitably clear symmetry? You still end up withint 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. Havingextern 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 2 '15 at 14:56
add a comment |
GCC ELF Linux implementation
main.c:
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main()
printf("%dn", not_extern_int);
printf("%dn", 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++?
3
It's not my down-vote, so I don't know. However, I'll proffer an opinion. Although looking at the output ofreadelfornmcan be helpful, you've not explained the fundamentals of how to make use ofextern, nor completed the first program with the actual definition. Your code doesn't even usenotExtern. There's a nomenclature problem, too: althoughnotExternis defined here rather than declared withextern, it is an external variable that could be accessed by other source files if those translation units contained a suitable declaration (which would needextern int notExtern;!).
– Jonathan Leffler
Aug 30 '15 at 14:57
@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 usingnotExternwas 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 新疆改造中心 六四事件 法轮功
Sep 2 '15 at 14:52
As to names, what aboutglobal_deffor the variable defined here, andextern_reffor the variable defined in some other module? Would they have suitably clear symmetry? You still end up withint 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. Havingextern 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 2 '15 at 14:56
add a comment |
GCC ELF Linux implementation
main.c:
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main()
printf("%dn", not_extern_int);
printf("%dn", 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++?
GCC ELF Linux implementation
main.c:
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main()
printf("%dn", not_extern_int);
printf("%dn", 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++?
edited Feb 3 at 17:27
answered May 29 '15 at 7:34
Ciro Santilli 新疆改造中心 六四事件 法轮功Ciro Santilli 新疆改造中心 六四事件 法轮功
148k34560475
148k34560475
3
It's not my down-vote, so I don't know. However, I'll proffer an opinion. Although looking at the output ofreadelfornmcan be helpful, you've not explained the fundamentals of how to make use ofextern, nor completed the first program with the actual definition. Your code doesn't even usenotExtern. There's a nomenclature problem, too: althoughnotExternis defined here rather than declared withextern, it is an external variable that could be accessed by other source files if those translation units contained a suitable declaration (which would needextern int notExtern;!).
– Jonathan Leffler
Aug 30 '15 at 14:57
@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 usingnotExternwas 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 新疆改造中心 六四事件 法轮功
Sep 2 '15 at 14:52
As to names, what aboutglobal_deffor the variable defined here, andextern_reffor the variable defined in some other module? Would they have suitably clear symmetry? You still end up withint 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. Havingextern 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 2 '15 at 14:56
add a comment |
3
It's not my down-vote, so I don't know. However, I'll proffer an opinion. Although looking at the output ofreadelfornmcan be helpful, you've not explained the fundamentals of how to make use ofextern, nor completed the first program with the actual definition. Your code doesn't even usenotExtern. There's a nomenclature problem, too: althoughnotExternis defined here rather than declared withextern, it is an external variable that could be accessed by other source files if those translation units contained a suitable declaration (which would needextern int notExtern;!).
– Jonathan Leffler
Aug 30 '15 at 14:57
@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 usingnotExternwas 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 新疆改造中心 六四事件 法轮功
Sep 2 '15 at 14:52
As to names, what aboutglobal_deffor the variable defined here, andextern_reffor the variable defined in some other module? Would they have suitably clear symmetry? You still end up withint 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. Havingextern 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 2 '15 at 14:56
3
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
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
@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 新疆改造中心 六四事件 法轮功
Sep 2 '15 at 14:52
@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 新疆改造中心 六四事件 法轮功
Sep 2 '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 2 '15 at 14:56
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 2 '15 at 14:56
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited Sep 2 '15 at 15:00
Jonathan Leffler
573k956881041
573k956881041
answered Oct 3 '12 at 4:58
loganaayaheeloganaayahee
6321613
6321613
add a comment |
add a comment |
extern simply means a variable is defined elsewhere (e.g., in another file).
add a comment |
extern simply means a variable is defined elsewhere (e.g., in another file).
add a comment |
extern simply means a variable is defined elsewhere (e.g., in another file).
extern simply means a variable is defined elsewhere (e.g., in another file).
edited May 5 '18 at 6:34
Jonathan Leffler
573k956881041
573k956881041
answered Jan 27 '16 at 19:47
GeremiaGeremia
1,3331725
1,3331725
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited Sep 2 '15 at 15:04
Jonathan Leffler
573k956881041
573k956881041
answered Aug 9 '12 at 9:21
user1270846user1270846
1066
1066
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited Jan 9 at 20:56
answered Jan 9 at 20:50
Lucian NutLucian Nut
139213
139213
add a comment |
add a comment |
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.
3
Note that theexterndeclaration should be in a header, not infirst.c, so that if the type changes, the declaration will change too. Also, the header that declares the variable should be included bysecond.cto 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 2 '15 at 15:09
add a comment |
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.
3
Note that theexterndeclaration should be in a header, not infirst.c, so that if the type changes, the declaration will change too. Also, the header that declares the variable should be included bysecond.cto 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 2 '15 at 15:09
add a comment |
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.
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.
edited Sep 2 '15 at 15:06
Jonathan Leffler
573k956881041
573k956881041
answered Sep 1 '14 at 7:35
shohamshoham
25319
25319
3
Note that theexterndeclaration should be in a header, not infirst.c, so that if the type changes, the declaration will change too. Also, the header that declares the variable should be included bysecond.cto 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 2 '15 at 15:09
add a comment |
3
Note that theexterndeclaration should be in a header, not infirst.c, so that if the type changes, the declaration will change too. Also, the header that declares the variable should be included bysecond.cto 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 2 '15 at 15:09
3
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 2 '15 at 15:09
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 2 '15 at 15:09
add a comment |
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!
add a comment |
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!
add a comment |
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!
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!
edited Oct 9 '18 at 12:16
answered Oct 9 '18 at 10:01
user50619user50619
200212
200212
add a comment |
add a comment |
protected by Lundin Aug 26 '15 at 14:18
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?