01 January 2007

The terminologies "definition" and "declaration" in this article are adopted from The C++ Programming Language (Special edition) (_TC++PL_ for short). In C: A Reference Manual (_Reference_ for short) they are called "defining declaration" and "referencing declaration" respectively.

Why Care

Distinguishing definitions from declarations are important since header files containing definitions could cause subtle problems. For example:

If a header file which contains a definition is included by more than one source files, each translation unit will get its own copy of that definition after preprocessing. The best result you may expect is that you'll get "multiple definition" error on linkage (except for ODR in C++). But there are also chances that compiler silently give you multiple instances of the variable. So it's always better to not put any definition in header files except for "const" variables and ODR (refer to Section 9.2.1 of TC++PL for more about what a header files should/shouldn't contain) .

This article is based on Reference and TC++PL. It reflects the test results of gcc and g++ of version (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-20). However, according to Reference, other C compiler's behavior may differ.

Confusing Statements

The most confusing statements (which are also where differences between C and C++ exist) are following two kinds of statements:

int i;
extern int i = 0;

For the first one, it is a definition in C++. In C the situation is rather complicated. The statement is deemed as a declaration if there is a definition in the same translation unit. Otherwise compiler will automatically define an i for you. But On linkage, variables defined by that means will be dealt with specially. Reference says that different compilers may choose different rules. According to test, GCC chooses which is called "Mixed Common Model" (4.8.4) by Reference. That is: if there are more than one translation unit contains such definition for i, those definitions will be combined into only one definition. Note how that is different from other kinds of definitions: multiple-definition is just illegal.

As for the second one, it is illegal in C. But in C++, the extern is silently ignored.

For a full discussion on C/C++ compatibility for declaration (of a more general sense), please refer to section 4.9 of Reference.

Clearer Ones

In fact, there are more clear statements. They mean the same in C/C++ as well.

  • All initializations (i.e. statements like following one) are definitions.
    int i = 0;                      // definition
    
  • All extern statements (except for the one discussed above) are declarations.
    extern int i;                   // declarations
    
  • For functions and structures, following are declarations:
    struct s;
    void f();
    

Section 9.2.3 of TC++PL detailed One Definition Rule (ODR). The ODR says that definitions of "class", "inline function", "template" can be repeated if and only if in different translation unit. Reference doesn't mention any rule alike. Nor did I find a way to test.

Advice

Avoid following statements, especially the first one:

int i;
extern int i = 0;

Instead, using following kinds of statements for declarations and definitions respectively whenever possible:

int i = 0; // initialization for definition
extern int i; // "extern" for declaration

Why?

  • Those two kinds of statement are more portable not only between C and C++ but also among C compilers.
  • Using initialization for definition helps to develop a good habit of initialize a variable when defining it: although compiler will initialize global variables for you, that is not the case for automatic variables.

Bibliography



blog comments powered by Disqus