【问题标题】:volatile struct vs. typecastvolatile 结构与类型转换
【发布时间】:2014-09-02 19:14:00
【问题描述】:

我的代码中有以下声明:

h 文件:

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

calibration_state_t get_calibration_state();

cpp 文件:

volatile calibration_state_t calibration_state = {false ,false};

功能

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

编译。但是,如果我将 return 语句替换为

return (calibration_state_t)calibration_state;

失败了

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

编译器是 avr-gcc 但我怀疑这无关紧要。为什么编译器无法编译类型转换?如何以干净的方式获得所需的返回值?

【问题讨论】:

  • 您正在强制消除波动性。不要那样做。避免 c 风格的演员表,这隐藏了这个错误。在后一种情况下,C++ 正确地完成了它的工作。

标签: c++ gcc struct volatile


【解决方案1】:

您使用强制转换的代码具有未定义的行为(§7.1.6.1 [dcl.type.cv]/p6):

如果试图引用一个用 a 定义的对象 volatile 限定类型,通过使用带有 非 volatile 限定类型,程序行为未定义。

*(calibration_state_t *)&calibration_statecalibration_state_t 类型的泛左值,一种非易失性限定类型,用于引用 calibration_state,一个使用 volatile 限定类型定义的对象。未定义的行为结果。

依靠未定义的行为来获得你想要的语义是非常危险的。虽然编译器不太可能真正召唤出鼻恶魔或吹掉你的腿(尽管允许这样做),但优化编译器可能会从未定义的行为合法地假设 get_calibration_state 永远不会被调用,并且包含它的任何代码路径都是无法访问,并相应地生成代码。这种依赖于未定义行为的优化可以而且确实会发生。

在引用绑定中,volatile 就像 const - 您不能将 const 对象绑定到非 const 引用,也不能将 volatile 对象绑定到非volatile 参考。为您的类提供一个接受 const volatile & 的复制构造函数。

【讨论】:

    【解决方案2】:

    究竟期望的行为是什么?例如,必须保留订单吗?如果其他设置qualified 然后running,是否可以得到qualified 的旧值但running 的新值?

    因为结构是易变的,所以对它的操作是程序可见行为的一部分。也就是这个:

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

    不等于:

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

    所以,你必须编写你想要的代码。编译器如何知道你想要什么行为?通过对编译器撒谎,你得到了一些行为,但我怀疑这是你想要的行为。

    【讨论】:

    • 嗯,这是一台 8 位机器,每个标志占用一位。因此,通过撒谎,我使复制操作成为原子操作。两个值同时复制。
    • @UdoKlein 那么听起来你已经精确地编写了你想要的行为,需要破解的行为,因为它基于 C 代码中无法表示的特殊平台知识。
    • 是的,这是特殊的平台知识。有问题的代码仅用于调试目的。另外我的记忆力非常很紧。因此,我宁愿不逐位复制结构(仅剩下 32k 的 ~2k)。
    【解决方案3】:

    在我看来:

    return *(calibration_state_t *)&calibration_state;
    

    显式移除volatile 说明符,因此隐式定义的复制构造函数:

    calibration_state_t(const calibration_state_t&)
    

    被执行。

    当您不先转换calibration_state 实例时,编译器无法调用复制构造函数(volatile 仍然存在)。

    【讨论】:

      猜你喜欢
      • 2020-06-09
      • 2015-11-30
      • 1970-01-01
      • 2016-05-05
      • 1970-01-01
      • 2012-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多