【问题标题】:Simple Character Interpretation In CC中的简单字符解释
【发布时间】:2012-02-03 07:56:11
【问题描述】:

这是我的代码

 #include<stdio.h>

 void main()
 {
     char ch = 129;
     printf("%d", ch);
 }

我得到的输出为 -127。什么意思?

【问题讨论】:

  • 这是我不明白的部分:129 是一个指向 ü 的代码:Seconded:编译器如何将数字 129 存储在“char”的内存中 - 如果 char 本身可以保存直到 128 个值!!存储或击中字符 ü 它必须首先看到 129:它没有存储 129 的内存::这是怎么发生的?
  • 我编辑了我的评论以详细解释处理器(而不是编译器)存储闪亮 129 的过程。(使用 ascii 艺术!!)

标签: c format char


【解决方案1】:

表示char是一个8位变量,只能保存2^8 = 256个值,因为声明是char chch是一个signed变量,也就是说它可以存储127负值和正值。当您要求超过 127 时,该值从 -128 开始。

把它想象成一些街机游戏,你从屏幕的一侧移动到另一侧:

ch = 50;

                                    ----->                        50 is stored
      |___________________________________|___________|           since it fits
    -128                       0         50          127          between -127
                                                                  and 128

ch = 129;

                                                    ---           129 goes over
      -->                                                         127 by 2, so
      |__|____________________________________________|           it 'lands' in
    -128  -127                 0                     127          -127

但是!!你不应该依赖它,因为它是未定义的行为!


为了纪念 Luchian Grigore,以下是正在发生的事情的位表示:

char 是一个保存 8 位或一个字节的变量。因此,我们有 8 个 0 和 1 努力代表您想要的任何值。如果charsigned 变量,它将表示它是正数还是负数。您可能读过有关表示符号的一位,这是对真实过程的抽象;事实上,它只是在电子领域实施的首批解决方案之一。但是这样一个微不足道的方法有一个问题,你会有2种方式来表示0(+0和-0):

0 0000000     ->    +0        1 0000000     ->    -0                    
^                             ^ 
|_ sign bit 0: positive       |_ sign bit 1: negative

保证不一致!!因此,一些非常聪明的人想出了一个称为 Ones' Complement 的系统,它将一个负数表示为其正数的否定(非运算):

01010101      ->    +85
10101010      ->    -85

这个系统...有同样的问题。 0 可以表示为 00000000 (+0) 和 11111111 (-0)。然后出现了一些更聪明的人,他们创建了二的补码,它将保留早期方法的否定部分,然后添加 1,因此删除了那个讨厌的 -0 并为我们的范围提供了一个闪亮的新数字:-128!。那么我们的范围现在看起来如何?

00000000     +0
00000001     +1
00000010     +2
...
01111110     +126
01111111     +127
10000000     -128
10000001     -127
10000010     -126
...
11111110     -2
11111111     -1

因此,当我们的小处理器尝试将数字添加到我们的变量时,这应该可以让您了解发生了什么:

 0110010     50                   01111111     127
+0000010    + 2                  +00000010    +  2
 -------     --                   --------     ---
 0110100     52                   10000001    -127
     ^                                  ^       ^
     |_ 1 + 1 = 10          129 in bin _|       |_ wait, what?!

是的,如果您查看上面的范围表,您可以看到最多 127 (01111111) 二进制文件很好而且花花公子,没有什么奇怪的事情发生,但是在第 8 位设置为 -128 (@987654337 @) 解释的数字不再保持其二进制大小,而是保持二进制补码表示。这意味着,二进制表示,变量中的位,1 和 0,我们心爱的char 的心脏,确实有一个 129... 它在那里,看看它!但是邪恶的处理器读取到微不足道的 -127 导致变量 HAD 为signed 破坏了它在欧几里得空间中通过实数线进行臭味移位的所有积极潜力。

【讨论】:

  • 这是未定义的行为。这可能适用于跑步,但这不是规则。请不要说不真实的事情。
  • @LuchianGrigore:问题很清楚“我得到的输出为 -127 是什么意思?”我回答了他为什么得到-127。我没有说这是一个好的做法,或者他应该一有机会就实施。
  • 你没有。你只是假设这就是发生的事情。你不知道他的平台,或者他的编译器。任何事情都有可能发生。这就是UB的意思。剩下的只是猜测。
  • 另外,129 goes over 127 by 2, so it lands in -127 是一个非常非常错误的解释。你没有说任何关于编码、位表示或任何东西(这可能会更好,尽管仍然是 UB)。
【解决方案2】:

这意味着您遇到了未定义的行为

任何结果都是可能的。

char ch=129; 是 UB,因为对于您的特定设置,129 不是 char 的可表示值。

【讨论】:

  • 它不是未定义的,因为 129 不能表示为字符。这是未定义的,因为 129 不能存储在 7 位中
  • @whitelionV 该标准没有提到比特。它说尝试存储超出该类型可表示范围的任何内容都是未定义的行为,这就是我所说的。这是真的。
  • 标准确实说 char 将至少保存 8 位,并且按照惯例(而不是通过实现),您应该使用它来保存字符。如果任何处理器将 unsigned char 编译为 16 位变量,它将打印 129 并一直打印到 32,767
  • 这种特殊情况 129 在char 中无法表示,因为这里char 显然是一个8 位有符号类型。但是,如果char 大于 8 位或无符号(根据 C 标准,这两者都是允许的),则 129 可以在char 中表示。所以,说 129 在char 中有条件地表示是正确的。
【解决方案3】:

您的char 很可能是使用Two's complement 存储的8 位有符号整数。这样的变量只能表示 -128 和 127 之间的数字。如果你执行“127+1”,它会环绕到 -128。所以 129 等价于 -127。

【讨论】:

    【解决方案4】:

    这是因为 char 被编码在一个字节上,因此是 8 位数据。

    事实上,char 有一个编码为 7 位的值,其中有一位用于符号,unsigned char 有 8 位数据作为其值。

    这意味着:

    分别取abcdefgh为8位(a为最左位,h为最右位),值以a为符号,bcdefgh为二进制编码为实数:

    42(十进制)= 101010(二进制) 存储为: abcdefgh 00101010

    当使用内存中的这个值时: a 为 0:数字为正,bcdefgh = 0101010:值为 42

    当你输入 129 时会发生什么:

    129(十进制)= 10000001(二进制) 存储为: abcdefgh 10000001

    当使用内存中的这个值时: a 为 0 :数字为负,我们应该减一并将值中的所有位反转,因此 (bcdefgh - 1) 反转 = 1111111 :值为 127 数字是-127

    【讨论】:

      【解决方案5】:

      在您的系统上:char 129 与 8 位有符号整数 -127 具有相同的位。 无符号整数从 0 到 255,有符号整数从 -128 到 127。

      相关(C++):

      您可能也有兴趣阅读What is an unsigned char? 的最佳最佳答案

      正如@jmquigley 指出的那样。这是严格未定义的行为,您不应依赖它。 Allowing signed integer overflows in C/C++

      【讨论】:

      • 我认为您的意思是签名字符从 -128 变为 +127?
      • 不保证是相同的位。这是未定义的行为。
      • 我不确定该链接中所说的最佳答案是否适用于 C。如果我错了,请纠正我,但我认为 C 中的字符文字是 int 类型,但在 C++ 中是 char 类型?
      【解决方案6】:

      char 类型是一个 8 位有符号整数。如果您在 two's complement 有符号表示中解释无符号字节 129 的表示,您会得到 -127。

      【讨论】:

      • char 不一定是有符号的,无论是有符号还是无符号都是实现定义的行为。然而,在这个特定的实现中,它似乎是签名的。
      【解决方案7】:

      char 的类型可以是signedunsigned,这取决于编译器。大多数编译器都将它作为`签名。

      在您的情况下,编译器将整数 129 静默转换为其有符号变量,并将其放入一个 8 位字段中,生成 -127。

      【讨论】:

        【解决方案8】:

        char 是 8 位,有符号。它只能保存值 -128 到 127。当您尝试将 129 分配给它时,您会得到您看到的结果,因为指示签名的位被翻转。另一种思考方式是数字“环绕”。

        【讨论】:

          【解决方案9】:

          一个普通的char 是有符号还是无符号,是实现定义的行为。这是 C 语言中一个非常愚蠢、晦涩的规则。 intlong 等保证被签名,但char 可以被签名无符号,这取决于编译器的实现。

          在您的特定编译器上,char 显然已签名。这意味着,假设您的系统使用二进制补码,它可以保存 -128 到 127 的值。

          您尝试将值 129 存储在这样的变量中。这会导致未定义的行为,因为您会遇到整数溢出。严格来说,当你这样做时,任何事情都可能发生。该程序可以打印“hello world”或开始射击无辜的旁观者,并且仍然符合 ISO C。实际上,大多数(所有?)编译器会将这种未定义的行为实现为“环绕”,如其他答案中所述。

          总而言之,您的代码依赖于标准未明确定义的两种不同行为。了解这种不可预测代码的结果如何以某种方式结束的价值有限。这里重要的是要认识到代码是晦涩难懂的,并学习如何以不晦涩的方式编写代码。

          例如,代码可以重写为:

          unsigned char ch = 129;

          甚至更好:

          #include <stdint.h>
          ...
          uint8_t ch = 129;
          

          根据经验,请务必遵守 MISRA-C:2004 中的这些规则:

          6.1 普通 char 类型只能用于字符值的存储和使用。

          6.2 signedunsigned char 类型只能用于数值的存储和使用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-09-02
            • 2010-09-06
            • 2019-01-25
            • 2011-01-14
            • 1970-01-01
            • 1970-01-01
            • 2014-07-06
            • 2015-02-24
            相关资源
            最近更新 更多