ccastingvolatile

How to cast volatile typedef struct to non-volatile typedef struct as argument of the function


I'm writing a C library for STM32 and ran into a problem.

I have typedef struct:

typedef struct foo {
    // some struct elements
} foo;

Volatile variable with foo type:

volatile foo bar;

And function with foo-typed argument:

int foobar(foo* baz) {
    // some operations with baz
    return 0;
}

When I'm trying to call foobar(&bar);, I get an error: error: invalid conversion from ‘volatile foo*’ to ‘foo*’ [-fpermissive]

Will it work if I cast volatile foo* to foo* ( foobar((foo*)&bar);)?

I tried to cast volatile foo* to foo* but I don't know if it will work without bugs.


Solution

  • It is not only cast. If you need to cast this way it means that your code has a issue.

    If you want to use a function which does not take a volatile parameter with a volatile object it means that this object needs to be treated as not *side effects prone. Your function does not have to assume that something not visible to the compiler can change it and apply optimizations which are not possible for volatile objects. The most important is that all accesses to the volatile object have to be applied to its permanent storage place.

    If you have this problem then:

    1. You overuse the volatile keyword and your volatile object does not have to be volatile (as you want to use it as non volatile). You should rethink your program data types.

    2. You can declare the function as taking volatile - but it will prevent many possible optimizations.

    3. You can have different functions handling volatile and non volatile data.

    Will it work if I cast volatile foo* to foo* ( foobar((foo*)&bar);)?

    No. It will only silence the warnings but it can be very dangerous.

    struct  x
    {
        bool dangerous;
        float data;
    };
    
    int dangerous(struct x *ptr)
    {
        if(!ptr -> dangerous)
        {
            /* do something */
            if(!ptr -> dangerous) never_call_if_dangerous_is_true();
        }
    }
    
    int dangerous_volatile(volatile struct x *ptr)
    {
        if(!ptr -> dangerous)
        {
            /* do something */
            if(!ptr -> dangerous) never_call_if_dangerous_is_true();
        }
    }
    

    and resulting code:

    dangerous:
            cmp     BYTE PTR [rdi], 0
            jne     .L2
            xor     eax, eax
            jmp     never_call_if_dangerous_is_true
    .L2:
            ret
    dangerous_volatile:
            mov     al, BYTE PTR [rdi]
            test    al, al
            jne     .L5
            mov     al, BYTE PTR [rdi]
            test    al, al
            jne     .L5
            jmp     never_call_if_dangerous_is_true
    .L5:
            ret
    

    in non volatile version skips the second check. If something (signal, interrupt) changes the dangerous member it will call the function - killing someone for example