【问题标题】:Assigning negative numbers to an unsigned int?将负数分配给无符号整数?
【发布时间】:2011-03-02 15:30:08
【问题描述】:

在 C 编程语言中,unsigned int 仅用于存储正值。但是,当我运行以下代码时:

unsigned int x = -12;
printf("%d", x);

输出仍然是-12。我以为它应该打印出来:12,还是我误会了什么?

【问题讨论】:

  • 你在对编译器撒谎:你告诉编译器你会给printf一个int"%d"),但是你给它一个unsigned不要对编译器撒谎
  • @Joachim:编译器正在执行“通常的算术转换”(参见 3.2.1.5)。 "... 否则,如果任一操作数的类型为 unsigned int,则另一个操作数将转换为 unsigned int ..." 并且-12 转换为无符号结果(UINT_MAX + 1) - 12
  • @Joachim:gcc 标志-Wsign-conversion(包含在-Wconversion 中)可用于对此发出警告。如您所知,它不属于-Wall-Wextra
  • @Joachim:我看不出编译器应该发出警告以完全合理地使用该语言的原因。遗憾的是,C 整数语义的细节通常不会在书籍和课程的早期介绍,但这并不是为不了解他们正在编程的语言的人定制编译器首选警告级别的真正借口......
  • @R: GCC 编译器对 user-Wall 说谎实际上是一种耻辱,因为它没有打开"全部”警告。

标签: c


【解决方案1】:

等号右侧的-12 设置为有符号整数(大小可能为32 位),其值为十六进制值0xFFFFFFF4。编译器生成代码以将此有符号整数移动到您的无符号整数x,它也是一个 32 位实体。编译器假定等号右侧只有一个正值,因此它只是将所有 32 位移动到 x 中。 x 现在的值 0xFFFFFFF4 如果解释为正数,则为 4294967284。但是%dprintf 格式表示32 位将被解释为有符号整数,因此您会得到-12。如果您使用了%u,它将打印为4294967284

在任何一种情况下,您都不会得到您所期望的,因为 C 语言“信任”代码编写者只要求“明智”的东西。这在 C 中很常见。如果您想为 x 分配一个值并且不确定等号右侧的值是否为正,您可以编写 unsigned int x = abs(-12); 并强制编译器生成代码以获取有符号整数在移动到无符号整数之前的绝对值。

【讨论】:

  • 这在技术上是错误的。在unsigned int x = -12; 中,-12int 类型的整数常量表达式。通过重复添加(或减去)UINT_MAX + 1 到该值直到获得介于 0 和 UINT_MAX(包括两者)之间的值,将其转换为 unsigned int,该值是转换的结果。在一个补码或符号和大小机器上,这会改变值的位模式;在更常见的二进制补码机器上,它只是对相同位模式的不同解释。
  • 但是那部分完全是由标准定义的,在unsigned int x = -12;之后,x的值必须一直是UINT_MAX - 11。下一行 printf("%d", x); 调用未定义的行为,因为 %d 转换说明符需要有符号的 int 并且 x 的值 notunsigned intint 中都可以表示.常见的行为当然是重新解释位模式,在这种情况下将产生-11 的补码机器,在符号和大小上使用 32 位 ints,-2147483636。在二进制补码机器上观察到的行为。
  • @DanielFischer 为什么x 的值不能在unsigned int 中表示,因为该值小于UINT_MAX
  • @ajay x 的值可以表示为unsigned int,但不能表示为int(除非实现非常奇怪以至于它的unsigned ints 有一个填充位,所以范围是int 范围的子范围)。使用%d 转换说明符打印unsigned int 是未定义的行为,除非该值在both 类型中都可以表示。
  • 对我来说很有意义。个人轶事:我试图通过将整数转换为无符号整数并让它存储新值来删除整数上的符号。当然,我发现这不是 C++ 的工作方式。
【解决方案2】:

该 int 未签名,但您已告诉 printf 将其视为已签名 int。

试试

unsigned int x = -12; printf("%u", x);

它不会打印“12”,但会打印 unsigned int 的最大值减去 11。

对读者的练习是找出原因:)

【讨论】:

  • “11 或 12”是错误的。结果定义明确,是UINT_MAX-11,而不是UINT_MAX-12
  • @R:我不记得它是 max-11 还是 max - 12,因此我说“负 11 或 12”(这在技术上是正确的) 其中之一)。感谢您指出它是-11,我将更改答案以反映这一点,谢谢。
  • 最终是数字的补码
【解决方案3】:

将 %d 传递给 printf 告诉 printf 将参数视为有符号整数,而不管您实际传递的是什么。使用 %u 打印为无符号。

【讨论】:

  • 简单易懂,最佳答案!
  • 我更欣赏这个答案。说到重点。谢谢。
【解决方案4】:

这一切都与价值的解释有关。

如果您假设 16 位有符号和无符号整数,那么这里有一些不完全正确的示例,但演示了这个概念。

0000 0000 0000 1100 unsigned int,有符号整数值 12

1000 0000 0000 1100 有符号 int 值 -12,以及一个大的无符号整数。

对于有符号整数,左边的位是符号位。 0 = 正 1 = 否定

对于无符号整数,没有符号位。 左边的位,可以让你存储一个更大的数字。

所以你没有看到你所期望的原因是。

unsigned int x = -12,取-12为整数,存入x。 x 是无符号的,所以 原来是符号位,现在是值的一部分。

printf 让您告诉编译器您希望如何显示一个值。

%d 表示将其显示为带符号的 int。 %u 表示将其显示为无符号整数。

c 让你做这种事情。程序员在你的掌控之中。

有点像枪支。 这是一个工具。 您可以正确使用它来处理某些情况, 或错误地移除你的一个脚趾。

一个可能有用的例子如下

unsigned int allBitsOn = -1;

该特定值将所有位设置为 1

1111 1111 1111 1111

这有时很有用。

【讨论】:

    【解决方案5】:
    printf('%d', x); 
    

    表示打印一个有符号整数。你必须改写这个:

    printf('%u', x);
    

    此外,它仍然不会打印“12”,而是“4294967284”。

    【讨论】:

      【解决方案6】:

      它们确实存储正值。但是您将(非常高的)正值作为有符号整数输出,因此它会再次被重新解释(以实现定义的方式,我可能会添加)。

      改用格式标志"%u

      【讨论】:

        【解决方案7】:

        您的程序具有未定义的行为,因为您将错误的类型传递给printf(您告诉它您将传递int,但您传递了unsigned int)。认为自己很幸运,实现中“最简单”的事情就是默默地打印错误的值,而不是跳转到一些有害的代码......

        【讨论】:

        • 如果值在两种类型中都可以表示,则传入一个无符号整数,其中期望对应有符号类型的变量参数实际上是明确定义的(参见 C99 7.15.1.1 §2);但是,这里不是这种情况,但 C 语言的任何合理实现都只会通过“类型双关”翻译值(参见第 73 页的脚注 82)
        【解决方案8】:

        您缺少的是 printf("%d",x) 期望 x 被签名,因此尽管您将 -12 分配给 x 它被解释为 2 的补码,这将是一个非常大的数字。 但是,当您将这个非常大的数字传递给 printf 时,它会将其解释为已签名,从而正确地将其转换回 -12。

        在 print f 中打印 unsigned 的正确语法是 "%u" - 试试这个,看看它的作用!

        【讨论】:

          【解决方案9】:

          将负值分配给 unsigned int 不会计算负值的绝对值:它将负值的二进制表示形式解释为 unsigned int,即 4294967284 (2^32 - 12)。

          printf("%d") 执行相反的解释。这就是您的程序显示 -12 的原因。

          【讨论】:

          • 它不只是正式地重新解释表示,而且实际上它没有使用一个补码或符号和大小机器。有符号值通过取余数模 UINT_MAX + 1 转换为 unsigned,因此无论有符号整数如何表示,unsigned int x = -12;x 设置为 UINT_MAX - 11
          【解决方案10】:

          int 和 unsigned int 用于分配多个字节来存储一个值而已。

          编译器应该给出有关有符号不匹配的警告,但它实际上不会影响内存中表示值 -12 的位。

          %x, %d, %u 等告诉编译器在打印时如何中断一些位。

          【讨论】:

            【解决方案11】:

            当您尝试显示 int 值时,您将其传递给 (int) 参数而不是 (unsigned int) 参数,这会导致它打印 -12 而不是 4294967284。整数以十六进制存储格式 和 -12 的 int 与 4294967284 的 unsigned int 十六进制格式相同。 这就是为什么“%u”打印出你想要的正确值而不是“%d”。这取决于你的参数类型。祝你好运!

            【讨论】:

              【解决方案12】:

              -12 是 16 位 2 的补码格式。所以这样做: 如果 (x & 0x8000) { x = ~x+1; } 这会将 2 的补码 -ve 数转换为等效的 +ve 数。祝你好运。

              【讨论】:

                【解决方案13】:

                当编译器将 -12 隐式转换为无符号整数时,底层二进制表示保持不变。这种转换纯粹是语义上的。二进制补码整数的符号位成为无符号整数的最高有效位。因此,当 printf 将无符号整数视为带有 %d 的有符号整数时,它将看到 -12。

                【讨论】:

                • 底层二进制表示在一个的补码或符号和大小机器上发生了变化。
                【解决方案14】:

                在一般情况下,当只能存储正数时,不会显式存储负数,而是存储它们的 2 的补码。同样的方法,-12 的 2 的补码将存储在 'x' 中,您使用 %u 获取它。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2015-02-21
                  • 2017-03-04
                  • 2017-01-08
                  • 1970-01-01
                  • 2019-11-28
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多