16

I presume that the following will give me 10 volatile ints

volatile int foo[10];

However, I don't think the following will do the same thing.

volatile int* foo;
foo = malloc(sizeof(int)*10);

Please correct me if I am wrong about this and how I can have a volatile array of items using malloc.

Thanks.

Mark
  • 3,177
  • 4
  • 26
  • 37

5 Answers5

12
int volatile * foo;

read from right to left "foo is a pointer to a volatile int"

so whatever int you access through foo, the int will be volatile.

P.S.

int * volatile foo; // "foo is a volatile pointer to an int"

!=

volatile int * foo; // foo is a pointer to an int, volatile

Meaning foo is volatile. The second case is really just a leftover of the general right-to-left rule. The lesson to be learned is get in the habit of using

char const * foo;

instead of the more common

const char * foo;

If you want more complicated things like "pointer to function returning pointer to int" to make any sense.

P.S., and this is a biggy (and the main reason I'm adding an answer):

I note that you included "multithreading" as a tag. Do you realize that volatile does little/nothing of good with respect to multithreading?

tony
  • 3,737
  • 1
  • 17
  • 18
  • 4
    volatile must be used when sharing state variables between threads; particularly for concurrent lock free algorithms where you use busy waits. E.g. thread 1 will spin on this instruction: while(barrier); until thread 2 sets barrier=false. If volatile was not used, then code could be deadlocked if the compiler decided to read the barrier value from it's local register instead of memory. – Mark Feb 23 '10 at 08:21
  • 1
    No - volatile does not insert a memory barrier for all compilers. See http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/ – Adrian Cox Feb 23 '10 at 10:00
  • yes, there is the rare case where volatile does some good. But a spin lock requires a memory barrier, or at least the data being read after 'barrier == true' needs a memory barrier. On MSVC volatile implies barriers, but that's non-standard. – tony Feb 23 '10 at 13:00
  • I like the observation about how to write qualifiers into declerations. Going to start using it. –  May 12 '12 at 20:25
  • Do you mean `!=` instead of `==`? I think `volatile int * foo;` has the same meaning as `int volatile * foo;` – TimC Dec 21 '12 at 10:56
  • Volatile means that the value MUST be read from main memory. While this does not insert a Barrier, current hardware MUST use a barrier to manage cache. This is how HW overcomes false sharing. So while the compiler may not be required to insert a memory barrier, you are in fact insisting on one being present by using the volatile keyword. – Dan Feb 23 '13 at 00:17
  • yep, meant !=. Wow, only 10 years late. Sorry. – tony Apr 21 '21 at 19:43
6
volatile int* foo;

is the way to go. The volatile type qualifier works just like the const type qualifier. If you wanted a pointer to a constant array of integer you would write:

const int* foo;

whereas

int* const foo;

is a constant pointer to an integer that can itself be changed. volatile works the same way.

MtnViewJohn
  • 683
  • 4
  • 9
3

Yes, that will work. There is nothing different about the actual memory that is volatile. It is just a way to tell the compiler how to interact with that memory.

Kyle Lutz
  • 7,966
  • 2
  • 20
  • 23
2

I think the second declares the pointer to be volatile, not what it points to. To get that, I think it should be

int * volatile foo;

This syntax is acceptable to gcc, but I'm having trouble convincing myself that it does anything different.

I found a difference with gcc -O3 (full optimization). For this (silly) test code:

volatile int  v [10];
int * volatile p;

int main (void)
{
        v [3] = p [2];
        p [3] = v [2];
        return 0;
}

With volatile, and omitting (x86) instructions which don't change:

    movl    p, %eax
    movl    8(%eax), %eax
    movl    %eax, v+12
    movl    p, %edx
    movl    v+8, %eax
    movl    %eax, 12(%edx)

Without volatile, it skips reloading p:

    movl    p, %eax
    movl    8(%eax), %edx    ; different since p being preserved
    movl    %edx, v+12
    ; 'p' not reloaded here
    movl    v+8, %edx
    movl    %edx, 12(%eax)   ; p reused

After many more science experiments trying to find a difference, I conclude there is no difference. volatile turns off all optimizations related to the variable which would reuse a subsequently set value. At least with x86 gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33). :-)

wallyk
  • 56,922
  • 16
  • 83
  • 148
1

Thanks very much to wallyk, I was able to devise some code use his method to generate some assembly to prove to myself the difference between the different pointer methods.

using the code: and compiling with -03

int main (void)
{
        while(p[2]);
        return 0;
}

when p is simply declared as pointer, we get stuck in a loop that is impossible to get out of. Note that if this were a multithreaded program and a different thread wrote p[2] = 0, then the program would break out of the while loop and terminate normally.

int * p;
============
LCFI1:
        movq    _p(%rip), %rax  
        movl    8(%rax), %eax   
        testl   %eax, %eax
        jne     L6              
        xorl    %eax, %eax
        leave
        ret
L6:
        jmp     L6

notice that the only instruction for L6 is to goto L6.

==

when p is volatile pointer

int * volatile p;
==============
L3:
        movq    _p(%rip), %rax
        movl    8(%rax), %eax
        testl   %eax, %eax
        jne     L3
        xorl    %eax, %eax
        leave
        ret 

here, the pointer p gets reloaded each loop iteration and as a consequence the array item also gets reloaded. However, this would not be correct if we wanted an array of volatile integers as this would be possible:

int* volatile p;
..
..
int* j;
j = &p[2];
while(j);

and would result in the loop that would be impossible to terminate in a multithreaded program.

==

finally, this is the correct solution as tony nicely explained.

int volatile * p;
LCFI1:
        movq    _p(%rip), %rdx
        addq    $8, %rdx
        .align 4,0x90
L3:
        movl    (%rdx), %eax
        testl   %eax, %eax
        jne     L3
        leave
        ret 

In this case the the address of p[2] is kept in register value and not loaded from memory, but the value of p[2] is reloaded from memory on every loop cycle.

also note that

int volatile * p;
..
..
int* j;
j = &p[2];
while(j);

will generate a compile error.

Mark
  • 3,177
  • 4
  • 26
  • 37