A variable defined as volatile means that the variable may be changed unexpectedly, so the compiler will not assume the value of the variable. To be precise, the optimizer must be careful to re-read the value of the variable each time it is used, instead of using the copy stored in the register. Here are a few examples of volatile variables:
1). Hardware registers of parallel devices (such as status registers)
2). Non-automatic variables accessed in an interrupt service routine
3). Variables shared by several tasks in a multi-threaded application
People who cannot answer this question will not be hired. I think this is the most basic question that distinguishes C programmers from embedded system programmers. Embedded system programmers often deal with hardware, interrupts, RTOS, etc., all of which require volatile variables. Not understanding the content of volatile can be a disaster.
Assuming that the interviewee answered this question correctly (well, I doubt it will be the case), I will dig a little deeper to see if this guy really understands the full importance of volatile.
1). Can a parameter be both const and volatile? Explain why.
2). Can a pointer be volatile? Explain why.
3). What is wrong with the following function:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
Here are the answers:
1). Yes. An example is a read-only status register. It is volatile because it can be changed unexpectedly. It is const because the program should not try to modify it.
2). Yes. Although this is not very common. An example is when a service routine modifies a pointer to a buffer.
3). There is a trick in this code. The purpose of this code is to return the square of the value pointed to by pointer *ptr, but because *ptr points to a volatile parameter, the compiler will generate code similar to the following:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
Because the value of *ptr can be changed unexpectedly, a and b may be different. As a result, this code may not return the square value you expect! The correct code is as follows:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
The original meaning of volatile is "volatile". Since register access is faster than RAM, the compiler will generally optimize to reduce the access to external RAM. For example:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) dosomething();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
The original intention of the program is to call the dosomething function in main when the ISR_2 interrupt occurs. However, because the compiler determines that i has not been modified in the main function, it may only execute one read operation from i to a certain register, and then each if judgment only uses the "copy of i" in this register, resulting in dosomething never being called. If the variable is modified with volatile, the compiler guarantees that the read and write operations of this variable will not be optimized (definitely executed). i in this example should also be described in this way.
Generally speaking, volatile is used in the following places:
1. Variables modified in the interrupt service program for detection by other programs need to be added with volatile;
2. Flags shared between tasks in a multi-tasking environment should be added with volatile;
3. Memory-mapped hardware registers usually also need to be described with volatile, because each read and write to it may have different meanings;
In addition, the above situations often require the consideration of data integrity (interrelated flags are interrupted and rewritten halfway through reading). In 1, this can be achieved by disabling interrupts
, in 2, task scheduling can be disabled, and in 3, good hardware design is the only way to achieve this.
The original meaning of volatile is "volatile".
Since register access is faster than RAM, compilers generally optimize to reduce access to external RAM. For example:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) dosomething();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
The original intention of the program is to call the dosomething function in main when the ISR_2 interrupt occurs. However, since the compiler determines that i has not been modified in the main function, it
may only perform a read operation from i to a certain register once, and then each if judgment only uses the "copy of i" in this register, resulting in dosomething never being
called. If the variable is modified with volatile, the compiler guarantees that the read and write operations of this variable will not be optimized (definitely executed). The same should be said for i in this example.
Generally speaking, volatile is used in the following places:
1. Variables modified in the interrupt service program for detection by other programs need to be added with volatile;
2. Flags shared between tasks in a multitasking environment should be added with volatile;
3. Memory-mapped hardware registers usually also need to be declared volatile, because each read or write to them may have a different meaning;
In addition, the above situations often require the consideration of data integrity (interrelated flags are interrupted and rewritten halfway through reading). In 1, this can be achieved by disabling interrupts
, in 2, task scheduling can be disabled, and in 3, good hardware design is the only way to solve this problem.
The key lies in two places:
1. Compiler optimization (please help me understand the following)
In this thread, when reading a variable, in order to improve access speed, the compiler sometimes reads the variable into a register first during optimization; later, when getting the variable value, it directly gets the value from the register;
When the variable value is changed in this thread, the new value of the variable will be copied to the register at the same time to keep it consistent
When the value of a variable is changed by another thread, the value of the register will not change accordingly, causing the value read by the application to be inconsistent with the actual variable value.
When the register value is changed by another thread, the value of the original variable will not change, causing the value read by the application to be inconsistent with the actual variable value.
Here is an inaccurate example:
When distributing salaries, the accountant always called employees to register their bank card numbers. One time, the accountant did not register immediately to save time and used the previously registered bank card number. It happened that an employee's bank card was lost and the bank card number had been reported lost. As a result, the employee could not receive his salary.
Employee - original variable address
Bank card number - backup of the original variable in the register
2. Under what circumstances will it appear (as mentioned in the first post)
1). Hardware registers of parallel devices (such as status registers)
2). Non-automatic variables accessed by an interrupt service subroutine
3). Variables shared by several tasks in a multi-threaded application
Supplement: Volatile should be interpreted as "directly accessing the original memory address". The interpretation of "volatile" is a bit misleading.
"Volatile" is caused by external factors, such as multithreading, interrupts, etc. It does not mean that the variable modified with volatile is "volatile". If there is no external factor, it will not change even if it is defined with volatile.
After defining it with volatile, the variable will not change due to external factors and can be used with confidence. Let's see if the previous explanation (volatile) is misleading.
-------------A brief example is as follows:------------------
The volatile keyword is a type modifier. The type variable declared with it indicates that it can be changed by some factors unknown to the compiler, such as the operating system, hardware, or other threads. When encountering a variable declared with this keyword, the compiler will no longer optimize the code that accesses the variable, thereby providing stable access to special addresses.
Examples of using this keyword are as follows:
int volatile nVint;
>>>>When the value of a variable declared with volatile is required, the system always re-reads the data from the memory where it is located, even if the previous instruction has just read the data from there. And the read data is saved immediately.
For example:
volatile int i=10;
int a = i;
...
//Other codes do not explicitly tell the compiler that i has been operated on
int b = i;
>>>>volatile indicates that i may change at any time, and each time it is used, it must be read from the address of i, so the assembly code generated by the compiler will re-read the data from the address of i and put it in b. The optimization method is that since the compiler finds that the code between the two codes that read data from i has not operated on i, it will automatically put the last read data in b. Instead of re-reading from i. In this way, if i is a register variable or represents a port data, it is easy to make mistakes, so volatile can ensure stable access to special addresses.
>>>>Note that in vc6, the general debug mode does not perform code optimization, so the effect of this keyword cannot be seen. Next, we can insert assembly code to test the impact of the volatile keyword on the final program code:
>>>>First, use classwizard to create a win32 console project, insert a voltest.cpp file, and enter the following code:
>>
#include
void main()
{
int i=10;
int a = i;
printf("i= %d",a);
//The purpose of the following assembly statement is to change the value of i in memory without letting 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 results are as follows:
i = 10
i = 32
Then, run the program in release version mode, and the output results are as follows:
i = 10
i = 10
The output clearly shows that in release mode, the compiler optimized the code and did not output the correct i value the second time. Next, we add the volatile keyword to the declaration of i and see what changes occur:
#include
void main()
{
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 the release version respectively, and the output is:
i = 10
i = 32
This shows that this keyword has played its role!
The variable corresponding to volatile may change without your program knowing.
For example, in a multi-threaded program, multiple programs can manipulate this variable in the memory that is 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 the external device operates, the system changes the value of this variable through the driver and interrupt events, and your program does not know.
For volatile type variables, the system directly extracts them from the corresponding memory every time it uses them, 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 because its value may change at any time.
A typical example
is for ( int i=0; i<100000; i++);
This statement is used to test the speed of an empty loop.
However, the compiler will definitely optimize it and not execute it at all.
If you write
for ( volatile int i=0; i<100000; i++);
it will execute.
Previous article:C language source code for simulating serial port
Next article:10 simple digital filtering algorithms (C language source code)
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Digital tube decreasing display and running light control (beginner level) [including Proteus simulation and source code]
- Instructions for purchasing the CC1352R1 wireless development board on TI.com
- Waveform Generator Circuit
- MCU based on 2.4G wireless transceiver module NRF24L01 wireless communication (basic test passed)
- FPGA Introduction Series 6--Judgment Statement
- Award-winning live broadcast: Embark on the journey from embedded to cloud with PIC and AVR microcontrollers in the Microchip ecosystem
- 【Ended】Microchip Live Broadcast|Unveiling the ISA/IEC 62443 Safety Standard for Industrial Systems
- Interesting Algorithms (C Language Implementation 2nd Edition)
- OEM or research lab? Where does innovation come from?
- Sharing of wireless quality resources!