C: Volatile Arrays in C
Asked Answered
F

3

34

The volatile keyword is used in C to prevent the compiler performing certain optimizations, amongst other subtle changes, on a variable.

For example;

volatile int my_int = 0;

creates an integer. In some situations it may prevent the following optimization:

while(my_int == 0); // Loop until my_int != 0

Optimize to:

while(1); // Loop infinity.

This is useful for situations including those frequently encountered in embedded systems, such as a situation where modification to a variable may be made by an interrupt function call. There are many other examples of where this technique is useful. my_int may be a flag which is modified by such a function. (This is just a toy model.)

However, consider the case where the data modified by the function is an array. The data may be pointed to by a pointer.

unsigned char* my_data = new unsigned char[256];

In this case, considering that my_data is a global variable in this specific situation of this question[1], is the volatile keyword redundant, or is it still required?

[1] It may not matter.

If the answer is that the volatile keyword is required, what it the correct syntax for use?

For example, volatile unsigned char* my_data, I assume declares that the pointer itself is volatile, and not the data it points to.

Finally, is there a difference between the use in C and C++?

Fabriane answered 15/8, 2014 at 18:42 Comment(6)
That particular optimization with a conditional loop being optimized to an infinite loop will probably not happen anyway, even without my_int being declared as volatile. Reason being that the compiler must know that the value is zero from the start of the loop, and that it will never be changed in the loop.Bootee
@JoachimPileborg Good point, although this was just to demonstrate the principle for anyone who was unfamiliar with the context. I have changed it.Fabriane
Strictly, preventing optimization is the means by which volatile works, rather than its purpose.Kink
A more likely scenario is where the condition is false, the loop is removed altogether.Kink
your question explains what volatile keyword does!Er
In modern C++ a more intuitive way could be achieved by using std::array. This would result in std::array<volatile char, N> data; making it obvious that char is volatile. N will usually be some number, e.g. 5: std::array<volatile char, 5> fiveChars;. Of course iterators to specific elements can be volatile as well: volatile auto itr = std::begin(fiveChars);Stuff
A
43

Yes, volatile is required, and the right declaration is:

volatile unsigned char *my_data;

This declares my_data to be a pointer to volatile unsigned char.

To make the pointer itself volatile, you'd need this instead:

unsigned char *volatile my_data;

And of course, both the pointer and the pointed-to data may be volatile:

volatile unsigned char *volatile my_data;

There's no difference between C and C++.

Apicella answered 15/8, 2014 at 18:45 Comment(4)
Thanks, I guess this is exactly the same in syntax as the const identifier then?Fabriane
You can also use unsigned char volatile *my_data;, which is equivalent to volatile unsigned char *my_data;. If you use this alternate ordering then you can read the declaration from right to left as, "my_data is a pointer to a volatile unsigned char". And that right to left reading also works for the volatile pointer declaration. And this also works for const.Actinopod
Note that new unsigned char[256] allocates non-volatile characters, the compiler may use that information even if you happen to point to those non-volatile characters with a pointer-to-volatile. The full code should be volatile unsigned char* my_data = new volatile unsigned char[256];Etruscan
Is unsigned char volatile * my_data interpreted as volatile unsigned char * my_data or unsigned char *volatile my_data according to the standard?Bothy
M
2

Filipe Gonçalves have already provided an answer, but I would like to elaborate a bit.

Basically, the volatile keyword is required whenever you would like your program to really consider the variable value, without trying to be smart. It does not matter what kind of variable you have, it's just that if compiler can't see any changes to a variable between the assignment and the call, it will probably optimize it (depending on your settings of course).

From compiler's POV, the sequence of events is "assign 0 to Var"->"do unrelated stuff, but surely not touching Var"->"check Var value" It does not matter where and how variable Var got declared, and the code will be optimized unless volatile keyword is used.

Margalit answered 17/10, 2018 at 1:28 Comment(2)
Such assumptions about the compiler's likely behaviour are dangerous to rely on in real code; compilers get smarter as time goes on. These days link-time optimization is a thingEtruscan
@Etruscan Interesting, thanks! I am aware of LTO, but it did not occur to me that the thing can optimize conditions the same way the compiler can so that volatile is still required. The paragraph about external functions was meant to provide an example of when optimization does not happen, but I can see how it can be misleading, so I deleted it.Margalit
O
1

C declarations are to be read form right to left with qualifiers to the left.

So volatile char * x means 'x' is a pointer to a volatile char. while char volatile * x means that 'x' is a volatile pointer to a char

The difference in pactice is that when accessing x[0], in the first case, the value at x[n] needs to be read on each appearance in the source code (e.g. twice in a = x[n]*x[n])

In the second case, x iteself needs to be read each time and then the address of x[n] needs to be calculated again and then x[n] is to be read.

For this reason, volatile char volatile *x is redundant.

However, declaring an array or its elements as volatile will prevent the compiler from buffering them across multiple reads. It does not ensure that a function that reads a volatile array won't read first half of the array while it contains old values and (after an external change) the second half while it contains updated values, leading to an inconsistent result. Using volatile on arrays or structs (or sets of individual variables) is only advisable when the individual elements are independent of each other. If changing one of them affects the others (or their use/meaning), then volatile won't help at all and a mutex should be used for the whole access (preventing the array content to be changed during processing).

Osher answered 8/7, 2020 at 13:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.