1. Static
(1) first introduce the first and most important one: hiding.
When we compile multiple files at the same time, all global variables and functions without the static prefix are globally visible. I will give an example to illustrate this sentence. We need to compile two source files, A. C and Main. C at the same time.
The contents of A.C are as follows:
Char A = 'a'; // global variable
Void MSG ()
{
Printf ("Hello/N ");
}
The content of Main. C is as follows:
Int main (void)
{
Extern char a; // extern variable must be declared before use
Printf ("% C", );
(Void) MSG ();
Return 0;
}
The running result of the program is:
A Hello
You may ask: why can the global variables A and MSG defined in A. C be used in Main. C? As mentioned above, all global variables and functions without the static prefix have global visibility, and other source files can also be accessed. In this example, A is a global variable, MSG is a function, and there is no static prefix. Therefore, it is visible to other source files main. C.
If static is added, other source files are hidden. For example, if static is added before the definitions of a and MSG, Main. C will not be able to see them. This feature allows you to define functions with the same name and variables in different files without worrying about name conflicts. Static can be used as the prefix of functions and variables. For a function, the role of static is limited to hiding. For a variable, static has the following two functions.
(2) The second role of static is to keep the variable content persistent. Variables stored in the static data area will be initialized at the beginning of the program, which is also the only initialization. There are two types of variables stored in the static storage area: global variables and static variables, but compared with global variables, static can control the visible range of variables. Static is used to hide the variables. Although this is not common, I will give an example.
# Include <stdio. h>
Int fun (void ){
Static int COUNT = 10; // In fact, this assignment statement has never been executed.
Return count --;
}
Int COUNT = 1;
Int main (void)
{
Printf ("Global/T/tlocal static/N ");
For (; count <= 10; ++ count)
Printf ("% d/T % d/N", Count, fun ());
Return 0;
}
The running result of the program is:
Global local static
1 10
2 9
3 8
4 7
5 6
6 5
7 4
8 3
9 2
10 1
(3) The third role of static is that the default Initialization is 0. In fact, global variables also have this attribute, because global variables are also stored in the static data zone. In the static data area, the default values of all bytes in the memory are 0x00. In some cases, this feature can reduce the workload of programmers. For example, to initialize a sparse matrix, we can set all elements to 0 one by one, and assign values to elements other than 0. If it is defined as static, the first 0 operation is saved. Another example is to use a character array as a string, but it is too troublesome to add '/0' at the end of the character array each time. If the string is defined as static, it saves the trouble because it is '/0 '. 2 inline: Let's look at the following function. The function body has only one line of statements:
Double average (double total, int number ){
Return total/number;
}
Is it necessary to define such a simple function? In fact, it has some advantages: First, it makes the program more readable; second, it makes this code reusable. However, it also has its disadvantages: when it is frequently called, the performance of the application (Time + space efficiency, especially time) will be improved due to the overhead of the function called) there is a loss. For example, if average repeatedly calls several thousand times in a loop statement, the execution efficiency of the program will be reduced.
Is there a way to avoid the overhead of function calls? For the above function, can I define it as an inline function form:
Inline double average (double total, int number ){
Return total/number;
}
Function introduction can reduce the program's target code and share program code.
Function calling requires time and space overhead. Calling a function actually transfers the program execution process to the called function. After the code of the called function is executed, it returns to the called place. This call operation requires that the on-site address be protected and memorized before the call, the on-site address be restored after the return, and the operation should be continued based on the original saved address. This overhead can be ignored for long functions, but for functions with very short code and frequently called functions, this overhead cannot be ignored. Inline functions are introduced to solve this problem and improve program running efficiency.
During program compilation, the compiler replaces the calling expression of the inline function in the program with the function body of the inline function. Because the code in the inline function body is replaced with the code in the program during compilation, the amount of code in the target program is increased and the space overhead is increased. The time overhead is not as large as the function call time, it can be seen that it is at the cost of increasing the target code in exchange for time savings.
◆ Conclusion: the inline function increases the running time efficiency, but increases the space overhead.
That is, the purpose of the inline function is to improve the execution efficiency (speed) of the function ).
Non-inline function calls have overhead for stack memory creation and release
In C, macro code can be used to improve execution efficiency. macro code is not a function, but like a function. The Compiler replaces function calls by copying macro code, saves the parameter pressure stack, generates call calls for the assembly language, returns parameters, and executes the return operation to increase the speed.
◆ Disadvantages of macro usage: (1) error-prone (unexpected marginal effect is often generated when the Preprocessor replicates macro code)
Example: # define max (a, B) (a)> (B )? (A): (B)
Statement result = max (I, j) + 2 is extended to result = (I)> (j )? (I) :( J) + 2;
But it means result = (I)> (j )? (I) :( J) + 2;
(2) debugging unavailable
(3) unable to operate private data members
The C ++ function inline mechanism not only improves the efficiency of macro code, but also increases the security and can operate data members freely.
The keyword inline must be put together with the function definition body to make the function inline. It does not work unless you put inline before the function declaration. Inlin is a keyword used for implementation, not a keyword used for declaration.
Many books Add the inline keyword before the declaration and definition of inline functions, but it should not be added before the declaration (adding or not will not affect the function), because the Declaration and definition cannot be confused.
★Declaration, definition, and statement
Declaration: The name is introduced to the system (a name is the alias of a memory block). It only tells the compiler the type of the name value and declares the existence of the name.
Definition: the storage space is allocated, that is, the storage type is available.
Statement: the basic component of the Program, which is divided into executable statements (defined as) and non-executable statements (declared ).
Some global or local variables defined before the program statement is formally written are declared in C and defined in C ++.
For example, int A; // it is declared in Standard C and cannot be executed. It is defined in C ++ and can be executed.
Extern int A; // declared as a non-executable cwinapp curapp statement; // The object definition is an executable statement.
◆ Pay attention to the following issues when using inline functions:
(1) inline functions defined in one file cannot be used in another file. They are usually shared in header files.
(2) inline functions should be concise and have only a few statements. If there are many statements, they are not suitable for being defined as inline functions.
(3) The Inline Function body cannot contain loop statements, if statements, or switch statements. Otherwise, even if the function is defined with the inline keyword, the compiler also treats the function as a non-inline function.
(4) inline functions should be declared before the function is called.
For example:
# Include <iostream. h>
Int increment (int I );
Inline int increment (int I ){
I ++; return I;
}
Void main (void ){......
}
If we modify the program, move the inline function definition to main:
# Include <iostream. h>
Int increment (int I );
Void main (void ){......
}
// Inline Function Definition after main () function
Inline int increment (int I ){
I ++; return I;
}
The inline function is defined after it is called. during compilation, the compiler will not directly replace it with main. That is to say, in fact, "increment (INT I)" is called only as a common function and does not have the nature of inline functions, which cannot improve the running efficiency.
3 volatile
Volatile reminds the compiler that the variables defined after it may change at any time. Therefore, the compiled program will directly read data from the variable address every time it needs to store or read the variable. If there is no volatile keyword, the compiler may optimize reading and storage, and may temporarily use the value in the register. If this variable is updated by another program, it will be inconsistent. The following is an example. In DSP development, it is often necessary to wait for an event to be triggered, so such a program is often written:
Short flag;
Void test ()
{
Do1 ();
While (flag = 0 );
Do2 ();
} This program will run do2 () only after the flag value of the memory variable changes to 1 (it is suspected that it is 0 (). The flag variable value is changed by another program, which may be a hardware interrupt service program. For example, if a button is pressed, the DSP is interrupted and the flag is changed to 1 in the key interrupt program so that the above program can continue to run. However, the compiler does not know that the flag value will be modified by other programs. Therefore, when it is optimized, it may first read the flag value into a register, and then wait until that register changes to 1. If this optimization is unfortunate, the while loop becomes an endless loop, because the contents of the register cannot be modified by the interrupted service program. To allow the program to read the value of the true flag variable every time, it must be defined as follows:
Volatile short flag;
It should be noted that it may run normally without volatile, but it may not run properly after the compiler optimization level is modified. Therefore, the debug version is normal, but the release version is not normal. For security, the volatile keyword is added as long as you wait for another program to modify a variable.
Volatile is intended to be "changeable"
Since the speed of accessing registers is faster than that of RAM, the compiler generally reduces the access to external Ram. For example:
Static int I = 0;
Int main (void)
{
...
While (1)
{
If (I) do_something ();
}
}
/* Interrupt service routine .*/
Void isr_2 (void)
{
I = 1;
}
The program is intended to call the do_something function in main when isr_2 is interrupted. However, since the compiler judges that I has not been modified in the main function, therefore, you may only perform one read operation from I to a register, and then use only the "I copy" in the register for each if judgment. As a result, do_something will never be called. If the variable is modified with volatile, the compiler ensures that the read and write operations on the variable are not optimized (certainly executed ). In this example, I should also describe this.
Generally, volatile is used in the following areas:
1. Volatile must be added to the variable modified in the interrupted service program for testing by other programs;
2. Volatile should be added to the labels shared by all tasks in a multi-task environment;
3. Volatile is also required for the hardware registers mapped to memory, because each read/write to it may have different meanings;
In addition, in the above situations, we often need to consider data integrity at the same time (some of the correlated symbols have been read in half and are interrupted for rewriting). In 1, we can achieve this through Guanzhong disconnection, task Scheduling can be disabled in 2, and 3 can only rely on the good design of hardware.
Volatile
Volatile is always related to optimization. the compiler has a technology called data stream analysis, which analyzes where variables are assigned, where they are used, and where they are invalid. The analysis results can be used for constant merging, constant propagation optimization, further eliminating code. However, sometimes these optimizations are not required by the program. In this case, you can use the volatile keyword to disable these optimizations. the literal meaning of volatile is variable and has the following functions:
1. The volatile variable is not cached in the register between two operations. In a multi-task, interrupted, or even setjmp environment, variables may be changed by other programs, and the compiler itself cannot know. Volatile tells the compiler this situation.
2. Do not optimize constant merging or constant propagation, so it is like the following code:
Volatile int I = 1;
If (I> 0 )...
If conditions are not considered unconditional.
3. read/write of volatile variables will not be optimized. If you assign values to a variable but do not use them later, the compiler can often omit the assignment operation. However, the processing of memory mapped Io cannot be optimized like this.
Some people mentioned above that volatile can ensure the atomicity of memory operations. This is not accurate. First, x86 requires a lock prefix to ensure atomicity under SMP. Second, some other methods, such as atomic_inc, must be used to guarantee atomicity.
For jiffies, it has been declared as a volatile variable. I think it is enough to use jiffies ++ directly. There is no need to use that complex form, because it cannot guarantee atomicity.
You may not know the following two sets of commands in Pentium and subsequent CPUs:
INC jiffies
;;
MoV jiffies, % eax
INC % eax
MoV % eax, jiffies
It works the same, but one command is not as fast as three commands.