【问题标题】:Can we assign integer with negative number to unsigned integer?我们可以将带负数的整数分配给无符号整数吗?
【发布时间】:2013-12-12 15:21:21
【问题描述】:
#include<stdio.h>
#include<conio.h>
main()
{
    int i=-5;
    unsigned int j=i;
    printf("%d",j);
    getch();
}

O/p
-----
-5

#include<stdio.h>
#include<conio.h>
main()
{
    int i=-5;
    unsigned int j=i;
    printf("%u",j);
    getch();
}

O/p
===
4255644633

这里我没有收到任何编译错误。

当使用标识符 %d 打印时它给出 -5,当使用 %u 打印时它正在打印一些垃圾值。

我想知道的是

1) 为什么编译器在将带负数的整数分配给 unsigned int 时会忽略。

2) 如何将有符号转换为无符号?

【问题讨论】:

  • 如果你打开警告,编译器会警告你你将一个有符号分配给一个无符号。编译器只是将数字复制过来。因此,符号位最终成为无符号情况下数字的一部分。因此,如果您将一个负数 n 分配给一个无符号数,您将得到一个大数 2^32-|n|(如果它是一个 32 位字)或 2^64-|n| 如果它是一个 64 位字。
  • 我感到困惑的是,在二进制补码中,-5 应该是 4294967291,而不是 4255644633。我在gcc 签出,这确实是我得到的价值。我不明白他是如何获得不同的价值的。
  • @user3095972:您发布的第二个代码不可能输出4255644633。您要么发布了错误的代码,要么发布了错误的输出。

标签: c


【解决方案1】:

“我们”是谁?

没有“垃圾值”,它可能只是将有符号整数的位视为无符号的结果。通常two's complement 将导致许多负值的值非常大。尝试以十六进制打印值以更清楚地查看模式,十进制通常很难破译。

【讨论】:

  • 次要注意:值not“接近0”将显示一个非常大的值——至少2^31 -1,或 2,147,483,648。 (在 32 位整数系统上。64 位系统将显示更高的值。)
  • @Jongware 是的,我删除了那个位。谢谢。
【解决方案2】:

我只想补充一点,有符号或无符号的概念是人类比机器更欣赏的东西。

假设一台 32 位机器,您的值 -5 将在内部由 32 位值 0xFFFFFFFB(二进制补码)表示。

当您将printf("%d",j); 插入到源代码中时,编译器并不关心 j 是有符号还是无符号,它只是将 0xFFFFFFFB 推入堆栈,然后将指针推向 "%d" 字符串。 printf 函数在调用时会查看格式字符串,看到 %d 并从中知道它必须将 0xFFFFFFFB解释signed 值,因此尽管j 是无符号整数,但它显示-5 的原因。

另一方面,当您编写 printf("%u",j); 时,"%u" 使 printf 将您的 0xFFFFFFFB 解释为 unsigned 值。该值为 2^32 - 5,即 4294967291。

传递给printf 的格式字符串决定了如何解释值,而不是变量j 的类型。

【讨论】:

  • 终于有人理解这个话题了,不会对“符号位”做出模糊的陈述! +1
  • 相反,此答案中对第一个代码行为的解释只是试图区分未定义行为的特定表现。鉴于现代编译器开发的状态(例如高级格式错误检查和printf 是 GCC 中的内置函数),这种解释可能被证明是完全不准确的。第一个代码被破坏了,仅此而已。
  • 第二种解释从C语言的角度来看是不正确的。 C 语言在语言级别定义了有符号到无符号转换的行为,并且定义非常好,无需引入任何 2 的补码位模式。仅仅因为结果恰好是0xFFFFFFFB 而说第二种情况的行为方式具有误导性和不正确性。它只会读到更多的混乱。
【解决方案3】:

将负值分配给无符号变量的可能性值得注意。在这种情况下发生的隐式转换完全由 C 语言定义。根据模运算规则将值带入目标无符号类型的范围。模数等于2^N,其中N 是无符号接收者中的值位数。这就是 C 语言中的一贯做法。

使用%d 说明符打印unsigned int 值是没有意义的。此说明符需要 signed int 参数。由于这种不匹配,您的第一个代码的行为是未定义的。

换句话说,关于哪个值是垃圾,哪个不是垃圾,你完全倒退了。

由于未定义的行为,您的 first 代码本质上是“打印垃圾值”。事实上,它恰好与您的 -5 的原始值相匹配,这只是未定义行为的一种具体表现。

同时,second 代码应该打印一个定义明确的正确值。它应该是通过模 UINT_MAX + 1-5 转换为 unsigned int 类型的结果。在您的情况下,模可能恰好是2^32 = 4294967296,这就是为什么您应该看到4294967296 - 5 = 4294967291

您如何获得4255644633 尚不清楚。您的 4255644633 显然是不同代码的结果,而不是您发布的代码。

【讨论】:

  • 我唯一不明白的是2^32不是4255644638,它是4294967295。我不知道他为什么得到那个值。
  • @Paulo Bu:你是对的。我被帖子欺骗了,只是在输出中添加了 5+1 :) 显然,OP 的输出是由不同的代码生成的。
【解决方案4】:

您可以并且应该根据编译器和设置收到警告(或者可能失败)。

你得到的值是由于二进制补码。

【讨论】:

  • -1 从有符号到无符号的转换遵循明确定义的转换。这些不应发出警告,并且在任何情况下都不会导致编译失败。
  • @JensGustedt - 我没有提到它的定义不明确。但是您可以将 VS 配置为失败而不是发出警告。请参阅#pragma
【解决方案5】:

第二种情况的输出不是垃圾值……

int i=-5;

当转换为二进制形式时,最高有效位被分配为“1”,因为 -5 是一个负数..

但是当你使用 %u 时,二进制形式被视为普通数,而 MSB 中的 1 被视为普通数的一部分..

【讨论】:

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