Why is a point-to-volatile pointer, like "volatile int * p", useful?
Asked Answered
P

3

97

volatile is to tell the compiler not to optimize the reference, so that every read/write does not use the value stored in register but does a real memory access. I can understand it is useful for some ordinary variable, but don't understand how volatile affects a pointer.

volatile int *p = some_addr;
int a = *p; // CPU always has to load the address, then does a memory access anyway, right?

What is the difference if it has been declared as int *p = some_addr?

Planospore answered 29/3, 2012 at 23:43 Comment(0)
B
176

A pointer of the form

volatile int* p;

is a pointer to an int that the compiler will treat as volatile. This means that the compiler will assume that it is possible for the variable that p is pointing at to have changed even if there is nothing in the source code to suggest that this might occur. For example, if I set p to point to a regular integer, then every time I read or write *p , the compiler is aware that the value may have changed unexpectedly.

There is one more use case for a volatile int*: If you declare an int as volatile, then you should not point at it with a regular int*. For example, this is a bad idea:

volatile int myVolatileInt;
int* ptr = &myVolatileInt; // Bad idea!

The reason for this is that the C compiler no longer remembers that the variable pointed at by ptr is volatile, so it might cache the value of *ptr in a register incorrectly. In fact, in C++, the above code is an error. Instead, you should write:

volatile int myVolatileInt;
volatile int* ptr = &myVolatileInt; // Much better!

Now, the compiler remembers that ptr points at a volatile int, so it won't (or shouldn't!) try to optimize accesses through *ptr.

One last detail - the pointer you discussed is a pointer to a volatile int. You can also do this:

int* volatile ptr;

This says that the pointer itself is volatile, which means that the compiler shouldn't try to cache the pointer in memory or try to optimize the pointer value because the pointer itself might get reassigned by something else (hardware, etc.) You can combine these together if you'd like to get this beast:

volatile int* volatile ptr;

This says that both the pointer and the pointee could get changed unexpectedly.The compiler can't optimize the pointer itself, and it can't optimize what's being pointed at.

Bos answered 29/3, 2012 at 23:47 Comment(17)
I think it's an error in C as well, but C compilers are less prone to complain about type mismatches.Bookkeeper
Thanks. So it has no difference whether there is "volatile" for my example, right? But if there is another statemenet "int b = *p" following, it does make a difference, right? Specifically, "b" may be initialized using a register storeing "*p" instead of doing a real memory reference.Planospore
@SetTimer- The C spec is sufficiently general that ideas like "caches," "registers," and "memory references" don't exist. The compiler will probably not hold the variable in a register, but it could in theory be smart enough to notice that the int being pointed at is not marked volatile and thus would compile the code identically with and without volatile.Bos
@templatetypedef: I doubt that's valid. Even if the pointed-to object was not declared volatile, one might want to access it as a volatile object overlaid with the non-volatile object, and as far as I can tell C's type compatibility rules allow such usage.Ronn
@R..- Sorry, to clarify - at a language level, you can definitely assign the pointer. However, the compiler might be smart enough to realize that doing so has no actual impact on the program semantics, and could just treat the pointer as non-volatile. It would have to be a very smart compiler to do this, though. Or am I incorrect about this?Bos
@Bos I am still confused. Say you have the code "int *p = &x; a = *p; do_something_else; b = *p;" I cannot imagine "b" would be assigned using a value that has been read for "a = *p", because that would be so wrong if the value of x has been changed somehow. My point is that even there is no volatile, I should expect "b = *P" will lead to a real read using the address of x rather than an old value stored in a register.Planospore
@SetTimer- That really depends on what do_something_else is. If the compiler could be completely convinced that do_something_else never changed the value of x, then it could definitely read the value of *p out of a register if it wanted. I doubt that for any reasonable code and most compilers that this would actually happen, but in theory it would be possible. Does that make sense?Bos
The C standard does not say that "the compiler will assume that it is possible for the variable that p is pointing at to have changed even if there is nothing in the source code to suggest that this might occur". It says that accesses to volatile objects can't be optimized away. If the compiler knows a pointer to volatile points to a non-volatile object then it can optimize away access. See my answer to Requirements for behavior of pointer-to-volatile pointing to non-volatile object.Mufinella
@philipxy: You are wrong. There is a defect report which clarifies how volatile is to be treated and that the wording in the standard is missleading. Luckily according to the report, most - if not all - implementations do it correctly and as intended.Threephase
@olaf That's great. I wouldn't exactly say I was wrong, since I correctly described what the standard says, as you confirm. Of course, what a program is supposed to do is per the standard as amended by a defect report of the right status. As I say in the linked answer, the standard did not seem to reflect the intent. Do you happen to know which report and its status?Mufinella
@philipxy: Well, if you read the report carefully, you might notice it states the wording in the standard is not what was meant, that the C++ standard is more what was intended by the commitee and that all major implementations implement what was intended, not what is written. So, I very well think you are wrong in terms of intention and practice (which is more relevant here than the theory.Threephase
@olaf Defect Report Summary for C11 Version 1.10 Date: April 2016 DR 476 volatile semantics for lvalues 04/2016 OpenMufinella
@philipxy: What the Standard should say is that the appropriate semantics for volatile will vary depending upon the target platform and intended application field. On many platforms, having a volatile read behave with acquire semantics and a volatile write behave with release semantics would be appropriate. On some platforms where reads can trigger hardware actions, they should have release as well as acquire semantics; likewise on some platforms where a write can trigger a stall until an I/O operation completes, they should have acquire as well as release semantics.Schock
@philipxy: On a platform that have no means of raising signals or doing anything else unusual, volatile need not do anything for objects other than automatic objects in functions that use setjmp, and counting as a "side effect" for purposes of preventing loop elision. The Standard should give implementors the authority to decide what's appropriate, but make clear that quality implementations will generally do more than the minimum required by the Standard [a note that should be applied in many other places as well].Schock
About the "Bad idea!" example, if I understand the C11 standard correctly, §6.5.16.1 under constraints states that discarding qualifiers is not allowed in this case. Both recent clang and gcc warn about this, but unfortunately allow it (with default options).Scandura
In "is a pointer to an int that the compiler will treat ..." I needed a moment to clearly understand that "that" refers to the int, not to the pointer. Anyway, great explanation!Rawdon
I have an other thought/question concerning the "beast" as you named it. If I declare a volatile pointer does it make a difference then if the value pointed to is volatile or not? In my opinion the value pointed to must not be cached in this case even if it is not explicitely marked volatile. Because if the path to the value can change at any time, the value can change at any time too. Yes that's quite theoretical but nevertheless interestint :-)Rawdon
P
10

This code volatile int *p = some_addr declares a pointer to a volatile int. The pointer itself is not volatile.

In the unlikely event that you needed the pointer to be volatile as well as the int, you would need to use:

volatile int * volatile p;

I can't think of a situation where you would need to use that.

Pledget answered 29/3, 2012 at 23:58 Comment(1)
Example: I'm using 'volatile uint8_t* volatile pData' in ISR code that modifies the pointer and the data it points to. The pointer is set by the main code, and both pointer and data are read later.Carvel
M
4

On the usefulness of volatile: This is needed, if you need to check memory, which is modified by hardware like a serial interface controller. It has its application in the world of embedded systems, where you work very close to the hardware without any OS in-between.

Michel answered 22/5, 2019 at 9:24 Comment(1)
Or if you are developing the OS.Benzedrine

© 2022 - 2024 — McMap. All rights reserved.