C language丨In-depth understanding of the volatile keyword

This article is a detailed analysis and introduction to the meaning of the keyword volatile in the C language. I hope to help you in learning.

 

Volatile is a type specifier. It is designed to modify variables that are accessed and modified by different threads. If there is no volatile, it will basically lead to such a result: either cannot write multi-threaded programs, or the compiler loses a lot of opportunities for optimization.

1. Principle effect

Volatile means "changeable" and should be interpreted as "direct access to the original memory address". "Variability" is caused by external factors, such as multithreading, interrupts, etc.

C language books define the volatile keyword as follows:

Volatile reminds the compiler that the variables defined behind it may change at any time, so every time the compiled program needs to store or read this variable, it tells the compiler not to optimize the variable, and it will directly start from the variable memory address. Read data, which can provide stable access to special addresses.

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, there will be inconsistencies. (To put it succinctly: the volatile keyword affects the results of the compiler's compilation. The variable declared with volatile indicates that the variable may change at any time. Do not perform compilation optimization for operations related to the variable to avoid errors)

2. General purpose

Generally speaking, volatile is used in the following places: 

1) Hardware registers of parallel devices (such as status registers)

         Memory-mapped hardware registers usually also need to add voliate, because each read and write to it may have a different meaning.

        For example: Suppose you want to initialize a device, and a certain register of this device is 0xff800000.

int *output = (unsigned int *)0xff800000;//Define an IO port;

int  init(void)

{

      int i;

      for(i=0;i< 10;i++){

        *output = i;

}

}

        After optimization by the compiler, the compiler thinks that the previous loop is nonsense for half a day, and has no effect on the final result, because in the end it just assigns the output pointer to 9, so the compiler finally compiles the compiled code for you. The result is equivalent to:

int init(void)

{

*output =9;

}

        If you initialize the external device in the same order as the code above, obviously the optimization process cannot achieve the goal. Conversely, if you do not repeat write operations to this port, but repeat read operations, the result is the same. After the compiler is optimized, perhaps your code reads this address only once. However, from the code point of view, there is no problem. At this time, you should use volatile to inform the compiler that this variable is unstable, and do not optimize when you encounter this variable.

2) Variables modified in the interrupt service program for detection by other programs need to add volatile; 

        When a variable is modified in a program that triggers an interrupt, and the compiler judges that the variable is not modified in the main function, it may only perform a read operation from memory to a register, and then only read a copy of the variable from the register each time. The operation of the interrupt program is short-circuited.

3) The flags shared between tasks in a multitasking environment should be volatile; 

        In this thread, when reading a variable, the compiler sometimes reads the variable into a register first during optimization; later, when the variable value is retrieved, the value is directly taken from the register; when the memory variable or register When the value of the variable is changed due to other threads, etc., the value of the register will not change accordingly, causing the value read by the application program to be inconsistent with the actual variable value.

4) Memory-mapped hardware registers usually also need to add volatile description, because each read and write to it may have a different meaning;

  Suppose a device is to be initialized, and a certain register of this device is 0xff800000. for(i=0;i< 10;i++) *output = i; The previous cycle is nonsense for half a day, which has no effect on the final result, because in the end only the output pointer is assigned to 9, and the hardware IO port is omitted Repeated reading operation.

This is the most basic problem that distinguishes C programmers from embedded system programmers: Embedded system programmers often deal with hardware, interrupts, RTOS, etc., all of which require the use of volatile variables. Failure to understand volatile content will bring disaster.

3. Volatile issues and summary

Several common interview questions for volatile:

1) Can a parameter be either const or volatile?

        Yes, such as 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) Can a pointer be volatile?

  Yes, when a middle service subroutine modifies a pointer to a buffer.

4. What is wrong with the following function?

int  square(volatile int*ptr)

{

    return * ptr * * ptr;

}

The purpose of this program is to return the square of the value pointed to by the 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;

}

 

Since the value of *ptr may be changed unexpectedly, a and b may be different. As a result, this code may not return the square value you expected! The correct code is as follows:

long square(volatile int*ptr)

{

    int a;

    a = *ptr;

    return a * a;

}

Note: Frequent use of volatile is likely to increase code size and reduce performance, so use volatile reasonably.

to sum up:

The volatile keyword is a type modifier, and the type variable declared with it can be changed by some factors unknown to the compiler. Volatile reminds the compiler that the variables defined later may change at any time, so every time the compiled program needs to store or read this variable, it will directly read the data from the variable address. 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, there will be inconsistencies. Therefore, when encountering a variable declared by this keyword, the compiler no longer optimizes the code that accesses the variable, thereby providing stable access to a special address.

 

If you want to better improve your programming ability, learn C language and C++ programming! Overtaking in a curve, one step faster!
[ C language C++ learning penguin circle ], share (source code, project actual combat video, project notes, basic introductory tutorial)
welcome partners who change careers and learn programming, use more information to learn and grow faster than you think!

Programming learning books:

 

Programming learning video:

 

Guess you like

Origin blog.csdn.net/Hsuesh/article/details/112523206