当 b 大于 a 中的位数时右移(a >> b)的未定义行为?
Undefined behaviour of right shift (a >> b) when b is greater than the number of bits in a?
显然,右移操作的行为:
a >> b
当b >= sizeof(a)*CHAR_BIT
时,在C和C++中是未定义的(而在正常情况下,由于右移而从左边引入的"新位"等于零)。
为什么这种未定义的行为比b >= sizeof(a)*CHAR_BIT
时将结果设置为零要好?
我们可以从"为什么语言设计者容忍未定义的行为"中了解到语言为什么选择未定义行为,它说:
这个答案来自C背后的两个通用设计原则:
- 该语言不应给实现带来不必要的开销
- 在各种各样的硬件上实现C应该尽可能容易
在这种特定的情况下,当我们使用大于位宽的移位计数时会发生什么将取决于体系结构,例如,正如我在这里的回答中所解释的:
在某些平台上,移位计数将被屏蔽为5 bits
。例如,在x86
体系结构上,我们可以在IA-32体系结构兼容性部分中的"英特尔®;64和IA-32体系结构软件开发人员手册"部分看到SAL/SAR/SHL/SHR--移位,内容是:
8086不屏蔽移位计数。但是,所有其他IA-32处理器(从英特尔286处理器开始)都会将移位计数屏蔽为5位,从而导致最大计数为31。[…]
因此,在某些平台上,为任意计数实现移位可能会带来负担,因此最好将其保留为未定义的行为。
为什么不指定行为
如果我们看一下国际标准——程序设计语言——C的基本原理,它说:
未指定的行为使实现者在翻译程序时有一定的自由度。这个纬度然而,并没有延伸到无法翻译程序的程度,因为所有可能的行为是"正确的",因为它们不会在任何实现中导致未定义的行为。
因此,一定存在或仍然存在行为不正确的情况,并且会出现严重问题。
为什么这种未定义的行为比将结果设置为零更好的示例。
通常,一个CPU只有一条指令来执行移位。如果该指令需要与上限进行比较,则需要更多的电路并减缓移位。相反,许多CPU只是使用移位的最低有效位来确定要移位多少。
// Example - not specified behavior (assume 32-bit int)
y = 1 << 34;
// same as
y = 1 << 2;
第一批PC使用8/16位处理器,该处理器使用最少的8位来确定移位,因此一旦移位计数大于"int宽度",但小于256,它确实会移位为零。这样做的问题是,每班需要1个时钟刻度。因此,在最坏的情况下,简单的移位命令可能需要255个时钟信号才能执行。当然,在16次滴答声之后,除了0之外什么都没有移动。此长最坏情况指令不可中断!因此,使该处理器的最坏情况中断延迟远比竞争对手差。英特尔没有再犯这个错误。
- 编译C++时未定义的引用
- vscode g++链路故障:体系结构x86_64的未定义符号
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 不知道某个东西是否被忽略会引入未定义的行为吗
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 未定义的引用在哪里
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- c++11评估顺序(未定义的行为)
- 使用mysql c++连接器的未定义引用
- 从python调用openMP共享库时,未定义opnMP函数
- 在 Mac 上使用 CMAKE 将 FFTW 和 FFTWPP 链接到项目中时未定义的符号
- Cmake 链接问题:未定义对 Button::mousePressEvent(QGraphicsSceneMouseE
- 未定义的引用 .. 使用 OpenCV 编译 C++ 代码时,从命令行
- 具有外部"c"和程序集的未定义函数
- 此增量后语句是否会导致未定义的行为?
- 尝试调用 .h 文件中定义的变量时出现变量未定义错误
- 在C++中使用内联方法时出现未定义的符号错误
- 对 Scalar ::Scalar() 的未定义引用