简体   繁体   中英

volatile struct vs. typecast

I have the following declarations in my code:

h file:

typedef struct {
    bool qualified : 1;
    bool running : 1;
} calibration_state_t;

calibration_state_t get_calibration_state();

cpp file:

volatile calibration_state_t calibration_state = {false ,false};

The function

calibration_state_t get_calibration_state() {
    return *(calibration_state_t *)&calibration_state;
}

compiles. However if I replace the return statement with

return (calibration_state_t)calibration_state;

It fails with

dcf77.cpp: In function ‘DCF77_Frequency_Control::calibration_state_t DCF77_Frequency_Control::get_calibration_state()’:
dcf77.cpp:2923:37: error: no matching function for call to ‘DCF77_Frequency_Control::calibration_state_t::calibration_state_t(volatile DCF77_Frequency_Control::calibration_state_t&)’
dcf77.h:204:7: note: candidates are: DCF77_Frequency_Control::calibration_state_t::calibration_state_t()
dcf77.h:204:7: note:                 DCF77_Frequency_Control::calibration_state_t::calibration_state_t(const DCF77_Frequency_Control::calibration_state_t&)

The compiler is avr-gcc but I suspect this does not matter. Why does the compiler fail to compile the type cast? How would I get to the desired return value in a clean way?

Your code that uses a cast has undefined behavior (§7.1.6.1 [dcl.type.cv]/p6):

If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined.

*(calibration_state_t *)&calibration_state is a glvalue of type calibration_state_t , a non-volatile-qualified type, and is being used to refer to calibration_state , an object defined with a volatile-qualified type. Undefined behavior results.

Relying on undefined behavior to get the semantics you want is incredibly dangerous. While the compiler is unlikely to actually conjure nasal demons or blow your legs off (though it is allowed to), an optimizing compiler may legally assume from the undefined behavior that get_calibration_state will never be called, and that any code path containing it is unreachable, and generate code accordingly. This kind of optimizations depending on undefined behavior can and do happen.

In reference binding, volatile is like const - you can't bind a const object to a non- const reference, and you can't bind a volatile object to a non- volatile reference. Give your class a copy constructor that takes a const volatile & .

What is the desired behavior exactly? Must ordering be preserved, for example? If something else sets qualified and then running , is it okay to get the old value of qualified but the new value of running ?

Because the structure is volatile, operations on it are part of the visible behavior of the program. That is, this:

calibration_state_t get_calibration_state()
{
    calibration_state_t ret;
    ret.qualified = calibration_state.qualified;
    ret.running = calibration_state.running;
    return ret;
}

Is not the same as:

calibration_state_t get_calibration_state()
{
    calibration_state_t ret;
    ret.running = calibration_state.running;
    ret.qualified = calibration_state.qualified;
    return ret;
}

So, you have to code what you want. How can the compiler know what behavior you want? You got some behavior by lying to the compiler, but I doubt it's the behavior you want.

For me it looks that:

return *(calibration_state_t *)&calibration_state;

explicitly removes the volatile specifier, so the implicitly defined copy constructor:

calibration_state_t(const calibration_state_t&)

is executed.

When you don't cast the calibration_state instance first, then compiler can not call the copy constructor ( volatile remains).

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