Usage of extern in C/C++ Language

Declaration of External Variables

Modern compilers generally adopt a file-based compilation method, which means that global variables defined in different files are mutually transparent during compilation. In other words, the visibility of global variables is limited to the internal scope of the file during compilation. Here is a simple example. Create a project containing two simple C++ source files, A.cpp and B.cpp:

//A.cpp
int i;
void main(){}
//B.cpp
int i;

These two files are very simple. A.cpp defines a global variable i, and B also defines a global variable i. Both can compile successfully when compiled separately, but an error occurs during linking, with the following error message:

Linking...
B.obj : error LNK2005: "int i" (?i@@ 3HA ) already defined in A.obj
Debug/A.exe : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.
A.exe - 2 error(s), 0 warning(s)

This means that during the compilation phase, the global variables defined in different files are transparent to each other. When compiling A, it does not detect that i is also defined in B, and similarly, when compiling B, it does not detect that i is also defined in A. However, during the linking phase, when the contents of each file are combined, if there are global variable names defined in multiple files that are the same, a duplicate definition error (one or more multiply defined symbols) will occur.

Therefore, the global variable names defined in different files must not be the same.

During the linking phase, the obj files generated by the compiler merge the contents of A and B, which is also the reason for the int i duplicate definition error.

Example

A global variable defined in one file can be used anywhere in the program. For example, if a global variable is defined in file A, it can also be used in file B. Let’s modify our program to verify this:

//A.cpp
void main(){
    i = 100;  // Attempting to use the global variable defined in B
}
//B.cpp
int i;

An expected compilation error occurs, indicating that int i is undeclared (undeclared identifier Error), because the variables in files A and B are not visible to each other before linking.

Compiling...
A.cpp C:/Documents and Settings/wangjian/Desktop/try extern/A.cpp(5) : error C2065: 'i' : undeclared identifier
Error executing cl.exe.
A.obj - 1 error(s), 0 warning(s)

The compiler is unable to recognize that a variable symbol, although not defined in the current file, may be defined in another file. To avoid such errors, the extern keyword comes into play. After adding the extern keyword to the above erroneous program, it compiles and links successfully. The code is as follows:

//A.cpp
extern int i;
void main(){
    i = 100; // Attempting to use the global variable defined in B
}
//B.cpp
int i;

Calling C-style Functions in C++ Files

Compared to C, C++ introduces new features such as overloading, which have some important differences in compilation. Let’s compile the following small program in both C and C++ to explore the differences in compilation methods.

int i;
int func(int t){return 0;}
void main(){}

The result of compiling in C is as follows:

COMM     _i  :  DWORD
PUBLIC    _func
PUBLIC    _main

The result of compiling in C++ is as follows:

PUBLIC    ?i@@ 3HA         ; i
PUBLIC    ?func@@YAHH@Z    ; func
PUBLIC    _main

It can be seen that under C compilation, variable names and function names are prefixed with an underscore, while the results of C++ compilation are much more complex, with i becoming ?i@@ 3HA and func becoming ?func@@YAHH@Z. This seemingly complex naming rule in C++ serves the features of function overloading, parameter checking, and so on.

Function Calls Under Different Compilation Methods

If the project contains not only CPP files but also C files compiled in C style, function calls will have some subtle differences. Consider the following CPP file A.CPP and C file B.C:

//A.CPP
void func();
void main(){func();}
//B.C
void func(){}

Compiling A.CPP and B.C separately poses no issues, but an error occurs during linking. The reason is the conflict arising from the different compilation methods of C and CPP. As mentioned earlier, under C compilation, variable names and function names are prefixed with an underscore, while the results of C++ compilation are much more complex, with i becoming ?i@@ 3HA.

Linking...
A.obj : error LNK2001: unresolved external symbol "void __cdecl func(void)" (?func@@YAXXZ)
Debug/A.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.
A.exe - 2 error(s), 0 warning(s)

At this point, the extern keyword can help the compiler resolve the aforementioned issues. For this example, simply modifying A.CPP to the following code will suffice:

//A.CPP
extern "C"{
  void func(); // Introduce the function or variable compiled in C style
}
void main(){func();}

Leave a Comment