Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
389 views
in Technique[技术] by (71.8m points)

c - Advantage of using extern in a header file

There is a similar question title here, but in reading the answers it doesn't seem to address that particular question: C: What is the use of 'extern' in header files? (it's more like a "Why use header files?").

In the following usages of extern:

extern int a;
int b;

// structs have no external linkage 
typedef struct Item_ {int id;} Item;
extern Item Item_Extern;

void main(void) {

}

// later in the program or another file
int a=4;
int b=5;
Item Item_Extern = {1};

Does the compiler produce any different assembly if extern is used, or is this more a metadata-tag for a type so that a user of the code (or compiler) will know explicitly that the definition will come in a different file (usually) or later on in the current file, and if it doesn't it'll be a hard error. Or, does extern 'do' anything else?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Introduction

There are three forms of declaration of concern here:1

extern int x; // Declares x but does not define it.
int x;        // Tentative definition of x.
int x = 0;    // Defines x.

A declaration makes an identifier (a name, like x) known.

A definition creates an object (such as an int).2 A definition is also a declaration, since it makes the name known.

A tentative definition without a regular definition in the same translation unit (the source file being compiled, with all of its included files) acts like a definition with an initializer of zero.

The way you should use these normally is:

  • For an object you will access by name in multiple files, write exactly one definition of it in one source file. (It can be a tentative definition3 if you would like it to be initialized with zero, or it can be a regular definition with an initializer you choose.)
  • In an associated header file (such as foo.h for the source file foo.c), declare the name, using extern as shown above.
  • Include the header file in each file that uses the name, including its associated source file. (The latter is important; when foo.c includes foo.h, the compiler will see both the declaration and the definition in the same compilation and give you a warning if there is a typo that makes the two declarations incompatible.)

Actually, the way you should use these normally is not to use them at all. Programs generally do not need external identifiers for objects, so you should design the program without them. The rules above are for when you do use them.

Tentative Definitions

In Unix and some other systems, it has been possible to put a tentative definition, int x;, in a header file and include it in multiple source files. Because a tentative definition acts like a definition in the absence of a regular definition, this results in there being multiple definitions in multiple translation units. The C standard does not define the behavior of this. So how does it work in Unix?

Until recently, when you compiled with GCC, it created an object file that marked tentatively defined identifiers differently from regularly defined identifiers. The tentatively defined identifiers were marked as “common.” When the linker found multiple definitions of a “common” identifier, it coalesced them into a single definition. Remember, the C standard does not define the behavior. But Unix tools4 did. So you could put int x; in a header and include it in lots of places, and you would get one int x out of it when linking the entire program.

Recent versions of GCC do not do this by default. Tentative definitions are, in the absence of regular definitions, treated more like regular definitions, and linking with multiple definitions of the same identifier will result in an error, even if the definitions arose from tentative definitions. GCC has a switch to select the old behavior, -fcommon.

This is information you should know so that you can understand old source files and headers that took advantage of the “common” behavior. It is not needed in new source code, and you should write only non-definition declarations (using extern) in headers and regular definitions in source files.

Miscellaneous

You do not need extern with a function declaration because a function declaration without a body (the compound statement that contains the code for the function) is automatically a declaration and behaves the same as if it had extern. Functions do not have tentative definitions.

Footnote

1 This answer addresses only external declarations and external definitions for identifiers of objects, with external linkage. The full rules for C declarations are somewhat complicated, partly due to the history of C’s evolution.

2 This is for definitions of identifiers that refer to objects. For other kinds of identifiers, what is a definition may be different. For example, typedef int foo is said to define foo as an alias for the type int, but no object is created.

3 It may be preferable to also include an initializer, even if it is zero, as this will make it a regular definition and avoid a potential problem where the same name is used tentative definitions in two different source files for two different things, resulting in the linker not complaining even though this is an error.

4 I may be being sloppy with terminology here; somebody else could identify precisely where this behavior was specified and what tools it applied to.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...