为什么整数提升的结果不同?
请看我的测试代码:
#include <stdlib.h>
#include <stdio.h>
#define PRINT_COMPARE_RESULT(a, b) \
if (a > b) { \
printf( #a " > " #b "\n"); \
} \
else if (a < b) { \
printf( #a " < " #b "\n"); \
} \
else { \
printf( #a " = " #b "\n" ); \
}
int main()
{
signed int a = -1;
unsigned int b = 2;
signed short c = -1;
unsigned short d = 2;
PRINT_COMPARE_RESULT(a,b);
PRINT_COMPARE_RESULT(c,d);
return 0;
}
结果如下:
a > b
c < d
我的平台是Linux,我的gcc版本是4.4.2。 我对第二行输出感到惊讶。 第一行输出是由整数提升引起的。但为什么第二行的结果不同呢?
以下规则来自 C99 标准:
如果两个操作数具有相同的类型,则不需要进一步转换。 否则,如果两个操作数都是有符号整数类型或者都是无符号整数类型 整数类型,整数转换等级较小的类型的操作数为 转换为具有更高等级的操作数类型。
否则,如果具有无符号整数类型的操作数的等级更大或 等于另一个操作数的类型的等级,然后操作数 有符号整数类型转换为无符号操作数类型 整数类型。
否则,如果有符号整型操作数的类型可以表示 操作数类型的所有值均为无符号整数类型,则 无符号整数类型的操作数被转换为 有符号整数类型的操作数。
否则,两个操作数都转换为无符号整数类型 对应有符号整型操作数的类型。
我认为这两次比较应该属于同一种情况,即整数提升的第二种情况。
Please look at my test code:
#include <stdlib.h>
#include <stdio.h>
#define PRINT_COMPARE_RESULT(a, b) \
if (a > b) { \
printf( #a " > " #b "\n"); \
} \
else if (a < b) { \
printf( #a " < " #b "\n"); \
} \
else { \
printf( #a " = " #b "\n" ); \
}
int main()
{
signed int a = -1;
unsigned int b = 2;
signed short c = -1;
unsigned short d = 2;
PRINT_COMPARE_RESULT(a,b);
PRINT_COMPARE_RESULT(c,d);
return 0;
}
The result is the following:
a > b
c < d
My platform is Linux, and my gcc version is 4.4.2.
I am surprised by the second line of output.
The first line of output is caused by integer promotion. But why is the result of the second line different?
The following rules are from C99 standard:
If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.Otherwise, if the type of the operand with signed integer type can represent
all of the values of the type of the operand with unsigned integer type, then
the operand with unsigned integer type is converted to the type of the
operand with signed integer type.Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
I think both of the two comparisons should belong to the same case, the second case of integer promotion.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当您使用算术运算符时,操作数会经历两次转换。
整数提升:如果
int
可以表示该类型的所有值,则操作数将提升为int。这适用于大多数平台上的short
和unsigned short
。此阶段执行的转换是对每个操作数单独完成的,而不考虑其他操作数。 (还有更多规则,但这是适用的。)常见算术转换:如果将
unsigned int
与signed int
进行比较,由于两者都不包含对方的整个范围,并且两者具有相同的秩,因此两者都转换为unsigned
类型。此转换是在检查两个操作数的类型后完成的。显然,如果没有两个操作数,“通常的算术转换”并不总是适用。这就是为什么有两套规则。例如,一个问题是移位运算符
<<
和>>
不执行通常的算术转换,因为结果的类型应该只取决于在左侧操作数上(因此,如果您看到有人输入x << 5U
,则U
代表“不必要”)。细分:让我们假设一个具有 32 位 int 和 16 位 Short 的典型系统。
int
或unsigned int
,因此不会进行任何提升。int
无法表示unsigned
所有可能的值,而unsigned
也无法表示int
所有可能的值>,没有明显的选择。在这种情况下,两者都会转换为无符号
。if (4294967295u < 2u)
,这是 false。现在让我们尝试一下
short
:int
忠实地表示,因此两者都提升为int
。int
,所以什么也不做。if (-1 < 2)
,这是真的。编写好的代码:有一种简单的方法可以捕获代码中的这些“陷阱”。只需始终在打开警告的情况下进行编译,并修复警告即可。我倾向于编写这样的代码:
您必须注意您编写的任何代码都不会遇到其他签名与未签名的陷阱:签名溢出。例如,以下代码:
一些流行的编译器会将
(x + 100 < y + 100)
优化为(x < y)
,但这是一个故事改天。只是不要溢出您的签名号码。When you use an arithmetic operator, the operands go through two conversions.
Integer promotions: If
int
can represent all values of the type, then the operand is promoted to int. This applies to bothshort
andunsigned short
on most platforms. The conversion performed on this stage is done on each operand individually, without regard for the other operand. (There are more rules, but this is the one that applies.)Usual arithmetic conversions: If you compare an
unsigned int
against asigned int
, since neither includes the entire range of the other, and both have the same rank, then both are converted to theunsigned
type. This conversion is done after examining the type of both operands.Obviously, the "usual arithmetic conversions" don't always apply, if there are not two operands. This is why there are two sets of rules. One gotcha, for example, is that shift operators
<<
and>>
don't do usual arithmetic conversions, since the type of the result should only depend on the left operand (so if you see someone typex << 5U
, then theU
stands for "unnecessary").Breakdown: Let's assume a typical system with 32-bit int and 16-bit short.
int
orunsigned int
, no promotions are done.int
can't represent all possible values ofunsigned
, andunsigned
can't represent all possible values ofint
, there is no obvious choice. In this case, both are converted tounsigned
.if (4294967295u < 2u)
, which is false.Now let's try it with
short
:int
, both are promoted toint
.int
, so nothing is done.if (-1 < 2)
, which is true.Writing good code: There's an easy way to catch these "gotchas" in your code. Just always compile with warnings turned on, and fix the warnings. I tend to write code like this:
You have to watch out that any code you do write doesn't run into the other signed vs. unsigned gotcha: signed overflow. For example, the following code:
Some popular compilers will optimize
(x + 100 < y + 100)
to(x < y)
, but that is a story for another day. Just don't overflow your signed numbers.摘自 C++ 标准:
实际上,这意味着,如果类型
int
可以覆盖您正在处理的整个值集,则所有操作(在列表中的类型上)实际上都是在类型int
上求值,否则在int 上执行。代码>无符号整数。
在第一种情况下,这些值将作为
unsigned int
进行比较,因为其中一个是unsigned int
,这就是为什么 -1 “大于”2。在第二种情况下,将 a 值作为有符号整数进行比较,因为int
涵盖了short
和unsigned Short
的整个域,因此 -1 小于 2。(背景故事:实际上,所有这些关于覆盖所有内容的复杂定义这种情况导致编译器实际上可以忽略(!):)后面的实际类型,而只关心数据大小。)
Taken from the C++ standard:
In practice it means, that all operations (on the types in the list) are actually evaluated on the type
int
if it can cover the whole value set you are dealing with, otherwise it is carried out onunsigned int
.In the first case the values are compared as
unsigned int
because one of them wasunsigned int
and this is why -1 is "greater" than 2. In the second case the values a compared as signed integers, asint
covers the whole domain of bothshort
andunsigned short
and so -1 is smaller than 2.(Background story: Actually, all this complex definition about covering all the cases in this way is resulting that the compilers can actually ignore the actual type behind (!) :) and just care about the data size.)
C++ 的转换过程被描述为通常的算术转换。但是,我认为最相关的规则位于子引用部分 conv.prom: Integral促销4.6.1:
有趣的是使用了“can”这个词,我认为这表明这种提升是由编译器自行决定执行的。
我还发现 this C 规范片段暗示了促销的省略:
有还要考虑“rank”的定义。规则列表相当长,但当它适用于这个问题时,“排名”很简单:
The conversion process for C++ is described as the usual arithmetic conversions. However, I think the most relevant rule is at the sub-referenced section conv.prom: Integral promotions 4.6.1:
The funny thing there is the use of the word "can", which I think suggests that this promotion is performed at the discretion of the compiler.
I also found this C-spec snippet that hints at the omission of promotion:
There is also the definition of "rank" to be considered. The list of rules is pretty long, but as it applies to this question "rank" is straightforward: