【问题标题】:Modifying a const variable with the volatile keyword使用 volatile 关键字修改 const 变量
【发布时间】:2013-08-06 13:08:10
【问题描述】:

我正在回答question 并制作了这个测试程序。

#include <stdio.h>
int main()
{
    volatile const int v = 5;
    int * a = &v;
    *a =4;
    printf("%d\n", v);
    return 0;
}

如果没有 volatile 关键字,代码会优化(使用 -O3 apple clang 4.2 编译)var 的变化,它按预期工作并且 const 变量被正确修改。

我想知道一个更有经验的 C 开发人员是否知道标准的一部分是否表明这是不安全的或 UB。

更新: @EricPostpischil 给了我这个标准引用

根据 C 2011 (N1570) 6.7.3 6,程序不得修改其自己使用 const 限定类型定义的对象:“如果尝试通过使用具有非 const 限定类型的左值,行为未定义。”外部代理可以修改具有 volatile 限定类型的对象,根据 6.7.3 7:“具有 volatile 限定类型的对象可能会以实现未知的方式进行修改或具有其他未知的副作用

我的程序违反了第一条规则,但我认为第二条规则可能会使程序免于第一条规则。

更新 2:

具有 volatile 限定类型的对象可能会以实现未知的方式被修改或具有其他未知的副作用。因此,任何引用此类对象的表达式都应严格按照抽象机的规则进行评估,如 5.1.2.3 中所述。此外,在每个序列点,最后存储在对象中的值应与抽象机规定的值一致,除非被前面提到的未知因素修改。134) 构成对具有 volatile 限定类型的对象的访问是实现-定义。

如果您查看此引用,您会看到必须根据某些规则评估 var,我还没有通读 5.1.2.3 部分的所有内容,但我相信这可能会对这个问题有所帮助。

【问题讨论】:

  • 我确定这不会编译没有错误。
  • @Devolus 1 警告,除非我在那里留下错字
  • @Devolus 在 c99 标准中,甚至有一个几乎等同于这个的例子,如果我记得哪个,它被称为“可能违反规则”甚至不是“违反规则”规则是,我帮你查一下,但我很抱歉。但我也很困惑,想,这不可能没有任何错误。我试过了......我很想知道...... MSVC2010编译器->没有警告,没有错误clang ->没有错误,1个关于“不合格类型的访问”之类的警告。 gcc 也有 0W/0E。
  • 为什么你会认为 6.7.3.7 对 6.7.3.6 有任何影响?通过指针进行的赋值不是“外部代理”或“以实现未知的方式修改”。

标签: c constants volatile undefined-behavior


【解决方案1】:

这是不安全的,因为不能保证在其他编译器中使用相同的行为。因此,您的代码依赖于编译器,甚至可能依赖于编译器开关。这就是为什么这是一个坏主意。

【讨论】:

  • 我在某些方面同意你的观点,但我也只是用 g++ 对其进行了测试,它表现出我的预期行为,希望有人能找到它不起作用的编译器
  • 这不是重点。关键是有人可以在规范中构建一个没有这样做或不允许这样做的编译器。那么你的代码就会崩溃,也许是在遥远的未来。
  • 是的,但我的问题的重点是,我相信即使引用了标准,也有待讨论标准的含义,我并不是说你错了,只是你有可能是跨度>
  • 我没有看到证据表明 6.7.3.7 是违反 6.7.3.6 的邀请
  • 好吧,也许你不知道,但我认为有可能,我还找到了标准草案,我正在查看它
【解决方案2】:

这一行:

int * a = &v;

是违反约束的。编译器必须生成诊断消息,并且可能会拒绝该程序。如果编译器仍然生成可执行文件,则该可执行文件具有完全未定义的行为(即 C 标准根本不再涵盖该程序)。

违反的约束是volatileconst 不能被隐式转换掉。

为了符合 C 标准,指针的指向类型必须与被指向的对象具有相同或更强的限定符,例如:

int const volatile *a = &v;

之后你会发现*a = 4;这行会导致编译错误。


可能的尝试可能是:

int *a = (int *)&v;

这一行必须编译,但它会导致通过*a 读取或写入未定义的行为。未定义的行为由 C11 6.7.3/6 指定(C99 和 C89 有类似的文本):

如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。如果尝试通过使用具有非 volatile 限定类型的左值来引用具有 volatile 限定类型定义的对象,则行为未定义。

【讨论】:

  • 如果原始变量和指针都是volatile 限定的,则承认存在它一无所知的硬件功能的高质量编译器应该假定程序员想要尝试导致的行为将指示值存储到指示位置。该标准不要求编译器编写者承认未知硬件特性的存在,并且依赖编译器编写者在向程序员公开这些特性时运用常识。在使用 EEPROM 之类的东西时,它实际上很有用...
  • ...拥有被限定为 const volatile 的存储空间,但随后具有执行必要操作的函数来写入它(在许多情况下,这将包括将所需值写入所需的地址)。我宁愿对一个编译器感到恼火,它强迫任何体操除了将指针投射到uint8_t volatile*uint32_t volatile* [取决于硬件] 以强制它在序列中的适当点生成必要的写操作。
猜你喜欢
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-18
  • 2015-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多