【问题标题】:Are bitwise operators on different int widths in C well defined?C 中不同 int 宽度的位运算符是否定义明确?
【发布时间】:2025-12-15 09:50:01
【问题描述】:

我想确定我理解如果两个不同宽度的整数相互按位或运算会发生什么。最明智的选择是用零填充较小的那个。我写了一个小程序来测试一下。

看看这个示例代码:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(void)
{
    uint32_t foo = 0x00000000;
    uint8_t bar = 0xFF;
    printf("%"PRIu32"\n", (foo | bar));
    printf("%"PRIu32"\n", (bar | foo));
}

如果我的猜测是正确的,我应该会得到两次 255。当我运行它时,我得到了

255
255

这是可以安全依赖的预期和明确定义的行为吗?是否有一个链接解释了具有不同 int 宽度的位操作的所有行为?

【问题讨论】:

  • 你不用int,而是uint32_t。这是一个无符号整数类型,但不一定是unsigned int,也不一定是(signed) int。您还可以通过将错误的可变参数类型传递给printf 来调用未定义的行为。为uint32_t 使用正确的格式说明符(请参阅inttypes.h)!
  • 关于bitops:这应该可以通过非常简单的搜索在线找到或在每本C书中找到。
  • @Olaf 感谢您指出这一点。我不太擅长printf。我在我的代码中编辑了它并得到了相同的结果,所以我将编辑我的帖子,以免分散我真正的问题。
  • 请再读一遍!您现在使用了另一个错误的说明符。再次阅读我的评论。
  • @user3528438 实际上是uint32_tsigned int 中较宽的一个。

标签: c bit-manipulation language-lawyer bitwise-operators


【解决方案1】:

根据C11 标准,第 §6.5.12 章,按位包含 OR 运算符

每个操作数都应该是整数类型。

对操作数执行通常的算术转换。

所以,按位运算应该没问题。

但是,在 printf() 的情况下,%d 需要 int 参数并且您提供 unsigned int 值。那是undefined behavior

您可以使用PRIu32 宏从inttypes.h 打印uint32_t

【讨论】:

  • %d for unsigned int 如果值大于 INT_MAX,则为未定义行为。如果值为负数,则 int 的 %ud 是未定义的行为。对于公共范围内的值,有符号或无符号值的有符号或无符号格式都可以。
  • @gasher729 %ud 是什么?
  • @SouravGhosh 一个%u 指令,当然还有一个字符d
  • @SouravGhosh:在一些帮助下,OP 现在可以正确地使用类型说明符。不确定他是在你显眼地展示它们(太 imo)之后编辑还是发现了他自己(这会是更好的方法)。
  • @user3528438 取决于哪个更宽,uint32_tunsigned(foo | bar) 将是更广泛的类型。
【解决方案2】:

您的代码在幕后做了一些与您可能预期不同的事情:

uint32_t foo = 0x00000000;
uint8_t bar = 0xFF;
printf("%zu\n", (foo | bar));

假设uint32_t 类型在您的系统上是unsigned int。在这种情况下,表达式(foo | bar) 由编译器按以下方式处理:

  • 首先,bar 被更改(提升)为类型 int(称为整数提升的过程) - 这根本不会改变其 255 的数学值。
  • 然后,生成的int 被转换为unsigned int,因为| 的另一个参数是unsigned int 类型。同样,数学值没有任何变化,它仍然是 255。
  • 最后,如您所料,结果是 255,它的类型为 unsigned int

你可以看看的相关主题是C对整数提升和隐式转换规则的处理。

【讨论】:

  • 没有“整体促销”,而是整数促销。并且没有对int 的强制转换(当然,除非int 超过32 位,但是第二个位置没有转换为unsigned int)。
  • @Olaf:我经常错了,但在这里你把我弄丢了。
  • “综合促销”。在 C89 中 ;-)
  • 这个答案是正确的,但除了uint32_tunsigned int 之外,还有更多可能的情况,最好涵盖所有情况
  • @Olaf 将bar 提升为int 再提升为uint32_t 是正确的(在int 为32 位的情况下)。请参阅 C11 6.3.1.8/1 “否则,整数提升将在两个操作数上执行。然后将以下规则应用于提升的操作数:[...] 转换带符号整数类型的操作数为无符号整数类型的操作数的类型。”