【问题标题】:How to cast or convert an unsigned int to int in C?如何在 C 中将 unsigned int 转换或转换为 int?
【发布时间】:2011-02-26 20:19:04
【问题描述】:

如果问题看起来很奇怪,我深表歉意。我正在调试我的代码,这似乎是问题所在,但我不确定。

谢谢!

【问题讨论】:

  • 真正的问题是当/如果 unsigned int 中的值超出有符号 int 可以表示的范围时,您想要做什么。如果它在范围内,只需分配它就可以了。如果超出范围,则会给出未指定的结果,因此您可能需要先将其缩小到正确的范围,或者将其分配给更大的有符号类型。
  • 未指定,实现定义(C99 6.3.1.3§3)。否则我同意,分配给更大的有符号整数是最简单的解决方案。

标签: c casting types int unsigned


【解决方案1】:

这取决于你想要的行为。 int 不能包含 unsigned int 可以包含的许多值。

你可以照常投射:

int signedInt = (int) myUnsigned;

但是如果unsigned 值超过int 可以容纳的最大值,这将导致问题。这意味着一半可能的unsigned 值将导致错误行为,除非您特别注意它。

如果您无缘无故地进行转换,您可能应该首先重新检查存储值的方式。

编辑: 正如 ProdigySim 在 cmets 中提到的,最大值取决于平台。但是您可以使用INT_MAXUINT_MAX 访问它。

对于通常的 4 字节类型:

4 bytes = (4*8) bits = 32 bits

如果使用所有 32 位,如 unsigned,最大值将为 2^32 - 1,或 4,294,967,295

有符号的int 有效地为符号牺牲了一位,因此最大值将为 2^31 - 1 或 2,147,483,647。请注意,这是其他值的一半。

【讨论】:

  • 你知道无符号的最大值是多少吗?和一个int?
  • @Eric Brotto:这将取决于您正在编译的系统。如果您需要检查,通常有可用于 INT_MAX、UINT_MAX 的宏。在大多数 32 位系统上,INT_MAX 将是 (2^31)-1 而 UINT_MAX 是 (2^32)-1。请注意,转换为 int 的 UINT_MAX 将为 -1。
  • “通常”是指 C 标准需要它们(对于托管系统)。
  • 实际上这些宏(位于<limits.h> 中)即使在独立(即非托管)系统上也是必需的。
【解决方案2】:

Unsigned int 可以通过如下所示的简单表达式转换为有符号(或反之亦然):

unsigned int z;
int y=5;
z= (unsigned int)y;   

虽然不是针对问题,但您想阅读以下链接:

【讨论】:

  • 我想你的意思是说“z = (unsigned int)y;”在上面的表达式中。由于 y 已经是一个 int,将其转换为 int 有点毫无意义......
【解决方案3】:

恕我直言,这个问题是常青树。如各种答案中所述,分配不在 [0,INT_MAX] 范围内的无符号值是实现定义的,甚至可能引发信号。如果无符号值被认为是有符号数的二进制补码表示,那么可能最可移植的方式是恕我直言,以下代码 sn-p 中显示的方式:

#include <limits.h>
unsigned int u;
int i;

if (u <= (unsigned int)INT_MAX)
  i = (int)u; /*(1)*/
else if (u >= (unsigned int)INT_MIN)
  i = -(int)~u - 1; /*(2)*/
else
  i = INT_MIN; /*(3)*/
  • 分支 (1) 很明显,不能调用溢出或陷阱,因为它 是保值的。

  • 分支 (2) 为避免有符号整数溢出而费尽心思 通过按位非取值的反码,将其转换为 到“int”(现在不能溢出),取反并减去 一个,这里也不能溢出。

  • 分支 (3) 提供了我们必须服用的毒药,或者 符号/幅度目标,因为有符号整数表示 范围小于二进制补码表示范围。

这很可能归结为对二进制补码目标的简单移动;至少我在 GCC 和 CLANG 中观察到了这种情况。在这样的目标上也无法到达分支 (3) - 如果想将执行限制为二进制补码目标,则可以将代码压缩为

#include <limits.h>
unsigned int u;
int i;

if (u <= (unsigned int)INT_MAX)
  i = (int)u; /*(1)*/
else
  i = -(int)~u - 1; /*(2)*/

配方适用于任何有符号/无符号类型对,代码最好放入宏或内联函数中,以便编译器/优化器对其进行排序。 (在这种情况下,用三元运算符重写配方是有帮助的。但它的可读性较差,因此不是解释策略的好方法。)

是的,“unsigned int”的一些强制转换是多余的,但是

  • 它们可能会帮助普通读者

  • 一些编译器会在有符号/无符号比较时发出警告,因为隐式转换会导致语言设计的一些非直观行为

【讨论】:

  • 您确定(unsigned int)INT_MIN 定义明确吗?
  • 是的。至少只要编译器头文件以它们应该的方式定义 INT_MIN,即具有最大可能负值的有符号整数。从负有符号整数到无符号整数的转换是明确定义的,并且总是产生二进制补码的位模式。
【解决方案4】:

如果您有一个变量unsigned int x;,您可以使用(int)x 将其转换为int

【讨论】:

    【解决方案5】:

    就这么简单:

    unsigned int foo;
    int bar = 10;
    
    foo = (unsigned int)bar;
    

    反之亦然...

    【讨论】:

    • 演员阵容冗长而丑陋。赋值本质上包括 C 中赋值有意义的类型的转换(甚至对于一些没有意义的类型)。强制转换通常只是为了掩盖不正确的代码。
    • 这不是真的,如果您要检查是否已经过了一定时间,这实际上是一个相当普遍的问题。您的时间间隔应该是一个无符号整数,因为对其进行签名没有意义,但 time_t 始终是有符号的,因此它可以表示早于 1970 年的日期。如果您在执行 if ((now - then ) > 间隔) 编译器会产生警告。
    【解决方案6】:

    如果在同一个表达式中使用 unsigned int 和 (signed) int,则有符号 int 会隐式转换为无符号数。这是 C 语言的一个相当危险的特性,因此您需要注意这一点。它可能是也可能不是您的错误的原因。如果您想要更详细的答案,则必须发布一些代码。

    【讨论】:

      【解决方案7】:

      C++Primer 5th第35页的一些解释

      如果我们为无符号类型的对象分配一个超出范围的值,则结果是该值的余数模目标类型可以容纳的值的数量。

      例如,8 位 unsigned char 可以保存 0 到 255 之间的值,包括 0 到 255。如果我们分配一个超出范围的值,编译器会将该值的余数以 256 为模。

      unsigned char c = -1; // assuming 8-bit chars, c has value 255
      

      如果我们将超出范围的值分配给有符号类型的对象,则结果是未定义的。该程序可能看起来可以工作,它可能会崩溃,或者它可能会产生垃圾值。

      第 160 页: 如果任何操作数是无符号类型,则操作数转换为的类型取决于机器上整数类型的相对大小。

      ... 当有符号不同且无符号操作数的类型与有符号操作数相同或大于有符号操作数时,将有符号操作数转换为无符号操作数。

      剩下的情况是有符号操作数的类型大于无符号操作数。在这种情况下,结果取决于机器。如果无符号类型中的所有值都适合大类型,则无符号操作数将转换为有符号类型。如果值不合适,则将有符号操作数转换为无符号类型。

      例如,如果操作数是 long 和 unsigned int,并且 int 和 long 具有相同的大小,则长度将转换为 unsigned int。如果 long 类型的位数更多,那么 unsigned int 将被转换为 long。

      我发现读这本书很有帮助。

      【讨论】:

      • "如果我们将一个超出范围的值赋给一个有符号类型的对象,结果是未定义的。" - 这是错误的,它实际上是实现定义的
      • 另外,这是一个 C 问题,但您引用了 C++ 参考。 C 和 C++ 是不同的语言
      猜你喜欢
      • 2022-01-04
      • 1970-01-01
      • 2013-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      相关资源
      最近更新 更多