C++ 隐式转换(有符号 + 无符号)
Posted
技术标签:
【中文标题】C++ 隐式转换(有符号 + 无符号)【英文标题】:C++ Implicit Conversion (Signed + Unsigned) 【发布时间】:2013-07-23 20:36:04 【问题描述】:我了解,关于隐式转换,如果我们有一个无符号类型操作数和一个有符号类型操作数,并且无符号操作数的类型与有符号操作数的类型相同(或大于),则有符号操作数将被转换为无符号。
所以:
unsigned int u = 10;
signed int s = -8;
std::cout << s + u << std::endl;
//prints 2 because it will convert `s` to `unsigned int`, now `s` has the value
//4294967288, then it will add `u` to it, which is an out-of-range value, so,
//in my machine, `4294967298 % 4294967296 = 2`
我不明白 - 我读到如果有符号操作数的类型大于无符号操作数:
如果无符号类型中的所有值都适合较大的类型,则无符号操作数将转换为有符号类型
如果无符号类型中的值不适合更大的类型,则有符号操作数将转换为无符号类型
所以在下面的代码中:
signed long long s = -8;
unsigned int u = 10;
std::cout << s + u << std::endl;
u
将被转换为signed long long,因为int 值可以适合signed long long??
如果是这样,在什么情况下较小的类型值不适合较大的类型值?
【问题讨论】:
你的第一个例子不是很好,因为结果应该是2。如果您想在计算后查看该值是否有符号,请使用结果为负的值。 【参考方案1】:标准中的相关引用:
5 个表达式 [expr]
10 许多二元运算符需要算术或运算操作数 枚举类型导致转换并产生类似的结果类型 大大地。目的是产生一个通用类型,这也是 结果。这种模式称为通常的算术转换, 定义如下:
[关于等号类型或等号类型省略的2条子句]
— 否则,如果具有无符号整数类型的操作数具有秩 大于或等于另一个操作数的类型的等级, 带符号整数类型的操作数应转换为 无符号整数类型的操作数。
——否则,如果类型 带符号整数类型的操作数可以表示所有值 无符号整数类型的操作数的类型,操作数 具有无符号整数类型的应转换为 带符号整数类型的操作数。
——否则,两个操作数都应为 转换为类型对应的无符号整数类型 带符号整数类型的操作数。
让我们在sizeof(int) < sizeof(long) == sizeof(long long)
的系统上考虑上述 3 个子句中的每一个子句的以下 3 个示例情况(很容易适应其他情况)
#include <iostream>
signed int s1 = -4;
unsigned int u1 = 2;
signed long int s2 = -4;
unsigned int u2 = 2;
signed long long int s3 = -4;
unsigned long int u3 = 2;
int main()
std::cout << (s1 + u1) << "\n"; // 4294967294
std::cout << (s2 + u2) << "\n"; // -2
std::cout << (s3 + u3) << "\n"; // 18446744073709551614
Live example 带输出。
第一个子句:相等等级的类型,因此signed int
操作数被转换为unsigned int
。这需要一个值转换,它(使用二进制补码)给出打印值。
第二个子句:有符号类型具有更高的等级,并且(在这个平台上!)可以表示无符号类型的所有值,因此将无符号操作数转换为有符号类型,你得到-2
第三条:有符号类型再次具有更高的等级,但是(在这个平台上!)不能代表无符号类型的所有值,因此两个操作数都转换为unsigned long long
,并且在对有符号操作数进行值转换之后,你得到打印的值。
请注意,当无符号操作数足够大(例如这些示例中的 6 个)时,由于无符号整数溢出,最终结果将为所有 3 个示例给出 2。
(已添加)请注意,当您对这些类型进行比较时,您会得到更多意想不到的结果。让我们用<
来考虑上面的例子1:
#include <iostream>
signed int s1 = -4;
unsigned int u1 = 2;
int main()
std::cout << (s1 < u1 ? "s1 < u1" : "s1 !< u1") << "\n"; // "s1 !< u1"
std::cout << (-4 < 2u ? "-4 < 2u" : "-4 !< 2u") << "\n"; // "-4 !< 2u"
由于2u
由u
后缀显式构成unsigned
,因此适用相同的规则。而用 C++ 编写时比较 -4 的结果可能不是你所期望的-4 < 2u
...
【讨论】:
+1 不错的完整答案。顺便说一句:不妨注意这是针对 4 字节整数和 8 字节长的。其他的可能不同。 @chux 我已将我的示例简化为标准中的 3 个条款,并且还指出了平台依赖项的位置。 @chux 我使用的平台(Coliru 在线编译器)是 64 位的,其中sizeof(long) == sizeof(long long)
。您可能使用 32 位,其中sizeof(long) == sizeof(int)
,因此第 2 和第 3 种情况互换。标准是在可以表示的范围内表述的,这就是我在回答中使用“在这个平台上”这个短语的原因。
也许使用具有更明确的相对等级的类型,例如 short
、long
、long long
,并避免使用大小 int
以减少您的好答案中的歧义?
@chux 除了在<=
方面的相对排名之外,标准并没有为整数类型提供许多绝对大小保证。此外,对于char
和short
,在算术转换之前还有对int
的积分提升。我认为这只会使答案复杂化。标准推理+代码示例应该足够清晰,可以解决每个实际案例。【参考方案2】:
请注意,C++11 标准在这里没有讨论较大或较小的类型,它讨论的是具有较低或较高等级的类型。
考虑long int
和unsigned int
的情况,它们都是32 位的。 long int
的等级比unsigned int
大,但由于long int
和unsigned int
都是32 位的,long int
不能代表unsigned int
的所有值。
因此我们陷入了最后一种情况(C++11:5.6p9):
否则,两个操作数都应转换为对应的无符号整数类型 带符号整数类型的操作数的类型。
这意味着long int
和unsigned int
都将转换为unsigned long int
。
【讨论】:
但同时unsigned long
不能代表long int
的所有值(例如-1)。那么,为什么long int
会转换为unsigned int
而不是相反呢?
@sasha.sochka:实际上它们都转换为unsigned long int
(我最初犯了一个错误)。【参考方案3】:
signed int
不适合 unsigned long long
。所以你会有这个转换:
signed int
-> unsigned long long
.
【讨论】:
以上是关于C++ 隐式转换(有符号 + 无符号)的主要内容,如果未能解决你的问题,请参考以下文章
符号:隐式字符串转换在 JavaScript 中是如何工作的?
为啥隐式符号到字符串的转换会导致 JavaScript 中的 TypeError?