与1进行按位AND将从其他操作数检索位模式。意思是,10101 & 11111 = 10101
。如果按位AND的结果为0,那么我们知道我们知道另一个操作数是0.当将单个字节与0xFF
(ones)进行与时,结果为0将指示NULL字节。
代码本身检查字节数组的四个字节分区中的每个字节。 注意:此代码不可移植;在另一台机器或编译器上,unsigned int可能超过4个字节。使用uint32_t
数据类型可能会更好,以确保32位无符号整数。
首先要注意的是,在小端机上,组成字符数组的字节将按相反的顺序读入无符号数据类型;也就是说,如果当前地址的四个字节是与abcd
对应的位模式,那么无符号变量将包含对应于dcba
的位模式。
第二个是C中的一个十六进制数字常量会产生一个int大小的数字,并在位模式的小端指定字节。意思是,当用4字节整数编译时,0xFF
实际上是0x000000FF
。 0xFF00
是0x0000FF00
。等等。
所以程序基本上是在四个可能的位置寻找NULL字符。如果当前分区中没有NULL字符,则前进到下一个四字节插槽。
以char数组abcdef
为例。在C中,字符串常量的末尾总是有空终止符,所以在该字符串的末尾有一个字节0x00
。
它会工作如下:
读 “ABCD” 为unsigned int类型X:
x: 0x64636261 [ASCII representations for "dcba"]
检查每个字节的空终止:
0x64636261
& 0x000000FF
0x00000061 != 0,
0x64636261
& 0x0000FF00
0x00006200 != 0,
并检查其他两个职位;在这个4字节分区中没有空终止符,所以前进到下一个分区。
读 “EF” 到unsigned int的X:
x: 0xBF006665 [ASCII representations for "fe"]
注为0xBF字节;这已经超过了字符串的长度,所以我们从运行时堆栈读取垃圾。它可能是任何东西。在不允许未对齐访问的机器上,如果字符串之后的内存不是1字节对齐,则会崩溃。如果字符串中只剩下一个字符,我们将读取两个额外的字节,因此与char数组相邻的内存对齐必须为2字节对齐。
检查每个字节的空终止:
0xBF006665
& 0x000000FF
0x00000065 != 0,
0xBF006665
& 0x0000FF00
0x00006600 != 0,
0xBF006665
& 0x00FF0000
0x00000000 == 0 !!!
所以我们返回len + 2
; len
是4,因为我们增加了4次,所以我们返回6,这实际上是字符串的长度。
它交易一个非常有问题的加速未定义的行为(它甚至可能更慢)。并且不符合标准,因为它返回'int'而不是'size_t' – Olaf
是的,如果int类型变得大于4字节或者机器不是little-endian,这不会引起问题吗? –
@MillieSmith:这是最少的问题,因为大多数64位系统是I32LP64(POSIX)。问题是未对齐的访问,endianess(如你所述)。即使在平台上允许未对齐访问,它们也可能比对齐访问慢得多。更不用说多重掩码和条件操作了。 – Olaf