比较下溢的无符号整数到-1明确定义? [英] Is comparing an underflowed, unsigned integer to -1 well-defined?
问题描述
请考虑以下†
: size_t r =
r--;
const bool result =(r == -1);
结果初始化 result
明确定义的行为?
和它的结果 true
,如我所料?
< hr>
这个Q& A是写的,因为我特别不确定两个因素。
他们都可以使用术语
† 此示例的灵感来自于循环条件当计数器无符号时:
for(size_t r = m.size() - 1; r!= -1; r - )
/ sup>
size_t r = 0;
r--;
const bool result =(r == -1);
严格来说, result
实现定义。在实践中,几乎肯定是 true
;如果有一个实现 false
,我会感到惊讶。
r -
之后的 SIZE_MAX
的值, code>< stddef.h> / < cstddef>
。
对于比较 r == -1
,对两个操作数执行通常的算术转换。通常的算术转换的第一步是将整数促销信息应用于两个操作数。
r
是 size_t
类型,一个实现定义的无符号整数类型。 -1 是类型
int
的表达式。
在大多数系统上, size_t
至少与 int
一样宽。在这样的系统上,整数提升使得 r
的值被转换为 unsigned int
类型(如果 size_t
与 int
具有相同的宽度,但转换排名较低,则可能发生前者)。现在左操作数(无符号)至少具有右操作数的排名(其被签名)。右操作数转换为左操作数的类型。此转换产生与 r
相同的值,因此等式比较产生 true
。
size_t
b 这是正常是16位(假设它是 typedef
for unsigned short
)和 int
是32位。因此 SIZE_MAX == 65535
和 INT_MAX == 2147483647
。或者我们可以有一个32位的 size_t
和一个64位的 int
。
现在比较的左侧有类型 size_t
和值 65535
。由于签名 int
可以表示 size_t
类型的所有值,积分促销转换值为 int
类型的 65535
。 ==
运算符的两侧都有类型 int
,因此通常的算术转换无关。该表达式等效于 65535 == -1
,这显然是 false
。
正如我所提到的,这种事情不太可能发生在类型 size_t
的表达式 - 但它可以很容易发生与较窄的无符号类型。例如,如果 r
被声明为 unsigned short
或 unsigned char
,或者甚至在签名的系统上的一个简单的
char
, result
可能会 false
。 (我说可能是因为 short
或者甚至 unsigned char
可以具有与 int
,在这种情况下 result
将会是 true
。)
在实践中,你可以通过显式地进行转换,而不是依赖于实现定义的通常的算术转换来避免潜在的问题:
const bool result =(r ==(size_t)-1);
或
const bool result =(r == SIZE_MAX);
C ++ 11标准引用:
- 5.10 [expr.eq]平等运算符
- 5.9 [expr.rel]关系运算符(指定执行通常的算术转换)
- 5 [expr]表达式,第9段:通常的算术转换
- 4.5 [conv.prom]积分促销
- support.types]
size_t
18.2第6-7节:
6类型
注意:建议实现为size_t
是一个实现定义的无符号整型类型
,
选择类型ptrdiff_t
和size_t
的整数转换排名(4.13)没有
大于signed long int
,除非更大的大小是
需要包含所有可能的值。 - end note]
所以没有禁止让 size_t
code> int 。我可以几乎似乎想象一个系统,其中 int
是64位,但没有单个对象可以大于2 32 -1字节,因此 size_t
为32位。
Consider the following†:
size_t r = 0;
r--;
const bool result = (r == -1);
Does the comparison whose result initialises result
have well-defined behaviour?
And is its result true
, as I'd expect?
This Q&A was written because I was unsure of two factors in particular.
They may both be identified by use of the term "crucial[ly]" in my answer.
† This example is inspired by an approach for loop conditions when the counter is unsigned:
for (size_t r = m.size() - 1; r != -1; r--)
size_t r = 0;
r--;
const bool result = (r == -1);
Strictly speaking, the value of result
is implementation-defined. In practice, it's almost certain to be true
; I'd be surprised if there were an implementation where it's false
.
The value of r
after r--
is the value of SIZE_MAX
, a macro defined in <stddef.h>
/ <cstddef>
.
For the comparison r == -1
, the usual arithmetic conversions are performed on both operands. The first step in the usual arithmetic conversions is to apply the integral promotions to both operands.
r
is of type size_t
, an implementation-defined unsigned integer type. -1
is an expression of type int
.
On most systems, size_t
is at least as wide as int
. On such systems, the integral promotions cause the value of r
either to be converted to unsigned int
or to keep its existing type (the former can happen if size_t
has the same width as int
, but a lower conversion rank). Now the left operand (which is unsigned) has at least the rank of the right operand (which is signed). The right operand is converted to the type of the left operand. This conversion yields the same value as r
, and so the equality comparison yields true
.
That's the "normal" case.
Suppose we have an implementation where size_t
is 16 bits (let's say it's a typedef
for unsigned short
) and int
is 32 bits. So SIZE_MAX == 65535
and INT_MAX == 2147483647
. Or we could have a 32-bit size_t
and a 64-bit int
. I doubt that any such implementation exists, but nothing in the standard forbids it (see below).
Now the left side of the comparison has type size_t
and value 65535
. Since signed int
can represent all the values of type size_t
, the integral promotions convert the value to 65535
of type int
. Both side of the ==
operator have type int
, so the usual arithmetic conversions have nothing to do. The expression is equivalent to 65535 == -1
, which is clearly false
.
As I mentioned, this kind of thing is unlikely to happen with an expression of type size_t
-- but it can easily happen with narrower unsigned types. For example, if r
is declared as an unsigned short
, or an unsigned char
, or even a plain char
on a system where that type is signed, the value of result
will probably be false
. (I say probably because short
or even unsigned char
can have the same width as int
, in which case result
will be true
.)
In practice, you can avoid the potential problem by doing the conversion explicitly rather than relying on the implementation-defined usual arithmetic conversions:
const bool result = (r == (size_t)-1);
or
const bool result = (r == SIZE_MAX);
C++11 standard references:
- 5.10 [expr.eq] Equality operators
- 5.9 [expr.rel] Relational operators (specifies that the usual arithmetic conversions are performed)
- 5 [expr] Expressions, paragraph 9: Usual arithmetic conversions
- 4.5 [conv.prom] Integral promotions
- 18.2 [support.types]
size_t
18.2 paragraphs 6-7:
6 The type
size_t
is an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.7 [ Note: It is recommended that implementations choose types for
ptrdiff_t
andsize_t
whose integer conversion ranks (4.13) are no greater than that ofsigned long int
unless a larger size is necessary to contain all the possible values. — end note ]
So there's no prohibition on making size_t
narrower than int
. I can almost plausibly imagine a system where int
is 64 bits, but no single object can be bigger than 232-1 bytes so size_t
is 32 bits.
这篇关于比较下溢的无符号整数到-1明确定义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!