Edg*_*net 9 c++ bit-shift undefined-behavior language-lawyer c++11
在c ++ 11中,根据en.cppreference.com,
对于有符号和非负数a,如果它在返回类型中可表示,则<< b的值为 *2 b,否则行为未定义.
我的理解是,由于255*2 24不能表示为a int32_t,因此对(int32_t) 255 << 24
产量的评估是未定义的行为.那是对的吗?这可能是编译器依赖的吗?这是一个IP16环境,如果重要的话.
背景:这来自我与arduino.stackexchange.com上的用户的争论.根据他的说法,"根本就没有任何不确定":
你注意到大部分的位移是"实现定义".所以你不能从规范中引用章节和经文.您必须转到GCC文档,因为这是唯一可以告诉您实际情况的地方. gnu.org/software/gnu-c-manual/gnu-c-manual.html#Bit-Shifting - 对于负移位值,它只是"未定义".
编辑:从目前为止的答案来看,似乎我对C++ 11标准的阅读是正确的.然后我的问题的关键部分是这个表达式是否在gcc中调用未定义的行为.正如davmac在他的评论中所说,我问"GCC,一个实现,是否定义了一种行为,即使它未被语言标准定义".
从我链接到的gcc手册,看起来确实已经定义了,虽然我发现本手册的措辞听起来更像是教程而不是"语言法".从PSkocik的答案(以及Kane对该答案的评论)来看,它似乎是未定义的.所以我仍然有疑问.
我想我的梦想是在一些gcc文档中有明确的声明,说明1)gcc没有定义标准中明确未定义的任何行为,或者2)gcc确实从版本XX.XX定义了这种行为并且提交到保持在所有后续版本中定义.
编辑2:PSkocik删除了他的答案,我发现这很不幸,因为它提供了有趣的信息.根据他的回答,凯恩对答案的评论以及我自己的实验:
(int32_t)255<<24 使用clang和编译时产生运行时错误 -fsanitize=undefined-fsanitize=undefined(int32_t)256<<24 编译时会出现运行时错误
g++ -std=c++11 -fsanitize=undefined第2点与C++ 11模式中gcc比标准更广泛地定义左移的解释是一致的.根据第3点,这个定义可能只是C++ 14的定义.但是,第3点
与引用的手册是gcc(C++ 11模式)的完整定义的想法不一致,因为该手册没有提供可能未定义的提示.<<(int32_t)256<<24
随着时间的推移,这种情况发生了变化,并且有充分的理由,让我们来看看历史.请注意,在所有情况下,只需执行static_cast<int>(255u << 24)一直定义的行为.也许只是这样做并侧面解决所有问题.
最初的C++ 11措辞是:
值
E1 << E2是E1左移位E2位置; 空位是零填充的.如果E1具有无符号类型,则结果的值将比结果类型中可表示的最大值模数减1.否则,如果有一个有符号类型和非负值,并且在结果类型中可表示,那么这就是结果值; 否则,行为未定义.E1×2E2E1E1×2E2
255 << 24 是C++ 11中未定义的行为,因为结果值不能表示为32位有符号整数,它太大了.
这种未定义的行为会导致一些问题,因为constexpr 必须诊断未定义的行为 - 因此设置值的一些常用方法会导致硬错误.因此CWG 1457:
8.8 [expr.shift]第2段的当前措辞使得通过将(带符号)1左移到符号位来创建给定类型的最负整数的未定义行为,即使这并非罕见地完成并且有效正确地在大多数(二进制补码)架构上[...]因此,这种技术不能用于常量表达式,这将破坏大量代码.
这是针对C++ 11的缺陷.从技术上讲,符合标准的C++ 11编译器会实现所有缺陷报告,因此在C++ 11中说这不是未定义的行为是正确的.255 << 24C++ 11中的行为被定义为-16777216.
可以在C++ 14中看到缺陷后的措辞:
值
E1 << E2是E1左移位E2位置; 空位是零填充的.如果E1具有无符号类型,则结果的值将比结果类型中可表示的最大值模数减1.否则,如果具有有符号类型和非负值,并且在结果类型的相应无符号类型中可表示,则转换为结果类型的该值是结果值; 否则,行为未定义.E1×2E2E1E1×2E2
C++ 17中的措辞/行为没有变化.
但对于C++ 20,由于Signed Integers是Two's Complement(及其措辞文件),措辞大大简化:
值
E1 << E2是与模数一致的唯一值,其中是结果类型的范围指数.E1×2E22NN
255 << 24 仍然在C++ 20中定义了行为(具有相同的结果值),只是我们如何到达那里的规范变得更加简单,因为语言不必解决签名整数的表示形式的问题.实现定义.
“未定义的行为是标准没有提出要求的行为。” 这意味着它甚至可以是预期/正确的行为。该 C++ 标准规定了有关移位运算符的内容。
\n\n\n\n\n8.5.7 移位运算符 [expr.shift](C++ 标准草案 N4713)
\n\n\n
\n- 操作数应为整型或无作用域枚举类型,并执行整型提升。结果的类型是提升后的左操作数的类型。如果右操作数为负,或者大于或等于提升的左操作数的位长度,则行为为 unde\xef\xac\x81ned。否则,如果
\nE1具有有符号类型和非负值,并且可以用结果类型的相应无符号类型表示,则转换为结果类型的该值就是结果值;否则,行为为 unde\xef\xac\x81ned。E1\xc3\x972E2
正如@rustyx 在下面指出的,“措辞”可以用结果类型的相应无符号类型表示”是C++14。不幸的是,在C++11中仍然是 UB 。”E1\xc3\x972E2
| 归档时间: |
|
| 查看次数: |
377 次 |
| 最近记录: |