Role of the volatile keyword

Source: Internet
Author: User
The role of the volatile keyword volatile reminds the compiler that the variables defined after it may change at any time. Therefore, each time a compiled program needs to store or read this variable, data is read directly 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, 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.

Deep Exploration of C language void and void pointer
1. Overview
This article will describe the profound meaning of the void keyword and describe how to use the void and void pointer types.

2. Meaning of Void
Void literally means "no type", void * is "no type Pointer", and void * can point to any type of data.

Void has almost only "Comments" and restrictions on the role of the program, because no one will ever define a void variable, let's try to define:

Void;

An error occurs when compiling this line of statements, and the message "illegal use of type 'void'" is displayed '". However, even if the compilation of void a does not go wrong, it has no practical significance.

Void actually plays the following role:
(1) Restrictions on function return;
(2) Restrictions on function parameters.

We will describe the above two points in section 3.

As we all know, if the pointer P1 and P2 are of the same type, we can directly assign values to each other between P1 and P2. If P1 and P2 point to different data types, you must use the forced type conversion operator to convert the pointer type on the right of the value assignment operator to the pointer type on the left.

For example:
Float * P1;
Int * P2;
P1 = P2;

The p1 = P2 statement will cause compilation errors and the prompt "'=': cannot convert from 'int * 'to 'float *'" must be changed:
P1 = (float *) P2;

Void * is different. pointers of any type can be directly assigned to it without forced type conversion:
Void * P1;
Int * P2;
P1 = P2;

However, this does not mean that void * can also be assigned to other types of pointers without force type conversion. Because "No type" can tolerate "type", while "type" cannot tolerate "No type ". The principle is simple. We can say that "both men and women are people", but not "people are men" or "people are women ". The following statement compilation error:
Void * P1;
Int * P2;
P2 = p1;

The message "'=': cannot convert from 'void * 'to 'int *' is displayed *'".

3. Use of Void

The following describes the rules for using the void Keyword:
Rule 1 if the function does not return a value, the void type should be declared.

In C language, any function without a limited return value type will be processed by the compiler as the return integer value. However, many programmers mistakenly think it is of the void type. For example:
Add (int A, int B)
{
Return A + B;
}
Int main (INT argc, char * argv [])
{
Printf ("2 + 3 = % d", add (2, 3 ));
}

The program running result is output:
2 + 3 = 5
This indicates that the function without return values is indeed an int function.

Dr. Lin Rui mentioned in "High Quality C/C ++ programming": "The C ++ language has strict type security checks and does not allow the above situations (that is, the function does not add a type declaration) occurred ". However, the compiler does not necessarily think so. For example, in Visual C ++ 6.0, the compilation of the add function is error-free and has no warning and runs correctly, therefore, we cannot expect the compiler to perform strict type checks.

Therefore, in order to avoid confusion, when writing C/C ++ programs, you must specify the type of any function. If the function does not return a value, it must be declared as void. This is both a need for good program readability and a requirement for standardized programming. In addition, after adding the void type declaration, you can also use the "self-annotation" function of the Code. The "self-annotation" of the code means that the code can annotate itself.

Rule 2 If the function has no parameters, the parameter should be declared as void

Declare a function in C ++:
Int function (void)
{
Return 1;
}

The following call is invalid:
Function (2 );

In C ++, the function parameter void means that this function does not accept any parameters.

We compile in Turbo C 2.0:
# I nclude "stdio. H"
Fun ()
{
Return 1;
}
Main ()
{
Printf ("% d", fun (2 ));
Getchar ();
}

Compiling is correct and 1 is output. In C, parameters of any type can be transferred to a function without parameters. However, compiling the same code in C ++ compiler will cause an error. In C ++, no parameters can be transferred to a function without parameters. If an error occurs, the system prompts "'fun ': function does not take 1 Parameters ".

Therefore, whether in C or C ++, if the function does not accept any parameters, you must specify the parameter as void.

Rule 3 be careful when using the void pointer type

According to the ANSI (American National Standards Institute) standard, you cannot perform algorithm operations on the void pointer, that is, the following operations are invalid:
Void * pvoid;
Pvoid ++; // ANSI: Error
Pvoid + = 1; // ANSI: Error
// The ANSI standard determines this because it insists that the pointer to the algorithm operation must be determined to know the data type it points.
// Example:
Int * pint;
Pint ++; // ANSI: Correct

The result of pint ++ is to increase sizeof (INT ).

However, the well-known GNU (GNU's not UNIX abbreviation) does not recognize this as it specifies that the void * algorithm operation is consistent with char.

Therefore, the following statements are correct in the GNU Compiler:
Pvoid ++; // GNU: Correct
Pvoid + = 1; // GNU: Correct

The execution result of pvoid ++ is increased by 1.

In actual programming, to cater to ANSI standards and improve program portability, we can write code that implements the same function as below:
Void * pvoid;
(Char *) pvoid ++; // ANSI: Correct; GNU: Correct
(Char *) pvoid + = 1; // ANSI: error; GNU: Correct

There are some differences between GNU and ANSI. In general, GNU is more open than ANSI and provides support for more syntaxes. However, in actual design, we should try to cater to ANSI standards as much as possible.

Rule 4 if the function parameter can be a pointer of any type, the parameter should be declared as void *

Typical function prototypes for memory operation functions such as memcpy and memset are:
Void * memcpy (void * DEST, const void * SRC, size_t Len );
Void * memset (void * buffer, int C, size_t num );

In this way, any type of pointer can be passed into memcpy and memset, which truly reflects the significance of the memory operation function, because the object it operates on is only a piece of memory, regardless of the memory type. If the parameter types of memcpy and memset are not void * But char *, it is really strange! Such memcpy and memset are obviously not "pure, out of low-level interests" functions!

The following code is correctly executed:
// Example: memset accepts any type of pointer
Int intarray [100];
Memset (intarray, 0,100 * sizeof (INT); // clear intarray 0

// Example: memcpy accepts any type of pointer
Int intarray1 [100], intarray2 [100];
Memcpy (intarray1, intarray2, 100 * sizeof (INT); // copy intarray2 to intarray1

Interestingly, the memcpy and memset functions also return the void * type. how knowledgeable are the compilers of the standard library functions!

Rule 5 void cannot represent a real Variable

The following code tries to make void represent a real variable, so it is all wrong code:
Void A; // Error
Function (void a); // Error

Void represents an abstraction in which variables in the world are "typed". For example, a person is either a man or a woman.

The emergence of void is only for an abstract need. If you have understood the concept of "abstract base class" in object-oriented, it is easy to understand the void data type. Just as we cannot define an instance for an abstract base class, we cannot define a void (let's say that void is an abstract data type) variable.

Const usage

Const is a relatively new descriptor in C language. We call it a constant modifier, that is, its modifier.
The object is a constant (immutable ).

Let's take a look at how it should be used in syntax.

1. modify local variables in the function body.
Example:
Void func (){
Const int A = 0;
}

First, we ignore the word const, so a is an int-type local automatic variable,
We give it an initial value of 0.

Then look at Const.

As a type qualifier, const has the same status as Int.
Const int;
Int const;
Is equivalent. So here we must clearly understand who the const object is, A, and Int.
Yes. Const requires that the modified object be a constant, cannot be changed, cannot be assigned a value, cannot be used
The left value (L-value ).
This is also incorrect.
Const int;
A = 0;
This is a common usage method:
Const double Pi = 3.14;
If you attempt to assign values to or modify pi after the program, an error will occur.

Then let's look at a slightly complex example.
Const int * P;
Remove the const modifier first.
Note that the following two values are equivalent.
Int * P;
Int * P;
In fact, we want to say that * P is of the int type. Obviously, p is the pointer to int.
Likewise
Const int * P;
It is equivalent
Const int (* P );
Int const (* P );
That is, * P is a constant. That is to say, P points to a constant.
Therefore
P + = 8; // valid
* P = 3; // invalid. P points to a constant.

So how to declare itself as a constant pointer? The method is to keep const close to P as much as possible;
Int * const P;
Const has only P on the right. Obviously, it modifies P, indicating that P cannot be changed. Remove the const.
It can be seen that p is a pointer to an int form variable.
Therefore
P + = 8; // invalid
* P = 3; // valid

Let's look at a more complex example, which is a combination of the above two.
Const int * const P;
P itself is a constant, and the variable P points to is also a constant.
Therefore
P + = 8; // invalid
* P = 3; // invalid

Const is also used to modify constant static strings.
For example:
Const char * name = "David ";
If no const exists, we may intentionally or unintentionally write a statement like name [4] = 'X' later.
Causes a value to be assigned to the read-only memory area, and the program will terminate unexpectedly immediately. With const, this error occurs.
It can be checked immediately when the program is compiled, which is the benefit of Const. Make logical errors compile
Was found.

Const can also be used to modify arrays.
Const char s [] = "David ";
Similar to the above.

2. Modify parameters during function declaration
Let's look at an example.
Name
Memmove -- Copy byte string

Library
Standard C library (libc,-LC)

Synopsis
# I nclude <string. h>

Void *
Memmove (void * DST, const void * SRC, size_t Len );

This is a function in the standard library used to copy strings (memory) in bytes ).
Its first parameter is where to copy the string to (DEST). It is the destination and this memory area must be
Is writable.
The second parameter indicates the string to be copied. We only read the memory area.
.
From the perspective of this function, we can see that the SRC Pointer Points to memory
The stored data remains unchanged throughout the function execution process. So SRC points to a constant. So
Const must be used.
For example, we use it here.
Const char * s = "hello ";
Char Buf [100];
Memmove (BUF, s, 6); // It is better to use strcpy or memcpy here.

If we write in turn,
Memmove (S, Buf, 6 );
Then the compiler will certainly report an error. In fact, we often reverse the Parameter order of various functions. The fact is editing
The interpreter helped us a lot at this time. If the compiler does not report an error quietly, remove it from the function declaration.
Const), the program will crash during running.

It is also worth noting that in the function parameter declaration, const is generally used to declare the pointer rather than the variable itself.
For example, the above size_t Len does not need to change the Len value during function implementation.
Should Len be declared as a constant? Yes, you can. Let's analyze the advantages and disadvantages of doing so.
If const is added, the implementer of this function can prevent it from being repaired when implementing this function.
Modify the value (LEN) that does not need to be modified. This is good.
But for the user of this function,
1. This modifier is meaningless. We can pass a constant integer or a very integer over
The other party obtains a copy.
2. Exposed implementation. I don't need to know if you have modified the Len value when implementing this function.

Therefore, const is generally only used to modify pointers.

Let's look at a complicated example.
Int execv (const char * path, char * const argv []);
Focus on what argv stands for later.
If the const is removed, we can see that
Char * argv [];
Argv is an array. Each element of argv is a char * pointer.
If Const. is added, who is the const modifier? It is an array, argv [], meaning
The elements of this array are read-only. What is the element type of the array? Char * type
Needle. That is to say, the pointer is a constant, and it points to no data.
Therefore
Argv [1] = NULL; // invalid
Argv [0] [0] = 'a'; // valid

3. global variables.
Our principle is to use as few global variables as possible.
Our second rule is to use as many const as possible.
If a global variable is used only in this file, the usage is similar to the local variable of the function mentioned above.
What is the difference.
If it is to be shared among multiple files, it involves a storage type issue.

There are two methods.
1. Use extern
For example
/* File1.h */
Extern const double PI;
/* File1.c */
Const double Pi = 3.14;
Other variables that need to use pi, including file1.h
# I nclude "file1.h"
Or copy the statement.
The result is that after the entire program is linked, all the storage areas that need to use the PI variable are shared.

2. Use static and static External Storage Class
/* Constant. H */
Static const Pi = 3.14;
The *. c file that needs to use this variable must contain this header file.
The preceding static values must not be small. Otherwise, the variable is defined multiple times.
The result is that each *. c file containing constant. H has its own copy of the variable,
The variable is actually defined multiple times, occupying Multiple Buckets, but the static keyword is added.
The file definition conflict is solved.
The disadvantage is that the storage space is wasted, resulting in larger executable files after the link. But usually, this is small.
Several bytes of change, not a problem.
The advantage is that you don't have to worry about the file in which the variable is initialized.

Finally, let's talk about the role of Const.
The advantage of const is that it introduces the concept of constants so that we do not need to modify the memory that should not be modified. Direct
The function is to make more logical errors be detected during compilation. Therefore, we should try to use const as much as possible.
However, many people are not used to using it. What's more, it is completed after the entire program is compiled/debugged.
Const. It would be nice to add const to the declaration of the function. If you want to add const to global/local variables
...... So it's too late to make the Code look more beautiful.

Introduction to the structure (struct) and Union (Union) in C Language

Union)
1. Joint description and joint variable definition
Union is also a new data type, which is a special form of variable.
The description of union and the definition and structure of Union variables are very similar. The format is:
Union union Union name {
Data Type member name;
Data Type member name;
...
} Name of the federated variable;
These variables share the same memory location, saving different data types and variables of different lengths at different times.
The following example indicates a combination of a_bc:
Union a_bc {
Int I;
Char mm;
};
You can then define the federated variables with the specified federated variables.
For example, to define a federated variable named lgc using the preceding description, you can write it as follows:
Union a_bc lgc;
In the combined variable lgc, the integer I and the character mm share the same memory location.
When a union is described, the compiler automatically generates a variable whose length is the maximum variable length in the Union.
The method and structure for joint access to its members are the same. Similarly, federated variables can be defined as arrays or pointers, but when defined as pointers, they must also use the "->;" symbol. In this case, the Federated access member can be expressed:
Union name->; member name
In addition, a union can appear in a structure, and its members can also be structures.
For example:
Struct {
Int age;
Char * ADDR;
Union {
Int I;
Char * Ch;
} X;
} Y [10];
To access the union member I of X in the structure variable Y [1], you can write it:
Y [1]. X. I;
To access the first character of the string pointer ch that joins X in the structure variable Y [2], you can write it as follows:
* Y [2]. X. ch;
If it is written as "Y [2]. X. * Ch;", it is incorrect.

2. Differences between structure and Union
The structure and combination have the following differences:
1. the structure and union are composed of multiple members of different data types, but at any time, the Union transfer only stores one selected member, and all members of the structure exist.
2. assigning values to different members of the Union will be rewritten to other members. The original value of the members does not exist, but the assignment values to different members of the structure do not affect each other.
The following is an example of how to understand deep integration.
Example 4:
Main ()
{
Union {/* define a Union */
Int I;
Struct {/* define a structure in the Union */
Char first;
Char second;
} Half;
} Number;

Number. I = 0x4241;/* Union member assignment */
Printf ("% C/N", number. Half. First, mumber. Half. Second );
Number. Half. First = 'a';/* assign values to structure members in the Union */
Number. Half. Second = 'B ';
Printf ("% x/N", number. I );
Getch ();
}
Output result:
AB
6261
From the above example, we can see that after I is assigned a value, the lower eight bits are the values of first and second. After first and second are assigned a character, the ASCII code of these two characters will also be used as the low-eight-bit and high-eight-bit I.

Volatile keywords in C

A variable defined as volatile means that this variable may be unexpectedly changed, so that the compiler will not assume the value of this variable. Precisely, the optimizer must carefully re-read the value of this variable every time when using this variable, rather than using the backup stored in the register. The following are examples of volatile variables:
1). Hardware registers of parallel devices (for example, Status Registers)
2). Non-automatic variables that will be accessed in an interrupt service subroutine)
3) variables shared by several tasks in multi-threaded applications
People who cannot answer this question will not be hired. I think this is the most basic problem to distinguish between C programmers and embedded system programmers. Embedded System programmers often deal with hardware, interruptions, RTOS, and so on, all of which require volatile variables. If you do not know volatile content, it will lead to disasters.
If the subject correctly answers this question (well, I doubt this will happen), I will go a little deeper to see if this guy understands the full importance of volatile.
1) can a parameter be const or volatile? Explain why.
2) can a pointer be volatile? Explain why.
3) What are the following function errors:
Int square (volatile int * PTR)
{
Return * PTR ** PTR;
}
The answer is as follows:
1). Yes. One example is read-only status registers. It is volatile because it may be unexpectedly changed. It is const because the program should not try to modify it.
2). Yes. Although this is not very common. One example is when a service subroutine repairs a pointer to a buffer.
3) This code has a prank. The purpose of this Code is to return the pointer * PTR points to the square of the value. 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;
}
* The value of * PTR may be unexpectedly changed, so a and B may be different. As a result, this Code may not return the expected square value! The correct code is as follows:
Long square (volatile int * PTR)
{
Int;
A = * PTR;
Return a *;
}



The volatile keyword tells the compiler not to hold temporary copies of variables. It is generally used in multi-threaded programs to avoid copying a variable into a register when one of the threads operates on it. See the following scenarios:

Thread a copies the variable into the Register, and then enters the loop to repeatedly check whether the value of the Register meets certain conditions (it expects the B thread to change the value of the variable.
In this case, when the B thread changes the value of the variable, the changed value does not affect its value in the register. So thread a enters an endless loop.

Volatile is used in this case.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.