【问题标题】:Comparison of Unsigned bit field value with signed values无符号位域值与有符号值的比较
【发布时间】:2019-07-11 13:04:10
【问题描述】:

在编写代码时,我在代码中观察到一件事,它与位域值与负整数的比较有关。

我有一个大小为一位的无符号结构成员和一个无符号整数。当我将负值与 unsigned int 变量进行比较时,我得到预期结果为 1,但是当我将结构成员与负值进行比较时,我得到相反的结果为 0。

#include <stdio.h>
struct S0
{
   unsigned int bit : 1;
};
struct S0 s;

int main (void) 
{
   int negVal = -3;
   unsigned int p = 123;
   printf ("%d\n", (negVal > p)); /*Result as 1 */
   printf ("%d\n", (negVal > s.bit));/*Result as 0 but expected 1 */
   return 0;
}

我的疑问是,如果我将负值与 unsigned int 进行比较,那么就会发生平衡(隐式类型转换)。但是,如果我比较 unsigned int 的结构成员,为什么没有发生隐式类型转换。如果我错过了位域的任何基础知识,请纠正我?

【问题讨论】:

标签: c bit-fields


【解决方案1】:

为了说明,以下使用 32 位 int 和 32 位 unsigned int

negVal &gt; p:

  • negVal 是一个 int,值为 -3。
  • p 是一个 unsigned int,值为 123。
  • C 2018 6.5.8 3 讨论了&gt; 和其他关系运算符,告诉我们通常的算术转换是在操作数上执行的。
  • 6.3.1.8 1 定义了通常的算术转换。对于整数类型,通常算术转换的第一步是对每个操作数执行整数提升
  • 6.3.1.1 2 定义整数提升。 intunsigned int 和比这些更宽的整数类型保持不变。对于其他整数类型,它表示:“如果int 可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为int;否则,它将被转换为unsigned int。”
  • 由于negValint,因此整数提升不会改变它。
  • 由于punsigned int,因此整数提升不会改变它。
  • 通常算术转换的下一步是将一个操作数转换为另一个操作数的类型。对于intunsigned intint 将转换为unsigned int
  • int -3 转换为unsigned int 得到4,294,967,293。 (转换定义为将UINT_MAX + 1(即 4,294,967,296)加到或减去该值所需的次数以使其进入范围。这相当于“包装”模 4,294,967,296 或重新解释 -3 的二进制补码表示作为unsigned int。)
  • 转换后,表达式negVal &gt; p 变为4294967293u &gt; 123u
  • 这个比较是真的,所以结果是1。

negVal &gt; s.bit:

  • negVal 是一个 int,值为 -3。
  • s.bit 是一个值为 0 的一位位域。
  • 如上所述,通常的算术转换是在操作数上执行的。
  • 如上所述,通常算术转换的第一步是对每个操作数执行整数提升。
  • 由于negValint,因此整数提升不会改变它。
  • 由于s.bit 是一个比int 窄的位域,它将通过整数提升进行转换。这个一位位域可以表示 0 或 1。这两者都可以用 int 表示,因此规则“如果 int 可以表示原始类型的所有值(受宽度限制) ,对于位字段),该值将转换为 int” 适用。
  • 将 0 转换为 int 得到 0。
  • 通常算术转换的下一步是将一个操作数转换为另一个操作数的类型。由于两个操作数现在都是int,因此不需要转换。
  • 转换后,表达式negVal &gt; s.bit 变为-3 &gt; 0
  • 这个比较是假的,所以结果是0。

【讨论】:

  • 感谢 Eric 的清晰解释,出于好奇,我想问将 s.bit 类型转换为无符号整数而不是 int 需要多少位宽,因为 int 可以容纳 32 位...跨度>
  • @swaraj: cast* 是一个运算符。它的格式为(type)。当你写(unsigned int) −3时,那是一个强制转换,它执行的操作是一个转换。转换也在其他地方进行。比如写a = b时,b的值就转换为a的类型。另一个例子是整数提升执行转换。您应该使用“cast”来指代运算符,并且应该使用“conversion”来指代任何转换。
  • @swaraj:为了将使用unsigned int 声明的位域提升为unsigned int 而不会提升为int,它必须具有与@ 一样多的位987654371@ 可以。如果它的位数较少,那么它可以表示的所有值也可以用int 表示,因此它将被提升为intunsigned int 中的位数因 C 实现而异,因此没有单一的常量大小可以声明一个位字段来确保它被提升为 unsigned int
  • @swaraj:为确保将位域转换为unsigned int,您应该使用强制转换:negVal &gt; (unsigned int) s.bit
【解决方案2】:

(移动我的评论作为答案)

gccs.bit 提升为 int,因此 (negVal &gt; s.bit)(-3 &gt; 0) 赋值为 0

请参阅Should bit-fields less than int in size be the subject of integral promotion?,但您的问题不是重复的。


(negVal &gt; p) 返回 1,因为 negVal 被提升为 unsigned 产生大值,请参阅 Signed/unsigned comparisons

【讨论】:

  • @Vagish — 有符号值转换为(大)无符号值。
  • @bruno—gcc 将 s.bit 提升为 int,但该位是 unsigned int 类型,在 c++ 文档草案中它提到“如果 unsigned int 可以表示,它可以转换为 unsigned int位域的所有值”,所以它应该转换为 unsigned int 对吗?
  • @swaraj 这个故事的寓意是不要在有符号和无符号之间进行任何比较,为此添加必要的演员表以表明预期的转换
  • @swaraj:问题被标记为 C 而不是 C++,那你为什么要引用 C++ 文档?
  • @Eric Postpischil - 这是我得到的唯一答案,我想用 c 回答,所以我不接受我只是参考了文件
猜你喜欢
  • 2015-10-31
  • 1970-01-01
  • 2012-09-30
  • 2011-07-21
  • 2012-07-28
  • 2016-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多