C中的文字和变量之间有什么区别(有符号和无符号短整数)?

时间:2015-10-26 06:47:15

标签: c bit-manipulation twos-complement unsigned-integer integer-promotion

我在计算机系统:程序员的角度,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。为什么会这样?我无法解释这一点。

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

PS。正如@Abhineet所说,通过将short更改为int可以解决这一难题。但是,怎么能解释这种现象呢?换句话说,4个字节中的-10xff ff ff ff,而2个字节中的0xff ffunsigned。将它们视为2s补码,将其解释为4294967295,它们具有655350的对应值。它们都不低于-1 >= 0u,我认为在这两种情况下,输出都需要x >= u,即-1 < 0u u = 00 00 x = ff ff

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

简而言之:

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

对于int:

Appdelegate.m

4 个答案:

答案 0 :(得分:10)

  

上面的代码产生-1&gt; = 0u

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

对于任何C表达式,其中有一个已签名的操作数和一个未被取消的操作数,balacing规则(正式:the usual arithmetic conversions)会隐式地将签名类型转换为无符号。

从有符号到无符号的转换是明确定义的(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;   否则,它将转换为unsigned int。这些被称为   整数促销。所有其他类型的整数不变   促销。

如果short小于给定平台上的int(就像在32位和64位系统上的情况一样),那么任何shortunsigned short都将始终获得转换为int,因为它们可以放在一个内。

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

答案 1 :(得分:3)

您正在使用C的整数提升规则。

类型小于int的运算符会自动将其操作数提升为intunsigned int。有关详细说明,请参阅注释。如果类型在此之后仍然不匹配(例如unsigned int与int),则二进制(双操作数)运算符还有一个步骤。我不会试图更详细地总结规则。 请参阅Lundin的回答

This blog post更详细地介绍了这一点,并提供了与您类似的示例:signed和unsigned char。它引用了C99规范:

  

如果int可以表示原始类型的所有值,则值为   转换为int;否则,它将转换为unsigned int。   这些被称为整数促销。所有其他类型都保持不变   通过整数促销。

你可以更容易地在诸如godbolt之类的东西上玩这个,with a function that returns one or zero。只需查看编译器输出,看看最终会发生什么。

#define mytype short

int main() {
    unsigned mytype u = 0u;
    mytype x = -1;
    return (x < u);
}

答案 2 :(得分:2)

除了你似乎假设的,这不是类型的特定宽度的属性,这里是2字节对4字节,而是要应用的规则的问题。整数提升规则规定shortunsigned short在相应值范围符合int的所有平台上转换为int。由于这是这种情况,因此将保留并获取类型int-1int中完全可以表示0。因此,-1中的测试结果小于0

如果针对-10u进行测试,则公共转换选择unsigned类型作为转换两者的公共类型。转换为-1的{​​{1}}的值为unsigned,大于UINT_MAX

这是一个很好的例子,为什么你永远不应该使用&#34; narrow&#34;算术或比较的类型。仅在具有服务器大小约束时才使用它们。对于简单变量,这种情况很少发生,但主要是对于大型数组,您可以通过窄类型存储获得真正的数据。

答案 3 :(得分:0)

  

0u不是unsigned short,而是unsigned int

编辑::对行为的解释, How comparison is performed ?

由Jens Gustedt回答,

  

这被称为&#34;通常的算术转换&#34;按标准和   只要两个不同的整数类型出现作为的操作数,就适用   同一个运营商。

     

本质上是做什么

     

如果类型有不同的宽度(更准确地说是标准   调用转换排名)然后如果两者都转换为更宽的类型   类型具有相同的宽度,除了非常奇怪的架构,   unsigned of their wins签名为无符号转换值-1   任何类型总是会产生最高的可表示价值   无符号类型。

可以找到他撰写的更具说明性的博客here