1

I know the volatile modifier has been discussed a lot. Please don't yell at me. I know why it is used, but I am trying to properly use it in my multi threaded C program using Visual Studio 2008 and 2010. Been having some trouble with it on Windows 10. Does it matter where I place the modifier in a simple declaration? For example, both of these build successfully but I was wondering if there was any difference in the meaning to the compiler:

// difference if any between these two?
volatile char _initialized = 0;
char volatile _initialized = 0;

What about a more complex declaration? Given this structure:

typedef struct _KEY_HANDLE
{
    ULONG handle;
    void *ptr;
} KEY_HANDLE;
...
// difference if any between these three
volatile KEY_HANDLE * key_handles = NULL;
KEY_HANDLE volatile * key_handles = NULL;
KEY_HANDLE * volatile key_handles = NULL;
...
key_handles = (PVOID) malloc(bufsz);
...

Thanks.

Neil Weicher
  • 2,370
  • 6
  • 34
  • 56
  • 5
    You understand that `volatile` has nothing to do with atomicity or cache coherence, and is therefore pretty much useless for anything to do with threading? – Fred Larson Dec 10 '18 at 20:51
  • 1
    Right, it is to force the compiler to reference the memory location each time the variable is referenced, and not to cache it in a register. Unless I am terribly mistaken. – Neil Weicher Dec 10 '18 at 20:59
  • @FredLarson Where does he reference atomic usage (maybe he edited the question)? He can wrap a mutex around critical sections where its used, depending on his application requirements. I have always seen it as a the first word on the left, so the most natural is" volatile KEY_HANDLE * key_handles = NULL;". I suspect all 3 of them would work but i am not 100% sure. – Bwebb Dec 10 '18 at 21:01
  • 1
    @Bwebb: See https://stackoverflow.com/a/2485177/10077 . As it explains, `volatile` itself is insufficient and the proper techniques do not require `volatile` to work. So I stand by my comment, except that I neglected to mention reordering. – Fred Larson Dec 10 '18 at 21:11
  • 1
    See c11 draft standard n1570: *6.7 Declarations* and *6.7.6 Declarators*. Both `volatile T` and `T volatile` are valid, `T volatile` is more consistent, because for `T *volatile` the `volatile` *must* be on the right. (The same rules apply for `const`, `_Atomic` [and `restrict` for pointers]) – EOF Dec 10 '18 at 21:26
  • @FredLarson You are mistaken: `volatile` is not useless. It is very dangerous for people who just assume that C/C++ `volatile` is somewhat the closest equivalent of Java `volatile`, or that making objects shared by different threads `volatile` will fix threading issues. "_nothing to do with atomicity_" atomicity of reads and writes (not other operations) is guaranteed by the CPU and ABI for word size variables that are correctly aligned "_or cache coherence_" which is guaranteed by all modern CPU – curiousguy Dec 11 '18 at 21:18
  • (...) **`volatile` makes C/C++ a portable assembly language** BUT for the objects accessed by `volatile` qualified lvalues which is easy to misuse as you have a high level language mixed with a low level language and they ignore each other (`volatile` qualified pointers types are especially problematic as the won't behave as Java `volatile` references). It means that you get whatever the CPU gives in term of MT guarantees. (And this is true on any C/C++ compiler.) – curiousguy Dec 11 '18 at 21:28

2 Answers2

2

https://barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword

According to this link, both of the top two chars are the same.

Similarly the two KEY_HANDLE pointers are pointers to a volatile KEY_HANDLE, while the third one is a volatile pointer to a non-volatile HEY_HANDLE.

volatile KEY_HANDLE * key_handles = NULL;  //pointer to a volatile KEY_HANDLE
KEY_HANDLE volatile * key_handles = NULL;  //pointer to a volatile KEY_HANDLE
KEY_HANDLE * volatile key_handles = NULL;  //volatile pointer to a non-volatile KEY_HANDLE
Bwebb
  • 675
  • 4
  • 14
1

For a base type:

volatile unit32_t *number; //The number pointed at is volatile
uint32_t *volatile number; //The pointer is volatile
volatile uint32_t number; //The number is volatile

For a struct:

volatile struct_t *struct_p; //The members of the struct pointed at are volatile
struct_t *volatile struct_p; //The pointer is volatile
volatile struct_t struct_o; //The members of the struct are volatile

These can be combined of course:

volatile uint32_t *volatile number; //Both the pointer and the number are volatile

Hopefully now you see the pattern of what the volatile modifier does depending on the position.

I have not used volatile like this:

uint32_t volatile number; //I assume it will mean that the number is volatile
uint32_t volatile *number; //I assume it will mean that the number is volatile

The thumb rule is that if the volatile modifier is behind the * (pointer) it modifies the pointer and not the pointed variable.

For the meaning of volatile it self it basically means that the compiler cannot assume anything about what data might be stored in the variable and thus cannot optimize away any operation on the variable.

Bregell
  • 139
  • 10