AND 0xFF 是什么作用?

62
在下面的代码中:
short = ((byte2 << 8) | (byte1 & 0xFF))

& 0xFF的目的是什么?因为有时候,我看到上面的代码被写成:

short = ((byte2 << 8) | byte1)

而且这似乎也很好地运作着。

2
byte1 是 uint8_t 类型吗? - Shahbaz
2
那我猜只是“为了确保而已”。可能写这段代码的人是想要在某种情况下保险起见,以防 byte1 的类型被更改,因为 byte2 已经不是8位了(否则 byte2 << 8 就是0)。 - Shahbaz
2
抱歉,即使byte2是8位类型,byte2 << 8也可以工作。默认情况下,表达式总是作为int工作的。编译器会隐式地将表达式视为((int)byte2) << ((int)8) - Patrick Schlüter
4
顺便提一下,short 是一个保留字,不能用作变量名。 - Patrick Schlüter
7个回答

53

如果byte1是一个8位整数类型,那么这是无意义的 - 如果它超过了8位,则基本上会给您该值的最后8位:

    0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 &  0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
    -------------------------------
    0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1

46

将整数与 0xFF 相与可以得到最低有效字节,例如,要获取 short s 中的第一个字节,您可以编写 s & 0xFF。这通常被称为“掩码”。如果 byte1 是单个字节类型(如 uint8_t)或已经小于256(结果全部是零,除了最低有效字节),则无需屏蔽高位,因为它们已经是零。

在可能使用有符号类型时,请参见 tristopia Patrick Schlüter 的下面的答案。在进行位运算时,我建议仅使用无符号类型。


28

第二个表达式的危险在于,如果byte1的类型是char,那么某些实现可能会将其视为signed char,这将导致在评估时进行符号扩展。

signed char byte1 = 0x80;
signed char byte2 = 0x10;

unsigned short value1 = ((byte2 << 8) | (byte1 & 0xFF));
unsigned short value2 = ((byte2 << 8) | byte1);

printf("value1=%hu %hx\n", value1, value1);
printf("value2=%hu %hx\n", value2, value2);

将会打印

value1=4224 1080     right
value2=65408 ff80    wrong!!

我在Solaris SPARC 64位上的gcc v3.4.6上尝试了它,结果与将byte1byte2声明为char相同。

太长不看:

掩码是为了避免隐式符号扩展。

编辑:我检查了一下,在C++中的行为是相同的。

编辑2:根据要求解释符号扩展。符号扩展是C评估表达式的方式所导致的后果。C中有一个叫做promotion rule的规则。在进行评估之前,C将隐式地将所有小类型转换为int。让我们看看我们的表达式会发生什么:

unsigned short value2 = ((byte2 << 8) | byte1);

byte1是一个包含位模式0xFF的变量。如果charunsigned,那么该值将被解释为255;如果它是signed,则为-1。在进行计算时,C会将该值扩展到int大小(通常为16或32位)。这意味着,如果变量是unsigned,并且我们将保留值255,则该值的位模式作为int将为0x000000FF。如果它是signed,我们想要的值是-1,其位模式为0xFFFFFFFF。符号被扩展到用于执行计算的临时大小。

在x86汇编中,可以使用movsx指令(对于零扩展,使用movzx)。其他CPU有其他指令(6809有SEX)。


+1 警告有点意思,但我不明白为什么或者什么是“符号扩展”,你能简单解释一下吗? - doc_id
这里有一个简洁的解释。你应该查看我放置的链接,以了解有关C语言推导规则的更多信息,因为这是一个非常重要的点,即使是经验丰富的程序员也会在这方面犯错误。 - Patrick Schlüter
0xFFFFFFFF 不是等于 -1 吗?-128 应该是 0xFFFFFF80。 - Thern
你是正确的。已更正EDIT2部分。0xFF的有符号解释是-1,而不是-128。所以0xFFFFFFFF是正确的,它是-1。 - Patrick Schlüter

10
假设您的byte1是一个字节(8位),当您对一个字节与0xFF进行按位AND时,您得到的是相同的字节。
因此,byte1byte1&0xFF是相同的。
假设byte101001101,那么byte1&0xFF = 01001101&11111111 = 01001101 = byte1 如果byte1属于其他类型,如4字节整数,则按位AND与0xFF会留下字节1的最低有效字节(8位)。

5
byte1 & 0xff 确保只有 byte1 的最低有效位是非零的。
如果 byte1 已经是一个只有8位的无符号类型(例如某些情况下的 char 或大多数情况下的 unsigned char),那么这行代码不会有任何影响/完全没有必要。
如果 byte1 是一个带符号或者超过8位的类型(例如 shortintlong),并且除了最低的8位之外的任何一位都被设置了,那么就会有所不同(也就是说,在与其他变量进行“或”运算之前,它将清除那些上面的位,因此这个操作数仅影响结果的最低有效位)。

不,参见我上面的答案。如果 byte1 的类型是 charsigned char,那么这是绝对必要的。 - Patrick Schlüter

2

它清除了除第一个字节外的所有位


0
仅使用 & 0xFF 可以确保如果字节长度超过了 8 位(语言标准允许),则其余部分将被忽略。

这似乎也很好用?

如果结果大于 SHRT_MAX,你将得到未定义的行为。在这方面,两者都一样糟糕。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接