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) &lt; 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。

(已添加)请注意,当您对这些类型进行比较时,您会得到更多意想不到的结果。让我们用&lt;来考虑上面的例子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"

由于2uu 后缀显式构成unsigned,因此适用相同的规则。而用 C++ 编写时比较 -4 的结果可能不是你所期望的-4 &lt; 2u...

【讨论】:

+1 不错的完整答案。顺便说一句:不妨注意这是针对 4 字节整数和 8 字节长的。其他的可能不同。 @chux 我已将我的示例简化为标准中的 3 个条款,并且还指出了平台依赖项的位置。 @chux 我使用的平台(Coliru 在线编译器)是 64 位的,其中sizeof(long) == sizeof(long long)。您可能使用 32 位,其中sizeof(long) == sizeof(int),因此第 2 和第 3 种情况互换。标准是在可以表示的范围内表述的,这就是我在回答中使用“在这个平台上”这个短语的原因。 也许使用具有更明确的相对等级的类型,例如 shortlonglong long,并避免使用大小 int 以减少您的好答案中的歧义? @chux 除了在&lt;= 方面的相对排名之外,标准并没有为整数类型提供许多绝对大小保证。此外,对于charshort,在算术转换之前还有对int积分提升。我认为这只会使答案复杂化。标准推理+代码示例应该足够清晰,可以解决每个实际案例。【参考方案2】:

请注意,C++11 标准在这里没有讨论较大或较小的类型,它讨论的是具有较低或较高等级的类型。

考虑long intunsigned int 的情况,它们都是32 位的。 long int 的等级比unsigned int 大,但由于long intunsigned int 都是32 位的,long int 不能代表unsigned int 的所有值。

因此我们陷入了最后一种情况(C++11:5.6p9):

否则,两个操作数都应转换为对应的无符号整数类型 带符号整数类型的操作数的类型。

这意味着long intunsigned 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++ 隐式转换(有符号 + 无符号)的主要内容,如果未能解决你的问题,请参考以下文章

关于int转换成unsigned的隐式转换问题?

符号:隐式字符串转换在 JavaScript 中是如何工作的?

为啥隐式符号到字符串的转换会导致 JavaScript 中的 TypeError?

C中的有符号到无符号转换 - 它总是安全的吗?

隐式转换将符号性“int”更改为“unsigned int”

P4 开发实践 — 编程基础 — 数据类型