Search code examples
cpointersvolatile

Why does declaring an unsigned char "Volatile" make it incompatible with a "non-volatile" unsigned char?


I am trying to use a volatile unsigned char as a pointer array argument to a function. I created the function:

static void queue_to_serial(unsigned char *queue)

I'm using "static" for the function, because it is only to be used in the current source file. Then I call the function and supply the address:

queue_to_serial(&queue);

And I get an error:

Error[Pe167]: argument of type "unsigned char volatile (*)[8]" is incompatible with parameter of 
type "unsigned char *"

The array is declared as a volatile but from what I know that means it shall not be optimized out as it can change asynchronously

static volatile unsigned char queue[MAX_NUM_CONNECTED_VALVES];  

The array is also declared to be static because I would like to retain the value assigned to the variable. Unless the array is updating at the exact time the function is run which should not happen this should in my mind work because the memory used by a volatile unsigned char is the same as an unsigned char. How does making a variable volatile change it? Or does declaring it as a pointer cause it to occupy memory at a different location? Thank you


Solution

  • When you write a function such as

    int foo(char* x);
    

    the compiler will generate some code for this function, which does not know anything about what kind of parameters you are going to cast to char* and pass to it. So it will assume x is a pointer to some regular memory that can be be optimized in usual way, and for example something like:

    int foo(char* x) 
    {
        a = x[0];
        b = x[0];
        return a + b;
    }
    

    could be optimized to something like return 2 * x[0]; - that is performing a single read from x[0].

    Now consider

    int bar(volatile char* x) 
    {
        a = x[0];
        b = x[0];
        return a + b;
    }
    

    This time the compiler must generate two reads from x.

    Why does it matter? Think that x is pointing to some hardware counter register, which will increment each time it is being read. So assuming it's initial value is 0, the foo(x) call will return 0 and the value of the counter will be 1. bar(x) will return 1 and the value of the counter will be 2.