当前位置: 首页 > 知识库问答 >
问题:

涉及具有易失性变量的表达式的琐碎语句的正确行为?

终安和
2023-03-14

考虑以下陈述

volatile int a = 7;
a;   // statement A
volatile int* b = &a;
*b;  // statement B
volatile int& c = a;
c;   // statement C

现在,我一直在试图在标准中找到一点,告诉我当遇到这些语句时编译器是如何工作的。我所能找到的只是A(可能还有C)给了我一个左值,B也一样:

标识符是一个id表达式,前提是它已被适当声明(第7条)。[..]
[…]结果是由标识符表示的实体。如果实体是函数、变量或数据成员,则结果为左值,否则为prvalue
[…]

一元*运算符执行间接寻址:应用它的表达式应是指向对象类型的指针,或指向函数类型的指针,结果是一个左值,表示表达式指向的对象或函数。

我在clang 3.2-11和g 4.7.3中尝试了这个,第一个在C 11模式下产生了三次读取,在C 03模式下产生了零读取(输出三个警告),而g只产生了前两个,明确警告我第三个不会产生。

很明显,哪种类型的值来自表达式,来自标准中引用的行,但是:
根据C标准,哪种语句(A、B、C)应该从易失性实体产生读取?

共有2个答案

万嘉熙
2023-03-14

这是gcc文件7.1,何时访问易失性C对象?在这里是相关的,我引述(我接下来的重点):

C标准与C标准在处理挥发性物体方面有所不同。它没有具体说明什么构成挥发性访问,只是说C在挥发性方面的行为应该与C类似

在空上下文中访问对象时,C和C语言规范不同:

并提供了这个例子:

volatile int *src = somevalue;
*src;

接着说:

C标准指定此类表达式不进行左值到右值的转换,并且取消引用的对象的类型可能不完整。C标准没有明确指定导致访问的是左值到右值的转换。

应参考标准草案第5.3.1节一元运算符第1段,其中规定:

一元*运算符执行间接寻址:应用它的表达式应是指向对象类型的指针,或指向函数类型的指针,结果是一个左值,表示表达式指向的对象或函数。[...]

关于参考文献:

当使用对volatile的引用时,G不会将等效表达式视为对volatile的访问,而是发出警告,表示没有访问volatile。这样做的理由是,否则就很难确定易失性访问发生在哪里,也不可能忽略返回易失性引用的函数的返回值。同样,如果希望强制读取,请将引用转换为右值。

因此,gcc似乎选择以不同的方式处理对volatile的引用,为了强制读取,您需要强制转换为右值,例如:

static_cast<volatile int>( c ) ;

它从第5.2.9节Static cast生成一个prvalue,并由此进行左值到右值的转换:

表达式static_cast(v)的结果是将表达式v转换为类型T的结果。如果T是左值引用类型或函数类型的右值引用,则结果为左值;如果T是对象类型的右值引用,则结果为xvalue;否则,结果为prvalue。

更新

C 11标准草案增加了5表达式第11段,其中规定:

在某些情况下,表达式只会因其副作用而出现。这样的表达式称为废弃值表达式。表达式将被求值,其值将被丢弃。数组到指针(4.2)和函数到指针(4.3)的标准转换不适用。当且仅当表达式是volatile限定类型的左值且为下列之一时,才会应用左值到右值的转换(4.1):

并包括:

-id表达式(5.1.1),

这对我来说似乎是模棱两可的,因为关于a;c;部分5.1.1 p8说它是一个左值,它对我来说并不明显,它涵盖了这种情况,但乔纳森发现DR 1054说它确实涵盖了这种情况。

郏经纬
2023-03-14

关于“隐式取消引用”的G警告来自gcc/cp/cvt. c中的代码,该代码故意不通过引用加载值:

    /* Don't load the value if this is an implicit dereference, or if
       the type needs to be handled by ctors/dtors.  */
    else if (is_volatile && is_reference)

G故意这么做,因为如手册所述(何时访问易失性C对象?)该标准不清楚什么构成对易失性限定对象的访问。如上所述,您需要强制进行左值到右值的转换,以强制从volatile加载。

Clang在C 03模式下发出警告,表示类似的解释:

a.cc:4:3: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
  a;   // statement A
  ^
a.cc:6:3: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
  *b;  // statement B
  ^~
a.cc:8:3: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
  c;   // statement C
  ^
3 warnings generated.

对于C 03,G行为和GCC手册似乎是正确的,但C 11与C 03之间存在差异,这是由DR 1054介绍的(这也解释了为什么在C)3和C 11模式下,Clang的行为不同)。5[expr]p10定义了一个废弃的值表达式,并表示对于volatile,左值到右值的转换应用于id表达式,例如语句a和C。左值到右值的转换规范(4.1[conv.lval])表示结果是glvalue的值,它构成了volatile的访问。根据5p10,所有三条语句都应该是access,因此G对语句C的处理需要更新以符合C 11。我把它报告为http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59314

 类似资料:
  • 根据JLS 17规范第17.3节: 例如,在下面的(断开的)代码片段中,假设这是。“完成”是一个非易失性布尔字段: 编译器可以自由读取此字段。只执行一次,并在每次执行循环时重用缓存的值。这意味着循环永远不会终止,即使另一个线程更改了这个值。完成 我尝试模拟以下示例:两个线程同时访问同一个布尔变量,第一个线程使用while循环中的共享布尔值,第二个线程更新布尔值。 1、代码无线程。第一个线程内的sl

  • 问题内容: 我想删除以AAA开头的Jenkins作业列表(例如) 当我用显式字符串做它很好用 但是,当我尝试从Jenkins Build属性获取正则表达式时,我失败了,并且if没有被调用… 运行它时,我会得到所有作业的列表:在工作区中构建/ var / lib / jenkins / jobs / Bulk_Delete_Job / workspace 但是什么也不会被删除/不会打印“ TEST”

  • 编程语言最强大的功能就是操作变量。变量就是一个有值的代号。 2.1 赋值语句 赋值语句的作用是创建一个新的变量,并且赋值给这个变量: >>> message = 'And now for something completely different'>>> n = 17 >>> pi = 3.141592653589793 上面就是三个赋值语句的例子。第一个是把一个字符串复制给名叫message的

  • 我正在对科特林进行修补,我正试图让我的头脑了解科特林中可为空的变量是如何工作的。这里我有一段代码,它执行布尔检查,以查看车辆是否超载。实现是处理可空变量的好方法还是有更优雅的方法? 非常谢谢!

  • 我正在用php和ajax创建一个消息传递站点。 获取对话有问题。 问题是,每当两个用户在他们之间聊天时,就会有两个id不同的行; A和B正在聊天,彼此只写了4条消息 消息数据库是这样的 我的目标是用这个代码获取记录 $页面所有者是登录的用户; 使用这种方法,我可以进行两次相同的对话 a 代码在页面上给了我两次对话,我只想得到一个结果; 我的整个php代码是这样的

  • 编程语言最强大的特性之一,是操作变量的能力。变量是指向某个值的名称。 赋值语句 赋值语句(assignment statement)会新建变量,并为这个变量赋值。 >>> message = 'And now for something completely different' >>> n = 17 >>> pi = 3.141592653589793 这个例子进行了三次赋值。 第一次将一个字