8

我在Computer Systems: A Programmer's Perspective, 2/E 一书中看到了以下代码。这很好用并创建了所需的输出。输出可以通过有符号和无符号表示的差异来解释。

#include<stdio.h>
int main() {
    if (-1 < 0u) {
        printf("-1 < 0u\n");
    }
    else {
        printf("-1 >= 0u\n");
    }
    return 0;
}

上面的代码产生-1 >= 0u了,但是,与上面相同的下面的代码没有!换句话说,

#include <stdio.h>

int main() {

    unsigned short u = 0u;
    short x = -1;
    if (x < u)
        printf("-1 < 0u\n");
    else
        printf("-1 >= 0u\n");
    return 0;
}

产量-1 < 0u。为什么会这样?我无法解释这一点。

请注意,我见过类似的问题但它们没有帮助。

PS。正如@Abhineet 所说,可以通过更改shortint. 然而,如何解释这种现象呢?换句话说,-1在 4 个字节中是0xff ff ff ff,在 2 个字节中是0xff ff。给定它们作为被解释为 的 2s 补码,它们具有和unsigned的对应值。它们都不少于,我认为在这两种情况下,输出都必须是,即。4294967295655350-1 >= 0ux >= u

在小端英特尔系统上的示例输出:

简而言之:

-1 < 0u
u =
 00 00
x =
 ff ff

对于整数:

-1 >= 0u
u =
 00 00 00 00
x =
 ff ff ff ff
4

4 回答 4

10

上面的代码产生 -1 >= 0u

所有整数文字(数字常量)都有一个类型,因此也有一个符号。默认情况下,它们是int已签名的类型。附加u后缀时,会将文字转换为unsigned int.

对于任何有一个有符号操作数和一个无符号操作数的 C 表达式,平衡规则(正式地:通常的算术转换)隐式地将有符号类型转换为无符号。

从有符号到无符号的转换是明确定义的(6.3.1.3):

否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。

例如,对于标准二进制补码系统上的 32 位整数,无符号整数的最大值为2^32 - 1(4294967295,limits.h 中的 UINT_MAX)。比最大值大一2^32。并且-1 + 2^32 = 4294967295,因此文字-1被转换为具有值的无符号整数4294967295。大于 0。


但是,当您将类型切换为 short 时,您最终会得到一个小的整数类型。这是两个示例之间的区别。每当小整数类型是表达式的一部分时,整数提升规则会隐式将其转换为更大的 int (6.3.1.1):

如果 int 可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为 int;否则,它将转换为无符号整数。这些被称为整数促销。整数提升不会改变所有其他类型。

如果short小于int给定平台(如 32 位和 64 位系统上的情况),则任何shortorunsigned short将因此始终转换为int,因为它们可以放入其中。

因此,对于表达式if (x < u),您实际上最终会得到if((int)x < (int)u)which 的行为符合预期(-1 小于 0)。

于 2015-10-26T07:29:42.540 回答
3

您遇到了 C 的整数提升规则。

小于类型的运算符int自动将其操作数提升为intor unsigned int。有关更详细的解释,请参阅评论。如果之后类型仍然不匹配(例如 unsigned int 与 int),则二进制(双操作数)运算符还有一个步骤。我不会尝试更详细地总结规则。 见伦丁的回答

这篇博客文章更详细地介绍了这一点,并提供了一个与您类似的示例:signed 和 unsigned char。它引用了 C99 规范:

如果一个 int 可以表示原始类型的所有值,则将该值转换为 int;否则,它将转换为无符号整数。这些被称为整数促销。整数提升不会改变所有其他类型。


你可以在 Godbolt 之类的东西上更轻松地使用它,使用返回 one 或 zero 的函数。只需查看编译器输出,看看最终会发生什么。

#define mytype short

int main() {
    unsigned mytype u = 0u;
    mytype x = -1;
    return (x < u);
}
于 2015-10-26T07:05:53.753 回答
2

除了您似乎假设的以外,这不是类型的特定宽度的属性,这里是 2 字节与 4 字节,而是要应用的规则的问题。整数提升规则声明short和在相应值范围适合的所有平台上unsigned short转换为. 由于这里是这种情况,因此两个都被保留并获得 type 。完全可以按原样表示。所以测试结果小于。intintint-1int0-10

-1在针对0u公共转换进行测试的情况下,选择unsigned类型作为两者都转换为的公共类型。-1转换为是大于unsigned的值。UINT_MAX0u

这是一个很好的例子,为什么你不应该使用“窄”类型来做算术或比较。仅当您有服务器大小限制时才使用它们。简单变量很少会出现这种情况,但主要是对于大型数组,您可以真正从存储在窄类型中获益。

于 2015-10-26T07:18:10.343 回答
0

0u不是unsigned short,是unsigned int

编辑:: 对行为的解释, 如何进行比较?

正如 Jens Gustedt 所回答的,

这被标准称为“通常的算术转换”,当两个不同的整数类型作为同一运算符的操作数出现时适用。

本质上是做什么的

如果类型具有不同的宽度(更准确地说是标准所说的转换等级),那么如果两种类型具有相同的宽度,则它将转换为更宽的类型,除了非常奇怪的架构之外,它们中的无符号赢得了值 -1 的有符号到无符号转换无论哪种类型,总是会产生无符号类型的最高可表示值。

他写的更多解释性博客可以在这里找到。

于 2015-10-26T06:58:03.807 回答