我知道,右移负号类型取决于实现,但是如果我执行左移怎么办?例如:
int i = -1;
i << 1;
这个定义明确吗?
我认为标准没有提到带符号类型的负值
如果 E1 具有符号类型和非负值,并且 E1 × 2E2 是 可以用结果类型表示,那么这就是结果值; 否则,行为是未定义的。
它只是澄清,如果结果不能用有符号类型表示,那么行为是未定义的。
您没有正确阅读该句子。标准将其定义为: 左操作数具有有符号类型 并且 非负值 并且 结果是可表示的(并且之前在同一段落中将其定义为无符号类型)。在所有其他情况下(请注意该句子中使用分号),即,如果这些条件中的任何一个未得到验证,则行为未定义。
C 标准的作者不想说,必须修改负数左移会捕获的机器的编译器以防止此类捕获(因为程序可能潜在地依赖它),但是如果左移负数允许数字触发陷阱,该陷阱可能导致任何任意行为(包括随机代码执行),这意味着允许左移负数执行任何操作。因此是未定义的行为。
实际上,直到大约 5 年前,99% 以上为使用补码数学的机器编写的编译器(意味着 1990 年以来制造的 99% 以上的机器)都会始终为
x<<y
和
x>>y
产生以下行为,在某种程度上,对这种行为的代码依赖被认为并不比假设
char
为 8 位的代码更不可移植。 C 标准没有强制要求这种行为,但任何想要与现有代码的广泛基础兼容的编译器作者都会遵循它。
y
是有符号类型,则对
x << y
和
x >> y
进行评估,就好像
y
被转换为无符号一样。
x
是类型
int
,则
x<<y
相当于
(int)((unsigned)x << y)
。
x
为
int
类型且为正,则
x>>y
相当于
(unsigned)x >> y
。如果
x
属于
int
类型且为负,则
x>>y
相当于
~(~((unsigned)x) >> y)
。
x
属于
long
类型,则适用类似的规则,但使用
unsigned long
而不是
unsigned
。
x
是 N 位类型并且
y
大于 N-1,则
x >> y
和
x << y
可以任意产生零,或者可以表现为右侧操作数是
y % N
;它们可能需要与
y
成正比的额外时间 [请注意,在 32 位机器上,如果
y
为负数,则可能需要很长的时间,尽管我只知道一台机器实际上会运行更多时间超过 256 个额外步骤]。编译器的选择不一定一致,但总是会返回指示值之一,而没有其他副作用。不幸的是,由于我不太明白的原因,编译器编写者决定,编译器不应允许程序员指示编译器应使用哪些假设来删除死代码,而应假设不可能执行任何未强制执行其行为的转换按C标准。因此,给出如下代码:
uint32_t shiftleft(uint32_t v, uint8_t n)
{
if (n >= 32)
v=0;
return v<<n;
}
编译器可能会确定,因为当 n 为 32 或更大时,代码将出现未定义行为,所以编译器可能会假设
if
永远不会返回 true,因此可能会省略代码。因此,除非或直到有人提出一个 C 标准来恢复经典行为并允许程序员指定哪些假设值得删除死代码,否则不能为任何可能输入超现代编译器的代码推荐这种构造。
这包括
如果 E1 具有符号类型
和非负值