未定义的行为错误:对成员变量的更改仅在某些上下文中可见
Undefined Behaviour Bug : Changes to a member variable visible only from some contexts
>我有一个结构,它相当于这个:
struct Interface
{
static inline TransmitData TXData{ nullptr, 0 };
//
static void TransmitContinue() noexcept
{
packet_t packet{ IN_Id };
//
if ( auto tx{ TXData.Consume( packet_t::Capacity() ) }; packet.Assign( tx ) )
{
// Point E
// A transmission is triggered here with the tx variable above.
}
}
static bool TransmitComplete() noexcept
{
TXData.PopFront( packet_t::Capacity() );
//
if ( TXData.Empty() )
{
// Point A
}
else
{
// Point B
TransmitContinue();
}
return true;
}
static bool Transmit( TransmitData data ) noexcept
{
if ( TXData.Empty() )
{
// Point C
TXData = data;
TransmitContinue();
return true;
}
else
{
// Point D
return false;
}
}
};
TransmitData::Empty 的实现如下:
ALWAYS_INLINE constexpr bool Empty() const noexcept
{
return ( Size() == 0 ) or ( Data() == nullptr );
}
TransmitData::Size 的实现如下:
ALWAYS_INLINE constexpr size_type Size() const noexcept
{
return m_Length;
}
TransmitData::P opFront 的实现如下(count 是要弹出的字节数(:
constexpr TransmitData & PopFront(size_type count = 1) noexcept
{
auto c{ std::min(Size(), count) };
m_Data += c;
m_Length -= c;
return *this;
}
TransmitData::Consumption的实现如下:
ALWAYS_INLINE constexpr TransmitData Consume( std::size_t index ) const noexcept
{
return { m_Data, std::min( index, Size() ) };
}
函数 "Transmit" 在 int main(( 中运行。
函数"TransmitComplete"从硬件中断运行。
"TransmitData"结构与std::string_view非常相似,具有一些附加功能。结构中包含的长度变量不是易失性的。
调用顺序如下:
- int main(( 调用 Transmit 函数
- 到达 C 点,将 TX 数据与参数数据一起分配。
- 到达 E 点,触发传输。
- 中断调用 TransmitComplete 函数 发送数据包后到达点A(TX数据为空(。在这个问题的情况下,这是正确的,TXData 实际上是在 Pop 函数之后是空的。
- 然后主发送更多数据
- 然后再次调用触发传输,这是问题所在。
尽管传输完成表示 TXData 在下一个调用中为空,但对传输的每个后续调用都表示 TXData 已满。 这很奇怪,因为:
- TXData 仅由"C 点"加载,这只发生一次
- 它只能通过"传输完成"删除
- 到达 A 点表明 TXData 是空的。这在再次调用传输之前发生。
- 对传输的第二次调用表示 TXData 已满(到达点 D(,没有加载更多数据。
存在问题的系统
这是在只有一个执行线程(主线程(的单核 ARM M0 应用程序上。中断函数始终在调用后续传输之前完成。
我正在使用 GCC 8.2。
当前工作理论
GCC 正在实例化计数器变量的两个实例。它没有将其优化为常量,因为 Empty 成员函数指示 TransmitComplete 和 Transmission 函数中长度变量的变化。
奇怪的是,从传输函数中对长度变量的更改对传输完成函数是可见的。但是,对 TransmitComplete 函数中的长度变量所做的更改对 Transmission 函数不可见。
注意如果我将 TransmitData 结构中的 m_Length 成员变量(类似于 string_view(更改为易失性,则代码按预期工作,没有问题。
上述内容的唯一更改是将传输数据成员m_Length从:
size_type m_Length{ 0u };
自:
size_type volatile m_Length{ 0u };
我不认为长度变量应该需要易失性,因为对变量的所有更改都是在代码中完成的,并且应该对编译器可见。
在长度成员上放置易失性会阻碍程序中其他地方的优化,我希望避免它,因为惩罚非常高。
问题
有谁知道为什么会发生此错误? 有谁知道上述症状是如何可能的? 我在做一些愚蠢的事情(可能(吗?
我认为问题是你的main
函数非常简单。编译器能够分析完整的控制流,并看到main
中的任何内容在第一次调用Transmit
时设置后m_length
无法更改。因此,它假设m_length
的值在整个执行时间内保持不变,因此以下所有m_length
与零的比较都是假的。
这就解释了为什么添加volatile
可以解决问题:它强制编译器在每次计算m_length
时重新访问。
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- 错误"Could not find Boost"(缺少:上下文标头)
- DCMTK 了解"DIMSE 没有有效的演示上下文 ID"错误
- MSVC 在不知道类型的情况下评估上下文(和错误)
- 关于 GetDIBits 和设备上下文的奇怪错误
- 自定义删除器,用于shared_ptr<>给出"无上下文错误"
- Linux/QT-错误创建SSL上下文(错误:140A90C4 ..)
- 分段错误将文本呈现到开罗上下文
- 在spirit parser Action中,在上下文参数上编译boost :: bind(成员函数)上的错误
- OpenGL 上下文创建期间未记录的错误
- 如何修复C#应用程序中调用C++DLL函数时的上下文加载错误
- 带有 QT 错误的 OpenGL:ASSERT:"QOpenGLFunctions::isInitialized(d_ptr)" 。无法创建 OpenGL 上下文
- 当任何子线程导致错误时,上下文切换到主线程
- 带有模板的矢量在打印上下文时在瓦尔格林德中给出错误
- C++上下文错误
- 渲染自定义模型时出现c++Directx 11设备上下文错误
- 此上下文标记中的错误可能会标记错误的位置
- 类声明错误:上下文信息不足,无法确定类型
- 激活上下文生成失败.VC运行时错误
- VS 2013 单元测试不起作用 =设置执行上下文时出现>错误