Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C中,如果B是易失性的,那么表达式(void)(B=1)是否应为B_C_Variable Assignment_C99_Volatile - Fatal编程技术网

在C中,如果B是易失性的,那么表达式(void)(B=1)是否应为B

在C中,如果B是易失性的,那么表达式(void)(B=1)是否应为B,c,variable-assignment,c99,volatile,C,Variable Assignment,C99,Volatile,我为几个嵌入式平台的编译器工作。一位用户最近抱怨我们的一个编译器有以下行为。给定如下代码: extern volatile int MY_REGISTER; void Test(void) { (void) (MY_REGISTER = 1); } 编译器生成以下内容(在伪汇编程序中): 也就是说,它不仅会写入我的_寄存器,而且会在之后将其读回。由于性能原因,额外的负荷使他心烦意乱。我解释说这是因为根据标准“赋值表达式具有赋值后左操作数的值[…]” 奇怪的是,移除铸件以使其无效会改变

我为几个嵌入式平台的编译器工作。一位用户最近抱怨我们的一个编译器有以下行为。给定如下代码:

extern volatile int MY_REGISTER;

void Test(void)
{
    (void) (MY_REGISTER = 1);
}
编译器生成以下内容(在伪汇编程序中):

也就是说,它不仅会写入我的_寄存器,而且会在之后将其读回。由于性能原因,额外的负荷使他心烦意乱。我解释说这是因为根据标准“赋值表达式具有赋值后左操作数的值[…]”

奇怪的是,移除铸件以使其无效会改变行为:载荷消失。用户很高兴,但我只是感到困惑

所以我也在GCC的几个版本(3.3和4.4)中检查了这一点。在这里,编译器永远不会生成加载,即使显式使用了该值,例如

int TestTwo(void)
{
    return (MY_REGISTER = 1);
}
变成

TestTwo:
    move regA, 1
    store regA, MY_REGISTER
    move returnValue, 1
    return

是否有人对标准的正确解释有看法?是否应该进行回读?如果值被使用或强制转换为无效,则添加只读是否正确或有用?

标准中的语言没有说明如何读取volatile变量,仅说明赋值表达式的值,a)由C语义定义,而不是由变量的内容定义,b)此处未使用,所以不需要计算。

回读似乎更接近标准(特别是考虑到读取volatile变量可能会导致与写入的变量不同的值),但我很确定这不是大多数使用volatile的代码所期望的,尤其是在读或写一个可变变量会触发其他一些影响的情况下

volatile通常没有很好的定义--“什么构成对 已定义实现的限定类型。”

编辑:如果我必须做一个编译器,我想如果变量没有被使用,我不会读回它,如果被使用,我会重新读取它,但是会有一个警告。那么,是否应该使用作废的铸件

(void) v;
当然应该是一个,考虑到这一点,我没有任何理由

(void) v = exp;
不可能。但在任何情况下,我都会给出一个警告,解释如何获得其他效果


顺便说一句,如果你在一个编译器上工作,你可能会有人与C委员会联系,填写一份正式的缺陷报告会给你带来一个有约束力的解释(嗯,有风险的DR被归类为“非缺陷”,而没有任何关于他们想要什么的提示…

标准中的相关段落是这样的

赋值运算符存储一个值 在左边指定的对象中 操作数。赋值表达式具有 后的左操作数的值 赋值,但不是左值。 赋值表达式的类型 是左操作数的类型,除非 左操作数具有限定类型, 在这种情况下,它是不合格的 左键类型的版本 操作数。更新左操作数存储值的副作用应 发生在上一个序列点和下一个序列点之间

因此,这显然会在“左操作数的值”和存储值的更新之间产生差异。还要注意,返回值不是左值(因此表达式返回中没有对变量的引用),并且所有限定符都将丢失

因此,我认为gcc在返回它有意存储的值时做了正确的事情

编辑:

即将发布的标准计划通过添加脚注来澄清:

允许执行 读取对象以确定值 但即使在 对象具有易失性限定类型

编辑2:

实际上,还有一段关于表达式语句的内容可能会说明这一点:

表达式中的表达式 语句的计算结果为空 表达式的副作用。\footnote{例如具有副作用的赋值和函数调用}

由于这意味着这样的语句不需要返回值的效果,因此这强烈地表明,只有在使用值的情况下,才可以从变量加载值


总而言之,当您的客户看到变量被加载时,他确实感到不安。如果您对该行为的解释过于宽泛,则该行为可能符合标准,但它显然处于可接受的边缘。

有点像是
volatile
,不过是分配给变量的值和变量的值(从您通过读取它得到的意义上讲)不一定是同一件事。因此,由于标准中规定了变量的值,因此并不清楚这是指分配的值,与读取变量无关。你很可能有一个易变的int,它的读数总是0,但是写入它会让扬声器发出那么多次的嘟嘟声。那么赋值后的“变量值”是什么?耸耸肩。@Steve真的。仔细阅读了本标准后,我认为没有具体说明。它说的是“左操作数的值”,但没有完全说明它是什么。@SteveJessop英文描述显然有缺陷。在C语言中,表达式是一个右值OTOH,这一事实非常清楚。注意,这是C++中的一个LV值。那么,我在吉姆的回答中的例子是什么?为什么左操作数的值是赋值的,而不是通过读取它总是得到的值?我并不是说一定要有负担,只是我认为根本不清楚不应该有负担。@Steve,我们对此完全一致。请参阅我的编辑:“作为一个无效表达式评估其副作用”——但读取可变变量也可能是“副作用”的一个例子。所以我不认为这是一个拉伸加载它,但它是一个
(void) v = exp;