2017-10-16 60 views
28

C11§6.5.7第5段:是“-1 >> 5;” C中未指定的行为?

E1 >> E2结果是E1右移E2比特位置。如果 E1具有无符号类型或者如果E1具有带符号类型和 非负值,则结果的值是E1/2*^E2的商的整数部分。 如果E1具有签名类型和负值 值,则结果值是实现定义的。

但是,该viva64参考文件说:

int B; 
B = -1 >> 5; // unspecified behavior 

我跑GCC此代码,它总是给输出-1

所以,标准说的是“如果E1有签署类型和负值,所得到的值是实现定义的”,但该文件说的是-1>>5;不确定的行为

那么,在C中是-1>>5;未指定的行为?哪个是对的?

+17

如果您正在尝试编写可移植代码,那么实现定义和未指定之间的区别不是很重要,因此代码检查器会以类似方式处理它们。 – Barmar

+1

它是实现定义的。 – chux

+0

@chux那么,那个文件是不正确的? – rsp

回答

33

两者都是正确的。实现定义的行为是特定类型的未指定行为。

引述的the C standard 3.4.1节定义“实现定义的”:

实现定义

未指定的行为,其中每个执行文件如何选择由

示例实现定义的行为的一个示例是t当有符号整数右移时,他传播高位 。

从第3.4.4节定义 “未指定的行为”:

未指定的行为

使用未指定的值,或者其他行为,其中该 国际标准提供了两种或更多的可能性,并施加 没有进一步的要求,在任何情况下选择

示例未指定行为的示例是评估函数参数的顺序。

至于GCC,你总会得到相同的答案,因为操作是实现定义的。它实现了通过符号扩展

负数右移从GCC documentation

符号整型的一些位操作的结果(C90 6.3,C99和C11 6.5)。

位运算符作用于包括 两者的符号和值的位值,其中符号位是最高值值比特以上立即考虑 的表示。 签字>>通过符号扩展作用于 负数。

作为一个扩展的C语言,GCC不使用C99和C11给出 纬度唯一治疗签订<<作为 未定义的某些方面。但是,-fsanitize=shift(和-fsanitize=undefined)将 诊断此类情况。他们也被诊断,在需要不断表达 的地方。

+1

这不完全正确:“实现定义的行为是一种特定类型的未指定行为”。如果行为是实现定义的,则标准指定实现必须定义并记录它,所以它不是未指定的。未指定的行为适用于实现可以自由选择但不必记录行为或使其一致的情况。 –

+7

@R除“实现定义的行为”的定义实际上使用确切的词语“未指定的行为”。我想要求文件不被认为是强加于“选择的进一步要求”。 – aschepler

+1

我会解释你引用的“实现定义的行为”的定义,它不包括“未指定的行为”,即含义为“对于未指定的行为,但也对此要求”。毕竟,“未指定的行为”的定义包括“对所选择的行为没有进一步的要求”,但是实现定义的行为的确要求实现记录选择,因此它不符合“未指定行为”的定义, –

12

“未指定的行为”和“实现定义”并不矛盾。这仅仅意味着C标准没有具体说明需要发生什么,而且各种实现可以做他们认为“正确”的事情。

在一个编译器上多次运行并获得相同结果仅意味着特定编译器是一致的。您可能会在不同的编译器上得到不同的结果。

+1

两个术语都不是另一个的子集。如果一个动作调用“未指定”行为,那么实现需要从一组有限的选择中进行选择(例如,'x()+ y()'必须像完全评估x()一样,然后评估y()或完全y()然后评估x()。那些是唯一的两个选择)。如果某个动作调用“实现定义”行为,则需要实现文档记录特定行为,但只要记录它们就可以做任何他们喜欢的任何事情。 – supercat

相关问题