glibc strchr的实现
1. 函数原型
char* strchr(const char *s, int c) 返回c在s中出现的第一个位置,如果没有则返回NULL
2. 实现方法
寻找一个字符在字符串中首次出现的位置,传统的做法是逐字节进行比较。为了提高速度,glibc的
实现中一次比较一个unsigned long类型的长度(4或8个字节,下面以4字节为例进行叙述,8字节的原理类似):
unsigned long longword;
判断longword的4个字节中是否含有寻找的字符c或结束符'\0',如果有则对longword逐字节比较,
找出具体的位置,没有则跳到下一个longword进行比较。
一次比较4个字节需要做两件事:内存对齐和零字节探测。
3. 内存对齐
char类型的长度为1个字节,而unsigned long类型的长度为4个字节,在内存中的地址必须是
4的整数倍,将(char *)赋值给(unsigned long *)之前必须确保内存对其。实现方式如下:
const unsigned char* char_ptr;
for(char_ptr = (const unsigned char *) s;
((unsigned long int) char_ptr & (sizeof(longword) - 1)) != 0;
++char_ptr)
if(*char_ptr == c)
return (void *) char_ptr;
else if(*char_ptr == '\0')
return NULL;
4. 零字节探测
为了探测longword中是否含有零字节,定义magic_bits,magic_bits中最高位以及每个字节的左
边都为0,把这些0的位置叫holes.根据holes判断是否有零字节。
magic_bits = 0x7efefeffL;
bits: 01111110 11111110 11111110 11111111
1-bits 确保进位传递到下一个0-bits
0-bits 提供空位置存储进位
将magic_bits与longword相加,如果longword中某个字节为0,该字节左边hole就没有进位位,
相加之后该位置为longword对应位置的值,与~longword做^操作之后该位置为1,再与~magic_bits
做&操作之后结果不等与0;如果longword中没有为0的字节,holes位置均有进位位,相加之后holes
位置的值与longword对应的位置相反,与~longword做^操作之后该位置为0,再与~magic_bits做
&操作之后结果为0.综上所述,longword中有0字节的条件为:
((magic_bits + longword) ^ ~longword) & ~magic_bits != 0
用零字节探测的方法同样可以判断longword中是否含有待查找的字符c,首先将c扩充为unsigned long类型:
unsigned long charmask;
charmask = c | (c << 8);
charmask |= charmask << 16;
如果longword中包含c,则longword ^ charmask一定包含零字节,所以longword中包含c的条件为:
((magic_bits + (longword ^ charmask)) ^ ~(longword ^ charmask)) & ~magic_bits != 0
reference:
glibc strchr.c
浙公网安备 33010602011771号