获取左移负数的警告
Get warning for left shifting a negative number
我正在尝试针对左移负数的未定义行为生成警告。根据this答案,C中负数的左移是未定义的。
The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and non-negative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behaviour is undefined.
我想了解为什么我没有收到此代码的警告:
x << 3
gcc -Wall(版本 9.1.0)
int main ()
{
int x= -4, y, z=5;
y = z << x;
y = x << 3;
return y;
}
另外,我也没有收到关于左移负数的警告
z << x
C89 中为左操作数的所有整数值指定了 left-shifting 任何小于字长的值的行为,即使负左操作数的指定行为通常更小在 non-two's-complement 补码平台上,或者在 error-trapping 平台上,迭代乘以 2 会溢出。
为了避免要求实现以 less-than-ideal 方式运行,C99 的作者决定允许实现以他们认为合适的方式处理此类情况。 C99 标准的作者并没有试图猜测实现可能有充分理由偏离 C89 行为的所有地方,而是推测实现将遵循 C89 行为,如果没有充分的理由不这样做,无论标准是否强制他们这样做。
如果标准的作者打算对 left-shift 操作的处理进行任何更改 在 C89 行为有意义的情况下,应该提及已发布的基本原理文档中的更改。无论如何都有 none。我可以从这种遗漏中看到的唯一推论是,从 "Behave in a particular fashion that happens to almost always make sense" 到 "Behave in that fashion if the implementer judges that it makes sense, and otherwise process the code in whatever other fashion the implementer sees fit" 的变化不足以证明评论的合理性。标准的作者未能以任何方式强制执行行为这一事实并不意味着他们没有预料到 general-purpose 两个 s-complement 实现不会像往常一样继续处理此类转变,也不是程序员不应该有类似的期望。
在5 << -4
中,GCC 9.1.0 和带有 clang-1001.0.46.4 的 Apple LLVM 10.0.1,针对 x86-64,针对 GCC 和LLVM-clang 的“班次计数为负”)。在 -4 << 3
中,GCC 不会发出警告,但 LLVM-clang 会发出警告(“转移负值是未定义的”)。
C 标准在这两种情况下都不需要诊断,因此编译器是否需要诊断是实现质量问题(或者,编译器可能通过定义负值的左移和因此不认为这是一个错误)。
当操作数不是常量时,如 z << x
和 x << 3
,我们可以推测编译器无法看到操作数为负,因此它不会发出警告。通常,这些表达式可能具有定义的行为:如果 x
不是负数并且在合适的范围内(在前一种情况下不大于 z
的宽度并且没有大到 x << 3
在后一种情况下溢出),定义了行为(除了 z << x
可能溢出的可能性)。 C标准并没有说可能具有负值的类型的左移,即,有符号类型,是未定义的,只是左移负值是未定义的。因此,只要 x
是表达式(例如 z << x
或 x << 3
.
中的带符号类型,编译器就会发出警告消息,这将是错误的
当然,给定 int x = -4
并且 x
在初始化和表达式 z << x
和 x << 3
之间没有任何变化,编译器可以推断出在这种特定情况下,班次未定义并发出警告。这不是标准要求的,编译器不这样做只是编译器实现质量的问题。
编译器在这种情况下不发出警告的简短解释是因为不需要这样做。
标准中对"undefined"的定义也明确规定了"no diagnostic required"。这意味着该标准不需要在这种情况下进行诊断。这样做的原因是,从技术上讲,编译器可能无法在合理的时间内或(在某些情况下)检测到所有未定义行为的实例。某些情况只能在 运行 时间检测到。编译器是复杂的代码片段 - 即使人类可以轻松识别问题 - 编译器可能不会(另一方面,编译器也会检测到人类无法轻易发现的问题)。
当不需要诊断时,在编译器发出警告之前,有几件事必须发生 - 都是自行决定的。
首先发生的事情相当于 "quality of implementation" 问题 - 供应商或编译器开发人员选择编写检测特定情况的代码和发出警告的其他代码
这两个步骤(检测案例并对其发出警告)是分开的,并且完全是自行决定的 - 标准不需要诊断,因此,即使编写了检测特定案例的代码,仍然不需要编译器发出警告。实际上,即使检测到问题,编译器开发人员也可能出于各种原因选择不发出警告。一些警告是关于罕见的边缘情况,因此不值得发出它们。某些警告具有固有的 "false positive" 率,这往往会导致开发人员向编译器供应商抱怨不必要的警告错误报告 - 因此编译器默认配置为不发出它们。
发出警告的下一个要求是编译器的用户必须选择显示警告。由于开发人员对供应商的强烈游说——大多数现代编译器都是默认配置的,因此它们只发出相对较少数量的警告。因此,希望从编译器获得尽可能多帮助的开发人员需要显式启用它。例如,通过对 gcc 和 clang 使用 -Wall
选项。
我正在尝试针对左移负数的未定义行为生成警告。根据this答案,C中负数的左移是未定义的。
The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and non-negative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behaviour is undefined.
我想了解为什么我没有收到此代码的警告:
x << 3
gcc -Wall(版本 9.1.0)
int main ()
{
int x= -4, y, z=5;
y = z << x;
y = x << 3;
return y;
}
另外,我也没有收到关于左移负数的警告
z << x
C89 中为左操作数的所有整数值指定了 left-shifting 任何小于字长的值的行为,即使负左操作数的指定行为通常更小在 non-two's-complement 补码平台上,或者在 error-trapping 平台上,迭代乘以 2 会溢出。
为了避免要求实现以 less-than-ideal 方式运行,C99 的作者决定允许实现以他们认为合适的方式处理此类情况。 C99 标准的作者并没有试图猜测实现可能有充分理由偏离 C89 行为的所有地方,而是推测实现将遵循 C89 行为,如果没有充分的理由不这样做,无论标准是否强制他们这样做。
如果标准的作者打算对 left-shift 操作的处理进行任何更改 在 C89 行为有意义的情况下,应该提及已发布的基本原理文档中的更改。无论如何都有 none。我可以从这种遗漏中看到的唯一推论是,从 "Behave in a particular fashion that happens to almost always make sense" 到 "Behave in that fashion if the implementer judges that it makes sense, and otherwise process the code in whatever other fashion the implementer sees fit" 的变化不足以证明评论的合理性。标准的作者未能以任何方式强制执行行为这一事实并不意味着他们没有预料到 general-purpose 两个 s-complement 实现不会像往常一样继续处理此类转变,也不是程序员不应该有类似的期望。
在5 << -4
中,GCC 9.1.0 和带有 clang-1001.0.46.4 的 Apple LLVM 10.0.1,针对 x86-64,针对 GCC 和LLVM-clang 的“班次计数为负”)。在 -4 << 3
中,GCC 不会发出警告,但 LLVM-clang 会发出警告(“转移负值是未定义的”)。
C 标准在这两种情况下都不需要诊断,因此编译器是否需要诊断是实现质量问题(或者,编译器可能通过定义负值的左移和因此不认为这是一个错误)。
当操作数不是常量时,如 z << x
和 x << 3
,我们可以推测编译器无法看到操作数为负,因此它不会发出警告。通常,这些表达式可能具有定义的行为:如果 x
不是负数并且在合适的范围内(在前一种情况下不大于 z
的宽度并且没有大到 x << 3
在后一种情况下溢出),定义了行为(除了 z << x
可能溢出的可能性)。 C标准并没有说可能具有负值的类型的左移,即,有符号类型,是未定义的,只是左移负值是未定义的。因此,只要 x
是表达式(例如 z << x
或 x << 3
.
当然,给定 int x = -4
并且 x
在初始化和表达式 z << x
和 x << 3
之间没有任何变化,编译器可以推断出在这种特定情况下,班次未定义并发出警告。这不是标准要求的,编译器不这样做只是编译器实现质量的问题。
编译器在这种情况下不发出警告的简短解释是因为不需要这样做。
标准中对"undefined"的定义也明确规定了"no diagnostic required"。这意味着该标准不需要在这种情况下进行诊断。这样做的原因是,从技术上讲,编译器可能无法在合理的时间内或(在某些情况下)检测到所有未定义行为的实例。某些情况只能在 运行 时间检测到。编译器是复杂的代码片段 - 即使人类可以轻松识别问题 - 编译器可能不会(另一方面,编译器也会检测到人类无法轻易发现的问题)。
当不需要诊断时,在编译器发出警告之前,有几件事必须发生 - 都是自行决定的。
首先发生的事情相当于 "quality of implementation" 问题 - 供应商或编译器开发人员选择编写检测特定情况的代码和发出警告的其他代码
这两个步骤(检测案例并对其发出警告)是分开的,并且完全是自行决定的 - 标准不需要诊断,因此,即使编写了检测特定案例的代码,仍然不需要编译器发出警告。实际上,即使检测到问题,编译器开发人员也可能出于各种原因选择不发出警告。一些警告是关于罕见的边缘情况,因此不值得发出它们。某些警告具有固有的 "false positive" 率,这往往会导致开发人员向编译器供应商抱怨不必要的警告错误报告 - 因此编译器默认配置为不发出它们。
发出警告的下一个要求是编译器的用户必须选择显示警告。由于开发人员对供应商的强烈游说——大多数现代编译器都是默认配置的,因此它们只发出相对较少数量的警告。因此,希望从编译器获得尽可能多帮助的开发人员需要显式启用它。例如,通过对 gcc 和 clang 使用 -Wall
选项。