在C语言中,不同整数宽度上的位运算符定义得好吗?

在C语言中,不同整数宽度上的位运算符定义得好吗?,c,bit-manipulation,language-lawyer,bitwise-operators,C,Bit Manipulation,Language Lawyer,Bitwise Operators,我想确保我了解如果两个不同宽度的整数按位或相互交错会发生什么。最明智的选择是用零填充较小的一个。我写了一个小程序来测试这个 请看以下示例代码: #include <stdio.h> #include <stdint.h> #include <inttypes.h> int main(void) { uint32_t foo = 0x00000000; uint8_t bar = 0xFF; printf("%"PRIu32"\n",

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

请看以下示例代码:

#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));
}

这种预期的、定义明确的行为是安全可靠的吗?是否有一个链接解释不同整数宽度的位操作的所有行为?

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

每个操作数应为整数类型

通常的算术转换是在操作数上执行的

因此,按位操作应该很好

但是,如果是
printf()
%d
需要
int
参数,而您提供的是
无符号int
值。就是


您可以使用
PRIu32
宏从
inttypes.h

打印
uint32\t
,您的代码在幕后执行的操作可能与您预期的不同:

uint32_t foo = 0x00000000;
uint8_t bar = 0xFF;
printf("%zu\n", (foo | bar));
让我们假设类型
uint32\u t
在您的系统上是一个
无符号int
。在这种情况下,表达式
(foo | bar)
由编译器按以下方式处理:

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

您需要了解的相关主题是C对整数提升和隐式转换规则的处理。

您不使用
int
,而是
uint32\u t
。这是一种无符号整数类型,但不一定是
无符号int
,也不一定是
(有符号)int
。您还可以通过向
printf
传递错误的varadic参数类型来调用未定义的行为。为
uint32\u t
使用正确的格式说明符(请参见
inttypes.h
)!关于比特操作:这应该通过一个非常简单的搜索在线找到,或者在每本C语言书中都可以找到。@Olaf谢谢你指出这一点。我不太擅长
printf
。我在我的代码中编辑了它,得到了相同的结果,所以我将编辑我的帖子,这样它就不会转移我真正的问题。请再读一遍!您现在使用了另一个错误的说明符。请再次阅读我的评论。@user3528438实际上它是
uint32\u t
signed int
中较宽的一个。如果值大于int\u MAX,则无符号int的%d为未定义行为。如果值为负值,则int的%ud为未定义行为。对于公共范围内的值,有符号或无符号值的有符号或无符号格式是可以的。@gnaser729什么是
%ud
?@SouravGhosh一个
%u
指令,然后是一个字符
d
。@SouravGhosh:在一点帮助下,OP现在正确地使用了类型说明符。不确定他是在你把它们(太显眼)展示出来之后编辑的,还是在你发现自己之后编辑的(哪种方式更好)。@user3528438取决于哪个更宽,
uint32\u t
还是
未签名的
<代码>(foo | bar)将是更广泛的类型。没有“积分促销”,而是整数促销。并且没有对
int
的强制(当然,除非
int
的位数超过32位,但是第二位就没有对
无符号int
的转换)。你能澄清一下你的意思吗?我只是遵循语言标准。一般来说,这是一个好主意,但对于“语言律师”问题至关重要。(虽然这一点很清楚)。有“整体促销”。在C89中;-)这个答案是正确的,但是除了
uint32\u t
unsigned int
之外,还有更多可能的情况,最好涵盖所有情况cases@Olaf正确的做法是,
bar
升级为
int
,然后升级为
uint32\u t
(在int为32位的情况下)。请参见C11 6.3.1.8/1“否则,将对两个操作数执行整数提升。然后对提升后的操作数应用以下规则:[……]将带符号整数类型的操作数转换为带符号整数类型的操作数的类型。”
uint32_t foo = 0x00000000;
uint8_t bar = 0xFF;
printf("%zu\n", (foo | bar));