Wednesday, November 3, 2010

Duplicate Global Variable Definitions in C?

If you are still a novel C developer, a common, however bad, practice of using a global variable is shown as follows:

/* global.h */
int globalInt;

/* file1.c */
#include "global.h"
void foo() {
   globalInt = 1;

/* file2.c */
#include "global.h"
#include "stdio.h"

void bar() {
  printf("the globalInt is %d", globalInt);

Neither Visual C++ studio 2008 nor GCC 4 has any complaint compiling and linking the program as C.

However you will get "duplicate definition" error if you try to link the same program as C++ either using VC++ or GCC.

After I made some researches and found the reason, I feel embarrassed because I have already been though C was just too easy.

In order to know the reason, you need to know the variable declaration, variable definition, and tentative definition and "multiple external definitions" in C99.

When you define a variable, you are telling the compiler to allocate memory for that variable, and possibly also to initialize its contents to some value.
For example int i = 100;

When you declare a variable, you are telling the compiler that the variable was defined elsewhere. You are just telling the compiler that a variable by that name and type exists, but the compiler should not allocate memory for it since it is done somewhere else.
For example extern int i;

Here are the two important rules you have probably already known:
A variable must be defined once in one of the modules of the program. If there is no definition or more than one, an error is produced, possibly in the linking stage.

A variable may be declared many times, as long as the declarations are consistent with each other and with the definition. It may be declared in many modules, including the module where it was defined, and even many times in the same module. But it is usually pointless to declare it more than once in a module.
Since the "globalInt" in our example is outside of any function, it has the "extern" linkage. so is the "int globalInt" a definition or declaration?
Based on C99, it is called "tentative definition" in Section 6.9.2 as stated below:

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition.If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.
Based on the above C99 standard, both file1.c and file2.c should have the following definition after being compiled:
int globalInt = 0;

So if both VC++ and GCC strictly followed the C99 standard, the linking step should have failed due to duplicate variable definition.
However C99 does have an Annex J.5.11. as follows:
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).
So obviously both VC++ and GCC allow consistent multiple external definitions by extending the "tentative definition" to the entire program scope from the original file scope.

Now the last puzzle why C++ complained.
Because C++ treats C99 "tentative definition" as normal variable definition, you of course got the variable duplicate error.

No comments:

Post a Comment