繁体   English   中英

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

[英]What is the difference between literals and variables in C (signed vs unsigned short ints)?

我在“ 计算机系统:程序员的角度 ”一书中看到了以下代码,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所说,这种困境可以通过将short更改为int来解决。 但是,怎么能解释这种现象呢? 换句话说,4个字节中的-10xff ff ff ff ,2个字节中的-10xff ff 将它们作为2s补码解释为unsigned ,它们具有429496729565535对应值。 它们都不小于0 ,我认为在这两种情况下,输出都需要为-1 >= 0u ,即x >= u

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

简而言之:

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

对于int:

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

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

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

对于任何C表达式,其中有一个已签名的操作数和一个未被取消的操作数,balacing规则(正式地说: 通常的算术转换 )会隐式地将签名类型转换为unsigned。

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

否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。

例如,对于标准二进制补码系统上的32位整数,无符号整数的最大值为2^32 - 1 (4294967295,limit.h中的UINT_MAX)。 超过最大值的是2^32 并且-1 + 2^32 = 4294967295 ,因此文字-1转换为unsigned int,值为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)。

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

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

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

如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int。 这些被称为整数促销。 整数促销不会更改所有其他类型。


你可以在诸如godbolt之类的东西上更容易地玩这个, 其功能是返回一个或零 只需查看编译器输出即可查看最终发生的情况。

#define mytype short

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

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

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

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

0u不是unsigned short ,它是unsigned int

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

正如Jens Gustedt所回答的,

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

实质上是做什么的

如果类型具有不同的宽度(更准确地说是标准调用转换排名),那么如果两个类型具有相同的宽度,它将转换为更宽的类型,除了非常奇怪的体系结构之外,它们的无符号赢得签名到值的无符号转换-1无论何种类型,总是会产生无符号类型的最高可表示值。

可以在这里找到他写的更具说明性的博客。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM