简体   繁体   中英

Volatile in struct causing warning in macro

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).

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. Eg 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 .

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, },
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM