我知道,右移负号类型取决于实现,但是如果我执行左移怎么办?例如:
int i = -1;
i << 1;
这个定义明确吗?
我认为标准没有提到带符号类型的负值
如果 E1 具有符号类型和非负值,且 E1 × 2E2 为
可以用结果类型表示,那么这就是结果值;
否则,行为未定义。
它只是澄清,如果结果不能用有符号类型表示,则行为是未定义的。
I know, right shifting a negative signed type depends on the implementation, but what if I perform a left shift? For example:
int i = -1;
i << 1;
Is this well-defined?
I think the standard doesn't say about negative value with signed 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 behavior is undefined.
It only clarifies that if the result isn't representable in signed type then the behavior is undefined.
发布评论
评论(4)
你没有正确地阅读那句话。标准定义它,如果:左操作数有一个有符号类型和一个非负值并且结果是可表示的(并且之前在同一段落中将其定义为无符号类型) )。在所有其他情况下(请注意该句子中使用分号),即,如果存在以下任何情况这些条件未经验证,行为未定义。
You're not reading that sentence correctly. The standard defines it if: the left operand has a signed type and a non-negative value and the result is representable (and previously in the same paragraph defines it for unsigned types). In all other cases (notice the use of the semicolon in that sentence), i.e, if any of these conditions isn't verified, the behaviour is undefined.
当 C 标准被编纂时,不同的平台在左移负整数时会做不同的事情。对于其中一些,该行为可能会触发特定于实现的陷阱,其行为可能超出程序的控制范围,并且可能包括随机代码执行。尽管如此,为此类平台编写的程序可能会利用此类行为(例如,程序可以指定用户在运行系统之前必须执行某些操作来配置系统的陷阱处理程序,但程序随后可以利用适当配置的陷阱处理程序)。
C 标准的作者不想说,必须修改负数左移会捕获的机器的编译器以防止此类捕获(因为程序可能潜在地依赖它),但是如果左移负数允许触发陷阱,该陷阱可能导致任何任意行为(包括随机代码执行),这意味着允许左移负数执行任何操作。因此未定义的行为。
实际上,直到大约 5 年前,99% 以上为使用补码数学的机器编写的编译器(意味着 1990 年以来制造的 99% 以上的机器)都会始终为
x< 和
x>>y
,在某种程度上,对此类行为的代码依赖被认为并不比假设的代码更不可移植char
是 8 位。 C 标准没有强制要求这种行为,但任何想要与现有代码的广泛基础兼容的编译器作者都会遵循它。x>> y
的计算方式与y
被强制转换为无符号一样。x
是int
类型,则x<相当于
(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 标准。因此,给定如下代码:
编译器可能会确定,因为当 n 为 32 或更大时,代码将参与未定义行为,所以编译器可能会假设
if
永远不会返回 true,因此可能省略代码。因此,除非或直到有人提出恢复经典行为并允许程序员指定哪些假设值得删除死代码的 C 标准,否则不能为任何可能输入超现代编译器的代码推荐此类构造。When the C standards were codified, different platforms would do different things when left-shifting negative integers. On some of them, the behavior might trigger implementation-specific traps whose behavior could be outside a program's control, and which could include random code execution. Nonetheless, it was possible that programs written for such platforms might make use of such behavior (a program could e.g. specify that a user would have to do something to configure a system's trap handlers before running it, but the program could then exploit the behavior of the suitably-configured trap handlers).
The authors of the C standard did not want to say that compilers for machines where left-shifting of negative numbers would trap must be modified to prevent such trapping (since programs might potentially be relying upon it), but if left-shifting a negative number is allowed to trigger a trap which could cause any arbitrary behavior (including random code execution) that means that left-shifting a negative number is allowed to do anything whatsoever. Hence Undefined Behavior.
In practice, until about 5 years ago, 99+% of compilers written for a machine that used two's-complement math (meaning 99+% of machines made since 1990) would consistently yield the following behaviors for
x<<y
andx>>y
, to the extent that code reliance upon such behavior was considered no more non-portable than code which assumedchar
was 8 bits. The C standard didn't mandate such behavior, but any compiler author wanting to be compatible with a wide base of existing code would follow it.y
is a signed type,x << y
andx >> y
are evaluated as thoughy
was cast to unsigned.x
is typeint
,x<<y
is equivalent to(int)((unsigned)x << y)
.x
is typeint
and positive,x>>y
equivalent to(unsigned)x >> y
. Ifx
is of typeint
and negative,x>>y
is equivalent to~(~((unsigned)x) >> y)
.x
is of typelong
, similar rules apply, but withunsigned long
rather thanunsigned
.x
is an N-bit type andy
is greater than N-1, thenx >> y
andx << y
may arbitrarily yield zero, or may act as though the right-hand operand wasy % N
; they may require extra time proportional toy
[note that on a 32-bit machine, ify
is negative, that could potentially be a long time, though I only know of one machine which would in practice run more than 256 extra steps]. Compilers were not necessarily consistent in their choice, but would always return one of the indicated values with no other side-effects.Unfortunately for reasons I can't quite fathom, compiler writers have decided that rather than allowing programmers to indicate what assumptions compilers should use for dead-code removal, compilers should assume that it is impossible to execute any shift whose behavior isn't mandated by the C standard. Thus, given code like the following:
a compiler may determine that because the code would engage in Undefined Behavior when n is 32 or larger, the compiler may assume that the
if
will never return true, and may thus omit the code. Consequently, unless or until someone comes up with a standard for C which restores the classic behaviors and allows programmers to designate what assumptions merit dead code removal, such constructs cannot be recommended for any code that might be fed to a hyper-modern compiler.这包括
This includes the
在 C++20 中,定义了左移负整数(以唯一合理的方式):
这句话适用于有符号和无符号整数。虽然这可能不是很明显,但对于有符号整数来说,这意味着只需移动位,没有废话。
在 C++20 中,定义了右移负整数(以唯一合理的方式):
这句话适用于有符号和无符号整数。对于有符号整数,这意味着移位必须是算术移位。
负(或大)移位量仍然是 UB:
注意:我添加这个答案不是因为它直接按原样回答问题,而是因为人们链接到这个问题来证明移位负整数是未定义的或实现已定义。以前是,但现在不是了。
In C++20, left-shifting negative integers is defined (in the only sensible way):
This sentence applies to both signed and unsigned integers. Although it may not be immediately obvious, for signed integers this means just shift the bits, no nonsense.
In C++20, right-shifting negative integers is defined (in the only sensible way):
This sentence applies to both signed and unsigned integers. For signed integers, it means that the shift must be an arithmetic shift.
Negative (or large) shift amount is still UB:
note: I've added this answer not because it directly answers the question as-is, but because people are linking to this question to prove that shifting negative integers is undefined or implementation defined. It used to be, but now it's not.