【问题标题】:How int is converted to char and how char is converted to int?int如何转换为char,char如何转换为int?
【发布时间】:2017-03-21 15:12:14
【问题描述】:

在以下示例中,打印全为 1 的字节的位表示:

#include <stdio.h>
int main (void)
{
  char c = 255;
  char z;
  for (int i = 7; i >= 0; i--) {
    z = 1 << i;
    if ((z & c) == z) printf("1"); else printf("0");
  }
  printf("\n");
  return 0;
}

输出为11111111

现在我们把char c改成int c,这样例子就变成了:

#include <stdio.h>
int main (void)
{
  int c = 255;
  char z;
  for (int i = 7; i >= 0; i--) {
    z = 1 << i;
    if ((z & c) == z) printf("1"); else printf("0");
  }
  printf("\n");
  return 0;
}

现在输出是01111111

为什么输出不同?

更新

编译如下test.c:

#include <stdio.h>
int main(void)
{
  char c=-1;
  printf("%c",c);
  return 0;
}

$ gcc test.c
$ ./a.out | od -b
0000000 377
0000001

输出为 377,表示 glibc 与 gcc 相矛盾,因为有符号字符会自动转换为无符号字符。 为什么会有这样的并发症?默认情况下让 char 无符号是合理的。有没有什么具体的原因?

【问题讨论】:

  • 如果您需要特定的位宽,请使用来自stdint.h 的固定宽度类型。并且在移位时,通常最好使用无符号类型,因为有符号整数上的某些移位/值是实现定义的,甚至会调用未定义的行为。

标签: c type-conversion implicit-conversion


【解决方案1】:
  • 这里的第一个问题是char 类型。这种类型永远不应该用于存储整数值,因为它具有实现定义的符号。这意味着它可以是有符号的也可以是无符号的,在不同的编译器上你会得到不同的结果。如果char 在给定的编译器上是无符号的,那么这段代码就会像你预期的那样运行。

    但是如果char 被签名,char c = 255; 将导致一个太大的值。然后,值 255 将以某种编译器特定的方式转换为有符号数。通常通过将原始数据值转换为等效的二进制补码。

    像 GCC 这样好的编译器会为此给出警告:“隐式常量转换中的溢出”。

    从不使用char 存储整数来解决这个错误。请改用uint8_t

  • 当您尝试在给定编译器上签名的 char 类型中存储 1 &lt;&lt; 7 时,会出现同样的问题。发生这种情况时,z 最终会变成负值 (-128)。

  • 在表达式z &amp; c 中,两个操作数都被默默地提升为int 类型。每当您使用 小整数类型(例如 char)时,大多数 C 表达式都会发生这种情况。

    &amp; 运算符不关心操作数是否有符号,它会对变量的“原始数据”值进行按位与运算。当c 是带符号的char 并且具有原始值0xFF 时,您将得到一个负数的结果,并设置了符号位。二进制补码计算机上的值 -1

所以要回答为什么在这两种情况下会得到不同的结果:

当您将类型切换为int 时,值255 将适合c,而不会转换为负值。 &amp; 操作的结果也将是一个int,并且这个int 的符号位永远不会被设置,这与char 的情况不同。

当您执行-128 &amp; 255 时,结果将是128 (0x80)。这是一个正整数。然而z 是一个负整数,其值为-128。它将被 == 运算符提升为int,但符号被保留。由于 128 不等于 -128,因此 MSB 将被打印为零。

如果您将char 切换为uint8_t,您会得到相同的结果。

【讨论】:

  • 255 &amp; -1 应改为128 &amp; -1
  • 参见 OP 中的 UPDATE
  • 为什么他们偷了char,而不是创建int8_t?用它们的名字来称呼事物不是更好吗? char 用于存储 char角色代码(即无符号数量)。
  • @IgorLiferenko "255 & -1 应该改为 128 & -1" 不,是正确的。
  • @IgorLiferenko“请参阅 OP 中的更新”。更新没有意义。你期望字符 -1 是什么?该代码只会打印废话,如果有的话,不管 char 的签名。
【解决方案2】:

编辑以澄清“默认签名”

在第一个清单中,(z == c) 测试了两个 char ;然而,在第二个清单中,(z == c) 测试了一个 char 和一个 int

为了在 charint 之间执行 &amp;== 操作,编译器将 char 扩展为int 的大小。

关于第 7 位(第 8 位):

如果您的编译器默认认为 char 是无符号的,则条件

(((int)(128) & (int)255) == (int)128)

将呈现 true,并打印 1。但是,在您的情况下,结果为 false,并显示 0

原因可能是您的编译器认为 char 已签名(默认情况下如 gcc)。在这种情况下,设置为 1 &lt;&lt; 7char 实际上是 -128,而在 int(至少两个字节)中,255 是正数。

(char)-128 扩展为 int 是(int)-128,因此条件

if ((z & c) == z) 

读取

if (((int)(-128) & (int)255) == (int)-128)

在这种情况下是假的。

【讨论】:

  • charnot “默认签名” 相反:默认签名是实现定义的。并且将太大的值转换为有符号整数也是实现定义的。作为一个小缺陷:all 排名低于int 的操作数被转换为int。这并不特定于 int 本身。
【解决方案3】:

对于 char 到 int,您必须将 char 定义为无符号,因为默认情况下 char 或任何类型都被视为 singed。

 int main (void)
 {
 int c = 255;
 unsigned char z;
 int i;
 for (i = 7; i >= 0; i--) {
 z = 1 << i;
 if ((z & c) == z) printf("1"); else printf("0");
 }
 printf("\n");
 return 0;
 }

【讨论】:

  • 不,char 具有实现定义的签名。它可能是有符号的或无符号的,这取决于编译器。
  • @Lundin 谢谢,我不知道这一点,因为我只使用 gcc 编译器,并且在 gcc 中提到“默认情况下 char 是 singed”
猜你喜欢
  • 1970-01-01
  • 2011-04-09
  • 2018-05-15
  • 2012-06-06
  • 2012-06-02
  • 2015-04-07
  • 1970-01-01
  • 2014-09-20
  • 2011-12-15
相关资源
最近更新 更多