类型转换 - unsigned to signed int/char

use*_*685 73 c types type-conversion integer-promotion signedness

我试过执行以下程序:

#include <stdio.h>

int main() {
    signed char a = -5;
    unsigned char b = -5;
    int c = -5;
    unsigned int d = -5;

    if (a == b)
        printf("\r\n char is SAME!!!");
    else
        printf("\r\n char is DIFF!!!");

    if (c == d)
        printf("\r\n int is SAME!!!");
    else
        printf("\r\n int is DIFF!!!");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

对于这个程序,我得到输出:

char是DIFF !!! int是相同的!

为什么我们两者都有不同的输出?
输出应该如下?

char是相同的!int是相同的!

一个键盘连接.

Lun*_*din 81

这是因为C中的各种隐式类型转换规则.C程序员必须知道其中有两个:通常的算术转换整数提升(后者是前者的一部分).

在char情况下,你有类型(signed char) == (unsigned char).这些都是小整数类型.其他这样的小整数类型是boolshort.该整数提升规则指出,当一个小的整数类型是操作的操作数,它的类型将得到提升到int,这是签署.无论类型是签名还是未签名,都会发生这种情况.

在这种情况下signed char,符号将被保留,并将被提升为int包含值-5 的符号.在它的情况下unsigned char,它包含一个值251(0xFB).它将被提升为int包含相同值的.你结束了

if( (int)-5 == (int)251 )
Run Code Online (Sandbox Code Playgroud)

在整数的情况下,您有类型(signed int) == (unsigned int).它们不是小整数类型,因此整数促销不适用.相反,它们通过通常的算术转换来平衡,该转换声明如果两个操作数具有相同的"等级"(大小)但是具有不同的符号,则将带符号的操作数转换为与无符号操作数相同的类型.你结束了

if( (unsigned int)-5 == (unsigned int)-5)
Run Code Online (Sandbox Code Playgroud)

  • 稍微不准确:如果`int`不足以表示较小转换级别的所有值,也可以提升为`unsigned int`; 例如,假设`int`和`short`都是16位类型; 那么,`unsigned short`到`int`的转换一般不能保留值,所以我们用`unsigned int`代替 (6认同)

zmb*_*mbq 36

好问题!

int比较有效,因为这两个整数包含完全相同的位,所以他们基本上是相同的.但那是什么char

啊,C 在各种场合暗中提升chars int.这是其中之一.你的代码说if(a==b),但编译器实际上转向的是:

if((int)a==(int)b) 
Run Code Online (Sandbox Code Playgroud)

(int)a是-5,但是(int)b是251.这些肯定是不一样的.

编辑:正如@ Carbonic-Acid指出的那样,(int)b只有当a char为8位长时才是251 .如果int是32位长,(int)b则为-32764.

REDIT:如果一个字节长度不是8位,那么就会有很多评论讨论答案的本质.在这种情况下唯一的区别是,(int)b不是251而是一个不同的数,而不是-5.这与仍然非常酷的问题无关.

  • 在过去的40年里,你看到一个超过8位的字节? (11认同)
  • @ user2522685因为C语言需要它,而且C语言不合理,一致也不符合逻辑. (6认同)
  • 没错,但我宁愿不要混淆OP.我会补充一点. (4认同)
  • @zmbq:[DSPs](http://en.wikipedia.org/wiki/Digital_signal_processor)每个字节不需要8位,Unisys仍然在大型机业务中,那里有一些奇怪的Forth处理器(不需要但是,如果你看起来很难,你仍然可以找到今天生产的这种系统 (3认同)
  • 还要注意答案是误导性的 - "int"比较不起作用,因为变量包含相同的位,但因为它们的值在转换后比较相等; C语言大多不关心表示 - 即使使用符号幅度表示,`(unsigned)-1 == UINT_MAX`也成立,其中,与二进制补码相反,转换不是noop (3认同)

Nob*_*lis 21

欢迎整数推广.如果我可以从网站引用:

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

当你进行这样的比较时,C可能会让人感到困惑,我最近对以下挑逗的一些非C编程朋友感到困惑:

#include <stdio.h>
#include <string.h>

int main()
{
    char* string = "One looooooooooong string";

    printf("%d\n", strlen(string));

    if (strlen(string) < -1) printf("This cannot be happening :(");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

确实打印This cannot be happening :(并且似乎表明25小于-1!

然而,在下面发生的是-1表示为无符号整数,由于底层位表示在32位系统上等于4294967295.自然25小于4294967295.

但是,如果我们显式地将size_t返回的类型转换strlen为有符号整数:

if ((int)(strlen(string)) < -1)
Run Code Online (Sandbox Code Playgroud)

然后它会将25与-1进行比较,所有这些都将与世界相得益彰.

一个好的编译器应该警告你有关无符号整数和有符号整数之间的比较,但它仍然很容易被忽略(特别是如果你不启用警告).

这对Java程序员来说尤其令人困惑,因为所有原始类型都有签名.以下是James Gosling(Java的创建者之一)在这个问题上所说的话:

Gosling:对于我作为一名语言设计师而言,我现在并不像以前那样真实地认为,"简单"真正意义上的结果是我可以期待J. Random Developer在他的脑海中保留这个规范.这个定义说,例如,Java不是 - 实际上很多这些语言都有很多极端情况,这些都是没人真正理解的.测试任何C开发人员关于unsigned的问题,很快你就会发现几乎没有C开发人员真正理解无符号算法是什么,无符号算术是什么.这样的事情让C变得复杂.我认为Java的语言部分非常简单.你必须查找的库.


ams*_*ams 10

十六进制表示-5是:

  • 8位,2位补码signed char:0xfb
  • 32位,二位补码signed int:0xfffffffb

将有符号数转换为无符号数时,反之亦然,编译器确实没有... 怎么办?数字是可转换的,或者不是,在这种情况下,未定义或实现定义的行为如下(我实际上没有检查哪个),并且最有效的实现定义的行为是什么都不做.

所以,十六进制表示(unsigned <type>)-5是:

  • 8位,unsigned char:0xfb
  • 32位,unsigned int:0xfffffffb

看起来熟悉?它们与签名版本的位数相同.

当你编写if (a == b),在哪里a和什么b类型时char,编译器实际需要读取的是什么if ((int)a == (int)b).(这是其他人正在抨击的"整数推广".)

所以,当我们转换会发生什么charint

  • 8位signed char到32位signed int:0xfb- >0xfffffffb
    • 嗯,这是有道理的,因为它匹配-5上面的表示!
    • 它被称为"符号扩展",因为它将字节的最高位"符号位"向左复制到新的更宽的值中.
  • 8位unsigned char到32位signed int:0xfb- >0x000000fb
    • 这次它执行"零扩展",因为源类型是无符号的,因此没有要复制的符号位.

所以,a == b确实0xfffffffb == 0x000000fb=>不匹配!

并且,c == d确实0xfffffffb == 0xfffffffb=>匹配!