我看了this非常太宽泛的问题,遇到了一些我以前不知道的UB。
我不时看到的UB的主要原因是在两个序列点之间改变变量两次。例如:x = x++
或z = y++ + ++y;
。读到在两个序列点之间两次更改变量是UB帮我看看这些情况下的根本原因是什么。
但是像带负片的位移这样的事情呢? (int x = 8 << -1
)是否有规则可以解释或者我应该将其记忆为唯一的UB可能性?
我看了here并在整数溢出部分下面发现了带负片的位移,但是我不明白为什么它们是相关的。当int
移位过多时,会引起溢出,但IMO移位负数只是UB,问题不在于“超越边缘”......
另见这里,但那没有回答我的问题:
对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。
所以我的问题是:
答案 0 :(得分:1)
(编写评论的答案,包括我的。)
查找实际未定义行为(UB)的一个很好的起点是Jonathan Leffler的这些引用:
是的,有很多案例,对它们进行分组会很棘手。 C11标准的附录J.2记录了第557-571页上未定义的行为(每个页面上只有几行,所以它超过14页)。
参考相关文章,该文章介绍UB的类型,发现工具并包含UB列表;长(和作者的意图)完成(davmac的cortesy):
https://blog.regehr.org/archives/1520
两种方法可以记住&#34;:
by Ajay Brahmakshatriya,专注于不可避免的平台依赖:
我的一般经验法则是 - 任何看起来会改变不同实现行为(目标,平台等)的行为都是“#34; spot&#34; UB
by Yunnosch,专注于平衡标准化和优化的问题:
如果硬件供应商可能很难就此达成一致,或者很难明确定义并且允许一些优化实施的空间,那么它可能是UB。
可悲的是,所有这些&#34;规则&#34;不容易申请。 检查实际标准是不方便的。 这两条经验法则基于一些必要的经验;你需要设计一些编译器和/或处理器,或者它们之间存在很大差异。
所以&#34; 的实际答案是否有一种简单的方法来发现UB?&#34; 可能只是&#34; 否。&#34;
答案 1 :(得分:1)
具体来说,是位移,负数被认为是整数溢出,如果是,为什么?
不是,因为任何数量的0移位都不会溢出,但是将值0移动一个负值仍然是未定义的行为。 (我假设您可以将整数溢出视为整数溢出,如果您首先将移位量重新解释为无符号整数,此时它会很大并且肯定超出允许范围,并且如果被解释为如果移位的值不为零,则乘以2的幂乘以肯定会溢出。
简而言之,由于语言标准表明存在未定义的行为,因此负向移位会产生未定义的行为。
如果没有,它是否是更大现象的一部分?
John Regehr在a blog post中给出了一些广泛的UB类别。按无效金额转换是在“其他UB”类别中...
是否有(其他)唯一案例无法归入一个基本原因?
是的,请看上面的帖子。其中(这些直接取自博客文章):
你可以用某种方式对这些和其他例子进行分类,但这取决于你如何做到这一点。
特别是,上面的最后一个例子(关于源文件没有以新行结尾)显示了一些规则的任意性。
答案 2 :(得分:0)
如果x<<y
y
为负数,则有些平台会处理类似z=x<<y
的内容,其微码相当于:
unsigned temp = x;
unsigned count=y;
while(count--)
temp<<=1;
z=temp;
如果y
为负数,则该循环可能会运行很长时间;如果它在微码级别处理(我认为一些Transputer芯片就是这样),它可能会中断许多分钟,这可能会破坏系统的其他方面。
在大多数平台上,除了人为设想的场景之外,编译器不会为x<<y
或x
之外的任何值y
带来任何副作用。产生一个可能毫无意义的价值;事实上,编译器生成没有副作用的代码比执行任何其他操作更容易。不幸的是,一些编译器编写者认为他们应该寻找&#34;聪明的&#34;利用y
&#34;&#39;#34;是消极的,引发任意不良的后果,而不考虑它是否真的有用,也许是错误地认为&#34;聪明的&#34;和&#34;愚蠢&#34;是反义词。