I use the following type:
/* double_buffer.h */
typedef struct
{
uint8_t * active_buffer_p; //< Address of active buffer
uint8_t current_writing_index; //< Current writing index in active buffer
uint8_t buffer_1[BUFFER_SIZE]; //< First buffer
uint8_t buffer_2[BUFFER_SIZE]; //< Second buffer
} double_buffer_t;
#define DoubleBuffer_init(buffers) do { \
(buffers).active_buffer_p = (buffers).buffer_1; \
(buffers).current_writing_index = 0; \
} while(0)
In my code, I declare an array of double buffer, using the volatile keywoard (because the buffers can be updated/read asynchronously in interrupts and in functions):
static volatile double_buffer_t m_double_buffers[NB_DOUBLE_BUFFERS];
I then initialize those buffers individually:
DoubleBuffer_init(m_double_buffers[id]);
When I compile the software (gcc), I got the following warning:
error: assignment discards 'volatile' qualifier from pointer target type [-Werror=discarded-qualifiers]
28 | (buffers).active_buffer_p = (buffers).buffer_1; \
The reason why I have this warning is quite unclear to me, and I am not sure how to fix it.
Any help would be appreciated (I can update the question if something is not clear).
CodePudding user response:
You get this warning because you have a volatile object, and you create a non-volatile pointer to it.
This is bad as the compiler could access the volatile object without knowing that it is volatile. E.g. it could transform two reads into a single, it could change the order etc.
One way to fix it is to define active_buffer_p
to uint8_t volatile * active_buffer_p
.
CodePudding user response:
When you declare a struct
variable as volatile
, each member object gets volatile
qualified, as if you had written the struct like this:
typedef struct
{
uint8_t* volatile active_buffer_p;
volatile uint8_t current_writing_index;
volatile uint8_t buffer_1[10];
volatile uint8_t buffer_2[10];
} double_buffer_t;
That is, in case of a pointer member, the pointer itself turns volatile
. type* volatile
= the pointer's address may change at any moment. And not what it is supposed to point at. volatile type*
= the pointed-at data might change at any moment.
And therefore when you assign, buffer_1
"array decays" into volatile uint8_t*
and then you try to assign that to a pointer qualified as uint8_t* volatile
. The pointer types aren't compatible since they have different qualifiers.
The solution is to declare the pointer member volatile uint8_t* active_buffer_p;
. Then if the struct variable is declared volatile
, this becomes volatile uint8_t* volatile
(the pointer and what it points at may change at any moment). And we can always assign a "non qualified" pointer to one with more qualifiers, but not the other way around.
const
works exactly the same way.
As a side note, that init macro is just ugly and fills no purpose but obfuscation. Consider dropping it in favour of readable code:
static volatile double_buffer_t m_double_buffers[NB_DOUBLE_BUFFERS] =
{
[0] = { .active_buffer_p = m_double_buffers[0].buffer_1,
.current_writing_index = 0 },
[1] = { .active_buffer_p = m_double_buffers[1].buffer_1,
.current_writing_index = 0 },
[2] = { .active_buffer_p = m_double_buffers[2].buffer_1,
.current_writing_index = 0 },
};
Or 100% equivalent:
static volatile double_buffer_t m_double_buffers[NB_DOUBLE_BUFFERS] =
{
[0] = { .active_buffer_p = m_double_buffers[0].buffer_1, },
[1] = { .active_buffer_p = m_double_buffers[1].buffer_1, },
[2] = { .active_buffer_p = m_double_buffers[2].buffer_1, },
};