The volatile keyword in C language

Volatile is a type specifier, just like the familiar const, it is designed to modify variables that are accessed and modified by different threads; volatile is used as an instruction keyword to ensure that this instruction will not be affected by Compiler optimization is omitted, and the value is required to be read directly every time.

A volatile variable means that the variable may be changed unexpectedly, so that the compiler will not assume the value of this variable.

effect

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 respectively represent different operations and will produce four different actions, but the compiler will optimize the above four statements, thinking that only XBYTE[2]=0x58 (that is, ignore the first three statements, Only one machine code is generated). If you type volatile, the compiler will compile one by one and generate the corresponding machine code (four codes are generated).
To be precise, the optimizer must carefully re-read the value of this variable every time it uses this variable, instead of using the backup 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 that will be accessed in an interrupt service subroutine (Non-automatic variables)

3) Variables shared by several tasks in multithreaded applications

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.

Assuming that the interviewee answers this question correctly (well, I wonder if this is the case), I will dig a little deeper to see if this guy really understands the importance of volatile.

1) Can a parameter be either const or volatile? Explain why.

2) Can a pointer be volatile? Explain why.

3) The following function is used to calculate the square of a certain integer. Can it achieve the expected design goal? If not, try to answer the question:

int square(volatile int *ptr)

{

    return ((*ptr) * (*ptr));

}

Here is the answer:

1) Yes. An example is the 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 an interrupt service routine modifies a pointer to a buffer.

3) This code is a prank. The purpose of this code is to return the square of the value pointed to by the pointer ptr. However, since 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 change between the two value statements, 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;

}

Talk about personal understanding:

The key lies in two places:

⒈Compiler optimization (please help me to see the understanding below)

In this thread, when reading a variable, in order to improve the access speed, the compiler sometimes reads the variable into a register when optimizing; when the variable value is retrieved later, the value is directly retrieved 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, in order to maintain consistency

When the value of the variable changes 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

When the value of the register changes due to other threads, etc., the value of the original variable will not change, resulting in inconsistency between the value read by the application and the actual variable value

Give an inaccurate example:

When paying salaries, the accountant calls the employee to register their bank card number every time; the accountant used the previously registered bank card number to save trouble and did not register immediately; just one employee’s bank card was lost and the bank card number was reported missing; As a result, the employee cannot receive wages

Employee-original variable address

Bank card number-the backup of the original variable in the register

⒉ Under what circumstances will it appear

1) Hardware registers of parallel devices

2) Non-automatic variables that will be accessed in an interrupt service subroutine (Non-automatic variables)

3) Variables shared by several tasks in multithreaded applications

Supplement: Volatile should be interpreted as "direct access to the original memory address" is more appropriate, the interpretation of "volatile" is simply misleading;

"Variability" is caused by external factors, such as multithreading, interrupts, etc. It is not because the variable modified with volatile is "volatile". If there is no external cause, even if the volatile definition is used, it will not change;

After using volatile definition, in fact, this variable will not change due to external reasons, so you can use it with confidence; Let’s see if the previous explanation (changeable) is misleading.

The volatile keyword is a type modifier. The type variable declared with it can be changed by some factors unknown to the compiler, such as the operating system, hardware, or other threads. When encountering the variable declared by this keyword, the compiler will no longer optimize the code that accesses the variable, so that it can provide stable access to the special address.

Examples of using this keyword are as follows:

volatile int vint;

When the value of a variable declared by volatile is required, the system always reads data from the memory where it is located, even if the previous instruction has just read data from it. And the data read is saved immediately.

E.g:

volatile int i=10;

int a=i;

//...

//Other code, did not explicitly tell the compiler, has operated on i

int b=i;

Volatile points out that i may change at any time, and must be read from the address of i every time it is used. Therefore, the assembly code generated by the compiler will read data from the address of i again and place 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 data read last time in b. Instead of reading from i again. In this way, if i is a register variable or represents a port data, it is prone to errors, so volatile can guarantee stable access to special addresses.

Note that in vc6, code optimization is not performed in general debugging mode, so the effect of this keyword is not visible. Next, insert assembly code to test whether there is a volatile keyword and its impact on the final code of the program:

First, use classwizard to build a win32 console project, insert a voltest.cpp file, and enter the following code:

#include<stdio.h>

 

void main(int argc,char *argv[])

 

{

     

    int i = 10;

     

    int a = i;

     

    printf("i=%d",a);

    //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道

    __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 the release version mode, and the output results are as follows:

i = 10

i = 10

The output result clearly shows that in the 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 statement of i to see what changes:

#include<stdio.h>

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, 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 it.

For example, in a multi-threaded program, multiple programs can manipulate this variable in the shared memory

Your own program cannot determine when this variable will change

For another example, it corresponds to a certain state of an external device. When the external device is operated, the system changes the value of this variable through the driver and interrupt events, but your program does not know it.

For a volatile variable, every time the system uses it, it directly extracts it from the corresponding memory, instead of using the original value in the cache to adapt to the unknown when it will change. The processing of this variable will not be optimized-obviously because its value may change at any time.


Guess you like

Origin blog.csdn.net/weixin_43704402/article/details/114131627