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.
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:
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.
You can declare the function as taking volatile
- but it will prevent many possible optimizations.
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