The role of the volatile keyword
When I was looking at the basic knowledge of C language, I saw the volatile keyword. I didn’t understand it very much, so I checked the information and summarized it.
volatile is translated as: volatile
- volatile is a type modifier, just like the familiar const, it is a variable designed to modify access and modify by different threads;
- The function of volatile is to act as an instruction keyword to ensure that this instruction is not omitted due to compiler optimization, and requires direct reading of the value every time.
Simply put, it is to prevent the compiler from optimizing the code.
For example, the following program:
XBYTE[2]=0x55; XBYTE[2]=0x56; XBYTE[2]=0x57; XBYTE[2]=0x58;
For external hardware, the above four statements represent different operations and will produce four different actions, but the compiler will optimize the above four statements, believing that only XBYTE[2]=0x58 (that is, ignore the first three statements and only produce one machine code).
If you type volatile, the compiler compiles one by one and generates the corresponding machine code (generates four pieces of code).
Typical examples
for(int i=0; i<100000; i++);
This statement is used to test the speed of an empty loop.
But the compiler must optimize it and will not execute it at all
If you write
for(volatile int i=0; i<100000; i++);
It will execute
The original meaning of volatile is "changeable"
Here are a few examples of volatile variables:
1) Hardware registers of parallel devices (such as status registers)
2) Non-automatic variables that will be accessed in an interrupt service subroutine
3) Variables shared by several tasks in multithreaded applications
Some related interview questions
1) Can a parameter be either const or volatile? Explain why.
2) Can a pointer be a volatile? Explain why.
3) The following function is used to calculate the square of an integer. Can it achieve the expected design goals? If not, please answer what questions exist:
int square(volatile int *ptr) { return ((*ptr) * (*ptr)); }
Here is the answer:
1) Yes. An example is a read-only status register. It is volatile because it may be changed unexpectedly. It's const because the program should not try to modify it.
2) Yes. Although this is not very common. An example is when an interrupt service subroutine modifies a pointer to a buffer.
3) This code is a prank. The purpose of this code is to return the square of the pointer ptr points to the value, but since ptr points to a volatile parameter, the compiler will produce a code similar to the following:
int square(volatile int* &ptr)//The parameter here should be declared as a reference, otherwise only copy will be used in the function body and cannot be changed externally
{ int a,b; a = *ptr; b = *ptr; return a*b; }
Since the value of ptr may change between two value statements, a and b may be different.
As a result, this code may not return the squared value you expect!
The correct code is as follows:
long square(volatile intptr)
{ int a; a = *ptr; return a*a; }
In addition, it may also appear in the debug version that cannot be seen, but only appears in the release version:
#include<> void main(int argc,char *argv[]) { int i = 10; int a = i; printf("i=%d",a); //The function of the following assembly statement is to change the value of i in memory, but it does not let the compiler know__asm { mov dword ptr[ebp-4],20h } int b = i; printf("i=%d",b); }
Then, run the program in debug version mode, and the output result is as follows:
i = 10
i = 32
Then, run the program in release version mode, and the output result is as follows:
i = 10
i = 10
The output results clearly show that in release mode, the compiler optimized the code, and the second time the correct i value was not output.
Let's add the volatile keyword to the declaration of i to see what changes are:
#include<> void main(int argc,char *argv[]) { volatile int i = 10; int a = i; printf("i=%d",a); __asm { ` mov dword ptr[ebp-4],20h } int b = i; printf("i=%d",b); }
Run the program in the debug version and release version respectively, and the output is:
i = 10
i = 32
This shows that this keyword plays its role!
volatile should be interpreted as "direct access to the original memory address" is more appropriate, and the explanation of "variable" is simply a bit misleading;
For example, multi-threaded programs can manipulate this variable in the memory they are accessed together.
Your own program cannot determine when this variable will change
For example, it corresponds to a certain state of an external device. When an external device operates, the system changes the value of this variable through the driver and interrupt events, but your program does not know it.
For variables of volatile type, the system extracts them directly from the corresponding memory every time it is used, and does not use the original value in the cache to adapt to its unknown changes. The system will not optimize the processing of such variables - obviously it is also because its value may change at any time.
Place of use
Generally speaking, volatile is used in the following places:
1. The variables modified in the interrupt service program for detection by other programs need to be added with volatile;
2. The flags shared between tasks in a multi-task environment should be added with volatile;
3. The hardware registers for memory maps usually need to be specified with volatile, because each read and write may have different meanings;
In addition, the above situations often require the integrity of the data at the same time (half of the associated flags are interrupted and rewritten). In 1, it can be achieved through interrupts, 2, task scheduling can be prohibited, and 3, it can only rely on good hardware design.
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.