2017-07-18 97 views
4

我看了this很好太宽了问题,遇到一些UB我以前都不知道。有没有规则可以发现UB?

UB我不时看到的主要原因是在两个序列点之间改变两次变量。诸如:x = x++z = y++ + ++y;。读取在两个序列点之间两次更改变量是UB帮助我了解这些情况下的根本原因。

但是,像负位移位这样的事情呢? (int x = 8 << -1)有没有一个规则可以解释或者我应该记住这是一个独特的UB可能性?

我看了here和部分整数溢出我发现带负片的位移是写的,但我不明白他们为什么相关。当int被移位太多时,会引起溢出,但IMO发生负移位仅仅是UB,并且问题不在于“超出边缘”的位...

也看了这里,没有回答我的问题:

对每个操作数执行整数升级。结果的类型是提升的左操作数的类型。如果右操作数的值为负或者大于或等于提升的左操作数的宽度,则行为未定义

所以我的问题是:

  1. 具体而言,位移位与底片考虑整数溢出,如果是这样,为什么?
  2. 如果不是,它是更大现象的一部分吗?
  3. 是否存在(其他)不能归类于一个基本原因下的独特个案?
+3

负移位未定义,移位过长(对于N位整数类型移位N位或更多位)也是如此。该标准如此说。你必须知道它是这样说的。是的,有很多情况,将它们分组会很棘手。 C11标准的附录J.2在第557-571页上记录了未定义的行为(每个结束页面只有几行,因此它多于14页)。定期阅读以了解未定义的内容。没有;我还没有记住它。 –

+0

@JonathanLeffler这是一个令人印象深刻的名单,thx!尽管我希望有更容易记住的东西:) – CIsForCookies

+1

回答你的部分 - *我希望更容易记住的东西*我的一般经验法则是 - 任何似乎改变不同实现(目标,平台等)行为的东西是一个红旗“点”UB。然后我确认清单。这很有意义,因为标准是在抽象机器上定义的,而不是任何实现。因此触摸实现的方面必须保持未定义。警惕实施定义的行为。 –

回答

1

具体来说,是位移与底片考虑整数溢出和如果是这样,为什么?

这不是,因为任何数量的移位0都不会溢出,但它仍然是未定义的行为,将值0移动一个负值。 (我假设你可以认为它是整数溢出,如果你首先将移位量重新解释为一个无符号整数,此时它会很大并且肯定超出允许的范围,并且如果解释为如果移位的值不为零,则乘以2的乘方肯定会溢出)。

简而言之,负偏移产生未定义的行为,因为语言标准说明了这一点。

如果不是,它是一个更大的现象的一部分?

John Regehr在a blog post中给出了UB的一些大类。按无效金额移位在“其他UB”类别中...

是否存在(其他)不能归入一个潜在原因下的独特个案?

是的,看到上面的帖子。其中(这些直接从博客文章中解除):

  • 减去指向未指向同一数组对象或超出同一数组对象的指针(6.5.6)。
  • 对象的存储值不是由允许类型的左值访问的(6.5)
  • 非空源文件不会以换行符结尾,换行符不是以反斜杠字符开头或结尾部分预处理令牌或注释(5.1.1.2)

您可能会以某种方式对这些和其他示例进行分类,但这取决于您如何执行此操作。

特别是,上面的最后一个例子(关于源文件没有以换行符结尾)显示了一些规则是多么的随意。

+0

现在你已经有了这个内容的答案,我提供了从我的答案中删除你不错的链接,为了不承担你的功劳。你想要我吗? – Yunnosch

+0

@Yunnosch不,没关系,但谢谢你的提问。 – davmac

1

(编译自评的答案,包括我)

一个很好的出发点,发现实际的未定义行为(UB)是这些引用由Jonathan Leffler

是的,有很多情况下,分组将是棘手的。 C11标准的附录J.2在第557-571页上记录了未定义的行为(每个结束页面只有几行,因此它多于14页)。

引用一篇相关文章,其中介绍了UB的类型,用于识别的工具和包含UB的列表;长(作者的意图),完成(davmac的cortesy):
的东西 “memorizable” https://blog.regehr.org/archives/1520

两种方法:

  1. by Ajay Brahmakshatriya,重点不可避免的平台依赖性:

    我的一般经验法则是 - 任何似乎会改变不同实现(目标,平台等)行为的行为都是“现货”的红旗UB

  2. by Yunnosch,重点问题,以平衡标准化和优化:

    如果它很可能是努力使硬件供应商同意这,或否则很难明确界定,并允许一定空间优化的实现,那么它可能是UB。

可悲的是,所有这些 “规则” 并不容易申请。 检查实际标准是不方便的。这两个经验法则是基于相当一些必需的经验;你或者需要设计一些编译器和/或处理器,或者由于它们之间的差异而遭受了很大的影响。

所以实际的答案“有没有一种简单的方法来发现UB?” 可能是简单的“号”

+0

我会考虑将此标记为社区答案,因为这很大一部分不是你的话。如果你这样做,请删除顶部的括号注释。 –

0

x<<y与本案y负,也有一些平台将处理类似z=x<<y与微等同于:

unsigned temp = x; 
unsigned count=y; 
while(count--) 
    temp<<=1; 
z=temp; 

如果y是负的,这个循环可能会遇到很长的时间;如果它是在微码级别处理的(我认为一些Transputer芯片就是这种方式),它可能在几分钟内禁用中断,这可能会干扰系统的其他方面。

在大多数平台上它会花费什么,做作的场景之外,对于编译器,以保证x<<y不会有针对xy以后产生一个可能,无意义值的任何值的任何副作用;事实上,编译器可以更轻松地生成没有副作用的代码,而不需要执行其他任何操作。不幸的是,一些编译器作者认为,他们应该寻找“巧妙”的方式来利用“不能”消极的事实,引发任意坏的后果,而不考虑其实际上是否有用,或许错误地认为“聪明“和”愚蠢“是反义词。

+0

还有一些平台,其中只有足够的'y'位连接到移位单元以支持有意义的移位范围(即,如果'y'被屏蔽为'x'位的大小)。 –

+0

@TobySpeight:通常情况下,大的y值的'x << y'或者表现为x <<(y-1)<< 1'或者'x <<(y&numbits)',但是重要的是除了产生可能无意义的价值之外,它们都不会产生副作用。 – supercat