问题标签 [undefined-behavior]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - 为什么当左侧操作数为负值时,左移操作会调用未定义行为?
在 C 中,当左侧操作数为负值时,按位左移操作调用未定义行为。
来自 ISO C99 (6.5.7/4) 的相关引用
E1 << E2 的结果是 E1 左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1 × 2 E2,以比结果类型中可表示的最大值多一个模数减少。如果 E1 有带符号类型和非负值,并且 E1 × 2 E2在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。
但是在 C++ 中,行为是明确定义的。
ISO C++-03 (5.8/2)
E1 << E2 的值是 E1(解释为位模式)左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值是 E1 乘以 2 的 E2 次幂,如果 E1 具有无符号长类型,则以模 ULONG_MAX+1 减少,否则为 UINT_MAX+1。[注意:常量 ULONG_MAX 和 UINT_MAX 在标题中定义)。]
这意味着
在 C 中调用未定义的行为,但该行为在 C++ 中定义良好。
是什么迫使 ISO C++ 委员会认为这种行为与 C 中的行为相反?
另一方面,implementation defined
当左操作数为负时,行为是按位右移操作,对吗?
我的问题是为什么左移操作会调用 C 中的未定义行为,为什么右移运算符只调用实现定义的行为?
PS:请不要给出“这是未定义的行为,因为标准是这样说的”这样的答案。:P
c - 我们可以通过指针更改用 const 定义的对象的值吗?
它会起作用吗?
java - 为什么此语句在 java x ^= y ^= x ^= y 中不起作用;
我期待这些值被交换。但它给出了 x=0 和 y=1。当我尝试使用 C 语言时,它给出了正确的结果。
java - ++i + ++i + ++i 在 Java 与 C 中
哪个更正确?Java 的结果是 12 或 C = 13。或者如果不是正确的问题,请详细说明。
c++ - i += ++i 在 C++0x 中是未定义的行为吗?
我对我发现的解释深信不疑,就 C++0x 而言,这i = ++i
并不是未定义的,但我无法判断 的行为是否 i += ++i
定义明确。有接盘侠吗?
c++ - 在 C/C++ 中检测有符号溢出
乍一看,这个问题似乎与如何检测整数溢出?,但实际上有很大不同。
我发现虽然检测无符号整数溢出非常简单,但检测 C/C++ 中的有符号溢出实际上比大多数人想象的要困难。
最明显但最幼稚的方法是:
问题在于,根据 C 标准,有符号整数溢出是未定义的行为。 换句话说,根据标准,一旦您甚至导致有符号溢出,您的程序就如同取消引用空指针一样无效。所以你不能导致未定义的行为,然后在事后尝试检测溢出,如上面的后置条件检查示例。
尽管上述检查可能适用于许多编译器,但您不能指望它。事实上,因为 C 标准说有符号整数溢出是未定义的,所以一些编译器(如 GCC)会在设置优化标志时优化掉上述检查,因为编译器假定有符号溢出是不可能的。这完全破坏了检查溢出的尝试。
因此,检查溢出的另一种可能方法是:
这似乎更有希望,因为我们实际上不会将两个整数相加,直到我们事先确保执行这样的相加不会导致溢出。因此,我们不会导致任何未定义的行为。
但是,不幸的是,此解决方案的效率比初始解决方案低很多,因为您必须执行减法运算才能测试您的加法运算是否有效。即使你不关心这个(小)性能损失,我仍然不完全相信这个解决方案是足够的。该表达式lhs <= INT_MIN - rhs
看起来与编译器可能优化掉的那种表达式完全一样,认为有符号溢出是不可能的。
那么这里有更好的解决方案吗?保证 1) 不会导致未定义的行为,以及 2) 不会为编译器提供优化溢出检查的机会?我在想可能有一些方法可以通过将两个操作数都转换为无符号数,并通过滚动你自己的二进制补码算术来执行检查,但我不确定如何做到这一点。
c - 在 C99 中, f()+g() 是未定义的还是仅仅是未指定的?
我曾经认为在 C99 中,即使函数的副作用f
和g
干扰,虽然表达式f() + g()
不包含序列点,f
并且g
会包含一些,所以行为是未指定的:要么 f() 会在之前调用g() 或 g() 在 f() 之前。
我不再那么肯定了。如果编译器内联函数(即使未声明函数,编译器也可能决定这样做inline
)然后重新排序指令怎么办?可以得到与上述两者不同的结果吗?换句话说,这是未定义的行为吗?
这不是因为我打算写这种东西,这是为了在静态分析器中为这样的语句选择最佳标签。
html - 奇怪的 HTML 注释行为
在我的代码中,我有一个带有单选按钮的表单。显示了第一个单选按钮,但没有显示第二个(每周重复)?如果我删除评论标记,那么一切正常。但为什么会这样?
c - 如何在没有中间副本的情况下在标准 C 中实现 memmove?
从我系统上的手册页:
void *memmove(void *dst, const void *src, size_t len);
描述
memmove() 函数将 len 个字节从字符串 src 复制到字符串 dst。
两个字符串可能重叠;复制始终以非破坏性
方式完成。
来自 C99 标准:
6.5.8.5 比较两个指针时,结果取决于所指向对象在地址空间中的相对位置。如果两个指向对象或不完整类型的指针都指向同一个对象,或者都指向同一个数组对象的最后一个元素之后,它们比较相等。如果指向的对象是同一个聚合对象的成员,则指向稍后声明的结构成员的指针比较大于指向结构中较早声明的成员的指针,并且指向具有较大下标值的数组元素的指针比较大于指向同一数组的元素的指针具有较低的下标值。所有指向同一个联合对象成员的指针比较相等。如果表达式
P
指向数组对象的一个元素,而表达式 Q 指向同一个数组对象的最后一个元素,则指针表达式Q+1
比较大于P
。在所有其他情况下,行为是 undefined。
重点是我的。
参数dst
和src
可以转换为指针以char
减轻严格的别名问题,但是是否可以比较两个可能指向不同块内的指针,以便在它们指向同一块内时以正确的顺序进行复制?
显而易见的解决方案是if (src < dst)
,但如果src
和dst
指向不同的块,那是未定义的。“未定义”意味着您甚至不应该假设条件返回 0 或 1(这在标准词汇中被称为“未指定”)。
另一种选择是if ((uintptr_t)src < (uintptr_t)dst)
,至少未指定,但我不确定标准是否保证src < dst
定义时,它等同于(uintptr_t)src < (uintptr_t)dst)
。指针比较是从指针算术定义的。例如,当我阅读关于加法的第 6.5.6 节时,在我看来,指针算术可能会朝着与uintptr_t
算术相反的方向发展,也就是说,兼容的编译器可能有, whenp
是 type char*
:
这只是一个例子。一般来说,将指针转换为整数时似乎很少保证。
这是一个纯粹的学术问题,因为memmove
是与编译器一起提供的。在实践中,编译器作者可以简单地将未定义的指针比较提升为未指定的行为,或者使用相关的 pragma 来强制他们的编译器memmove
正确编译它们。例如,这个实现有这个片段:
我仍然想用这个例子来证明标准在未定义的行为方面走得太远,如果确实memmove
不能在标准 C 中有效地实现。例如,在回答这个 SO question时没有人打勾。
c++ - const_cast 和 UB
$5.2.11/7 - "[注意:根据对象的类型,通过指针、左值或指向数据成员的指针的写操作由 const_cast 产生,该 const_cast 丢弃了 const-qualifier68)可能会产生未定义的行为 (7.1. 5.1).]"
本节 (C++03) 的措辞令我惊讶。令人惊讶的是两件事。
a) 首先,使用“may”。为什么是“可能”?标准中的其他地方对未定义的行为非常明确
b)为什么放弃最初的 const 对象的 const 性不是立即“未定义的行为”。为什么触发UB需要写入?