Before discussing global variables, we must first understand several basic concepts:
1. compilation unit (module ):
Today, when IDE development tools are widely used, many developers are no longer clear about the concepts of compilation. Many programmers are most afraid of handling connection errors ), because it does not give the specific location of your program errors as a compilation error, you are often annoyed by this error, but if you often use GCC, if makefile and other tools are used for development in Linux or embedded systems, you may understand the differences between compilation and connection! Errors may be generated in two places. One is a compilation error, which is mainly a syntax error, the other is a connection error, and the other is a repeated definition of variables. The compilation unit is each OBJ file generated during the compilation phase. An OBJ file is a compilation unit, that is, a CPP (. c) corresponds to it. H files constitute a compilation unit. A project consists of multiple compilation units. Each OBJ file contains the relative address of variable storage.
2. Differences between declaration and definition
When a function or variable is declared, it does not provide its actual physical memory space. Sometimes it can ensure that your program is compiled. However, when a function or variable is defined, it has actual physical space in the memory. If the external variables referenced in the compilation module are not defined in any part of the project, even if it can pass during compilation, an error will be reported during connection because the program cannot find this variable in the memory! You can also understand that the same variable or function can be declared multiple times, and the definition can only be one time!
3. Functions of extern
Extern has two functions. First, when it is connected with "C", for example, extern "C" Void fun (int A, int B ); the compiler will tell the compiler to translate the corresponding function name instead of the C ++ name according to the C rule when compiling the function name fun, when translating this function name, the C ++ rule will make the name "fun" completely invisible. It may be fun @ abc_int_int # % $ or something else, this depends on the "temper" of the compiler (the methods used by different compilers are different). Why does this happen because C ++ supports function overloading, I will not discuss this issue too much here. If you are interested, you can search for it online. I believe you can get a satisfactory explanation!
When extern is not modified with "C", for example, extern int g_int in the header file, it declares the keyword of the function or global variable scope, the declared functions and variables can be used in other modules in this module. Remember that it is a declaration, not a definition! That is to say, if Module B (compilation unit) references the global variables or functions defined in module A, it only needs to include the header file of module A. During the compilation phase, although Module B cannot find this function or variable, it does not report an error. It will find this function from the target code generated by module A during connection.
If you understand the above concepts, let's take a look at the usage differences of the following global variables/constants:
1. Global variables modified with extern
The role of extern has been described above. The following is an example:
The following statements are contained in test1.h:
# Ifndef test1h
# Define test1h
Extern char g_str []; // declare the global variable g_str
Void fun1 ();
# Endif
In test1.cpp
# Include "test1.h"
Char g_str [] = "123456"; // defines the global variable g_str
Void fun1 ()
{
Cout <g_str <Endl;
}
The above is the test1 module, which can be compiled and connected. If we still have the Test2 module and want to use g_str, we only need to reference it in the original file.
# Include "test1.h"
Void fun2 ()
{
Cout <g_str <Endl;
}
The above test1 and Test2 can be compiled and connected at the same time. If you are interested, you can use ultraedit to open test1.obj. You can include the string "123456" in it, but you cannot find it in test2.obj. This is because g_str is the global variable of the entire project and only one copy exists in the memory. The test2.obj compilation unit does not need to have one more copy, otherwise, this error will be repeatedly reported during connection!
Some people like to put together the Declaration and definition of global variables to avoid forgetting the definition. For example, change test1.h
Extern char g_str [] = "123456"; // This is equivalent to no extern
Then, remove the g_str definition in test1.cpp. When the test1 and Test2 modules are compiled and connected again, a connection error is reported because you put the definition of the global variable g_str after the header file, test1.cpp this module contains test1.h so it defines g_str once, And test2.cpp also contains test1.h so it defines g_str again. At this time, the connector finds two g_str pairs when connecting test1 and test2. If you want to put the g_str definition in test1.h, replace # include "test1.h" in Test2 code:
Extern char g_str [];
Void fun2 ()
{
Cout <g_str <Endl;
}
At this time, the compiler will know that g_str is an external compilation module and will not be repeatedly defined in this module, but I would like to say this is very bad, because you cannot use # include "test1.h" in test2.cpp, other functions declared in test1.h cannot be used unless they are modified with extern, in this case, you need to declare a large string of functions, and the role of the header file is to provide external interfaces for use. Therefore, remember to only declare the header file. The truth is always so simple.
2. Global variables modified with static
First, I want to tell you that static and extern are a pair of "water and fire" guys. That is to say, extern and static cannot modify a variable at the same time. Second, the static modified global variables are declared and defined at the same time. That is to say, when you declare the global variables using static in the header file, they are also defined. Finally, the scope of static global variable modification can only be its own compilation unit. That is to say, its "Global" is only valid for this compilation unit, but not for other compilation units. For example:
Test1.h:
# Ifndef test1h
# Define test1h
Static char g_str [] = "123456 ";
Void fun1 ();
# Endif
Test1.cpp:
# Include "test1.h"
Void fun1 ()
{
Cout <g_str <Endl;
}
Test2.cpp
# Include "test1.h"
Void fun2 ()
{
Cout <g_str <Endl;
}
The above two compilation units can be connected successfully. When you open test1.obj, you can find the strings "123456" in it, and you can also find them in test2.obj, the reason why they can be connected successfully without repeatedly defining errors is that although they have the same content, they store different physical addresses, just as two different variables assigned the same value, the two variables act on their respective compilation units.
Maybe you are more serious. You secretly track and debug the above Code. As a result, you find that the memory address of g_str of two compilation units (test1, Test2) is the same, so you can conclude that static modified variables can also be applied to other modules, but I want to tell you that your compiler is deceiving you, and most compilers have optimized functions for the code, in order to achieve the generation of the Target Program, the memory is reduced and the execution efficiency is higher. When the compiler connects to each compilation unit, it will copy only one copy of the memory with the same content, for example, in the above "123456", the variables in the two compilation units are the same content, so it will only exist in the memory during the connection, if you change the code above to the following, you can immediately crack the lies of the compiler:
Test1.cpp:
# Include "test1.h"
Void fun1 ()
{
G_str [0] = 'a ';
Cout <g_str <Endl;
}
Test2.cpp
# Include "test1.h"
Void fun2 ()
{
Cout <g_str <Endl;
}
Void main ()
{
Fun1 (); // a23456
Fun2 (); // 123456.
}
At this time, when you track the code, you will find that the g_str address in the two compilation units is not the same, because you modified it in one place, so the compiler is forced to restore the original memory, there are two copies in the memory to use the variables in the two modules.
It is precisely because static has the above features that, when defining static global variables, it is usually put in the original file rather than the header file, so that it will not cause unnecessary information pollution to other modules, remember this principle!
3. Global constants modified by const
The global constants modified by const are widely used. For example, the error message strings in software are defined by global constants. The global constants modified by const have the same characteristics as static, that is, they can only act on this compilation module, but const can be connected with extern to declare that this constant can act on other compilation modules, as shown in
Extern const char g_str [];
In the original file, do not forget to define:
Const char g_str [] = "123456 ";
So when the const is used independently, it is the same as static, and when it is used together with extern, its features are the same as extern! So I can't describe const too much. I just want to remind you that const char * g_str = "123456" is different from const char g_str [] = "123465, the preceding const modifies char * rather than g_str. Its g_str is not a constant and is regarded as a defined global variable (which can be used by other compilation units ), therefore, if you want char * g_str to follow the global constant rules of const, it is better to define const char * const g_str = "123456 ".