【问题标题】:Printing declared char value in C在 C 中打印声明的 char 值
【发布时间】:2014-05-16 02:51:51
【问题描述】:

我了解字符变量的范围为 (signed)-128 到 127 和 (unsigned)0 到 255

char x;
x = 128;

printf("%d\n", x); 

但它是如何工作的?为什么x 得到-128

【问题讨论】:

  • 谷歌“补码”
  • 然后谷歌“符号扩展”以了解为什么在格式字符串中提升为 int (%d) 时它保持负数。
  • 要理解为什么它保持负数,请回忆一下 C 对值的操作。 -128 的值在转换为-128 对其有效的另一种类型时保持为-128。无论表示形式(其中“符号扩展”是一个方面)
  • @JohnH "sign extension" 不相关;如果 x 为负数,则无论它如何表示,它在提升为 int 时都保持负数。由于标准规定了这一点,符号扩展就是典型实现的方式。

标签: c


【解决方案1】:

printf 是一个可变参数函数,仅提供第一个参数的确切类型。
这意味着默认提升应用于以下参数,因此所有秩小于int 的整数都被提升为intunsigned int,所有秩小于double 的浮点值都被提升为double

如果您的实现有 8 个 CHAR_BIT,并且签名了简单的 char,并且您有一个强制的 2s-complement 实现,那么您将得到

128(字面量)到 -128 (char/signed char) 到 -128 (int) 打印为 int => -128

如果所有列出的条件,但强制 2s 补码实现都得到满足,你会得到一个信号或一些实现定义的值。

否则你会得到 128 的输出,因为 128 适合 char / unsigned char

案例 2 的标准报价(感谢 Matt unearthing the right reference):

6.3.1.3 有符号和无符号整数

1 当一个整数类型的值被转换为另一个整数类型而不是_Bool时,如果 该值可以用新类型表示,它是不变的。
2 否则,如果新类型是无符号的,则通过重复添加或转换值 比新类型可以表示的最大值多减一 直到值在新类型的范围内。60)
3 否则,新类型是有符号的,值不能在其中表示;无论是 结果是实现定义的或引发了实现定义的信号。

【讨论】:

    【解决方案2】:

    这一切都与可变参数函数、默认参数提升等无关。

    假设您的系统已签名字符,则 x = 128; 正在执行超出范围的分配。其行为是 implementation-defined ;这意味着编译器可以选择一个动作,但它必须记录它的作用(因此,它是可靠的)。允许此操作包括发出信号。

    现代编译器对超出范围赋值所做的通常行为是截断值的表示以适合目标类型。

    在二进制表示中,128 是000....00010000000。 将其截断为有符号字符会给出二进制表示的有符号字符10000000。在 two's complement representation 中,所有现代 C 系统都将其用于负数,这是值 -128 的表示。 (出于对历史的好奇:在一个补码中,这是-127,在符号幅度中,这是-0,这可能是一个陷阱表示,因此会引发一个信号。

    最后,printf 准确地打印出这个字符的值-128%d 修饰符适用于 char,因为默认参数提升和 INT_MIN <= CHAR_MININT_MAX >= CHAR_MAX.;这种行为是有保证的,除非系统具有无符号的普通字符和sizeof(int)==1(确实存在,但如果你在其中,你就会知道它)。

    【讨论】:

    • 超出范围的分配是未定义的行为,不是实现定义的。根据标准,“如果在计算表达式期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义。”
    • @JimBalter,您引用的文字没有描述超出范围的分配。在评估x = 128; 中的表达式期间没有异常情况。这里的条件是从int 转换为(签名)char,由 C99 6.3.1.3 覆盖
    • 它确实描述了这一点,因为赋值是表达式。但是,6.3.1.3 似乎在这里适用,因为它专门关于不同类型的整数之间的分配。
    • 在分配发生之前int 被转换为char;在右侧的值转换为要分配的变量的类型之前,分配无法继续。
    • 点了。谢谢你的课。
    【解决方案3】:

    让我们看看128 存储为 8 位时的二进制表示:

    1000 0000

    现在让我们看看-128 存储为 8 位时的二进制表示:

    1000 0000

    您当前设置的char 的标准看起来是signed char(注意这不在c 标准中,如果您不相信我,请查看here),因此当您分配128x 的值是您为它分配的值 1000 0000,因此当您编译并打印它时,它会打印出该二进制表示的有符号值(意思是 -128)。

    事实证明,我的环境与假设 char 实际上是 signed char 相同。正如预期的那样,如果我将x 转换为unsigned char,那么我会得到128 的预期输出:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
      char x;
      x = 128;
      printf("%d %d\n", x, (unsigned char)x);
      return 0;
    }
    

    给我-128 128的输出

    希望这会有所帮助!

    【讨论】:

    • 次要 nitpick:“假设 char 实际上是已签名的 char”-charsigned char 是不同的类型,即使在您的系统上签名了普通的 char
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多