【问题标题】:Bitwise "not" operator in C returns signed resultC 中的按位“非”运算符返回有符号结果
【发布时间】:2013-05-11 11:41:23
【问题描述】:

考虑这段代码:

uint16_t a = ~ ( uint16_t ) 0;
int16_t  b = ~ ( int16_t ) 0;
printf (
    "%d %d %d %d\n",
    a  == ~ ( uint16_t ) 0,
    a  == ( uint16_t ) ( ~ ( uint16_t ) 0 ),
    b  == ~ ( int16_t ) 0,
    b  == ( int16_t ) ( ~ ( int16_t ) 0 )
);

输出是:

0 1 1 1

GCC 抛出关于 a == ~ ( uint16_t ) 0 的警告:

由于数据类型[-Wtype-limits]的范围有限,比较总是错误的

为什么按位“非”运算符试图返回有符号值?我怎样才能防止这种情况发生?

【问题讨论】:

  • 波浪号 ~ 运算符是 not 一元非。它是一个(一元)flip all bits 位运算符。
  • ~ 是一个补码运算符。
  • 我现在该操作员做什么 =)。我不确定应该如何命名该运算符。维基百科说“按位'不是'”
  • @puchu ~ 是按位非,! 是布尔非...
  • 另请阅读此答案:C not operator why do I get a warning

标签: c linux gcc gnu99


【解决方案1】:

为什么按位“非”运算符试图返回有符号值?

因为没有运算符适用于小于int 的类型;较小的类型(包括 uint16_t,如果 int 超过 16 位)在用作操作数时会被提升。如果原始类型的所有值都可以由int 表示,就像它们在这里一样,那么提升将是int

我怎样才能防止这种情况发生?

你不能;这就是语言的工作方式。您必须将运算符的结果转换为您想要的类型,或者像初始化 a 时那样隐式转换,或者使用强制转换显式转换。请注意,您不需要演员 before 应用~;但是为了获得最大的可移植性,在执行按位逻辑时应该坚持使用无符号类型:

uint16_t a = ~0u;
printf("%d\n", a == (uint16_t)~0u);  // Should print 1

【讨论】:

  • 这不清楚为什么printf 应该打印“1”。是否打算使用此处显示的表达式 (uint16_t)~0 或原始代码中的表达式 ~ ( uint16_t ) 0 来初始化 a?如果是后者,则不必打印“1”。后一个表达式总是在a 中产生 65535,但前一个表达式可能具有值 0、1 或 65535,具体取决于 C 实现。最好使用~0u 而不是~0
  • @EricPostpischil:你是对的,当然;很容易忘记,当我一生中(或多或少)不存在这种东西时,该语言支持带符号整数的奇异表示。我已经添加了一个注释,应该首选无符号类型,以防有人在编写 UNIVAC 时盲目地遵循我的建议。
【解决方案2】:

符号是我们置于位模式之上的一个概念。按位不 (~) 仅涉及位模式,而不涉及值的符号。将有符号的值记为无符号值的结果是相同的。

话说回来看看C标准:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf(免费提供草稿版)。第 6.3.1.4 节,第 51 页:

如果 int 可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为 int;否则,它将转换为无符号整数。这些被称为整数促销。 (58) 整数提升不会改变所有其他类型。

我的意思是,当我们实际操作 char 和 short 类型时,它们将被提升为 int(或 unsigned int,具体取决于大小)。这是有道理的,因为我们希望尽可能快地完成操作,因此我们应该以机器的原生大小为目标。

鉴于此,我们可以看到实际发生的情况。机器将以“int”大小执行所有操作,因为“==”和“~”的操作数都可以放入我假设在您的机器中为 32 位的 int 字段。

现在首先要看的是“a”的值。我们取 0,我们不取 0xFFFFFFFF。我们将其分配给 uint16_t 值并获得 0xFFFF。当我们准备好进行比较时,我们加载 0xFFFF,意识到该值是无符号的并且零将其扩展到 0x0000FFFF。对于 'b' 的值,除了我们读取 0xFFFF 进行比较之外,一切都是一样的,我们将其扩展为 0xFFFFFFFF。现在为您的案例:

  1. 不写零会得到 0xFFFFFFFF,与 0x0000FFFF 相比会失败。
  2. 我们将 0xFFFFFFFF 截断为 0xFFFF,然后将其零扩展为 0x0000FFFF,得到与“a”相同的值。

等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-03-19
    • 2011-01-31
    • 2012-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 2021-05-12
    相关资源
    最近更新 更多