之所以系统地记下C语言的str系列函数,是因为4月份参加的两次实习生招聘面试是都让在纸上写代码实现(腾讯:strstr函数,华为:strtok函数),当时最后写得虽然没什么大问题,但略感生涩,特别是strtok函数。之前看过 strtok源码剖析 位操作与空间压缩 这篇博客,当时写起来的时候比较生疏,印象不深。真所谓“眼过千遍,不如手过一遍”。点击查看C语言其他一些str系列库函数的实现:C语言str系列库函数
vc++8.0库中的strtok源码阅读起来很容易理解,加上有上个链接的分析,因此不再赘诉。若在纸上写代码,不考虑线程安全,那么我们不妨再做点改动,让这个函数效率更高:
代码如下:
char * __cdecl strtok (
char * string,
const char * control
)
{
unsigned char *str;
const unsigned char *ctrl = (const unsigned char *)control;
static unsigned char* _TOKEN = NULL;
//注意这里使用了static类型,实际的strtok函数出于线程安全会使用TLS
static unsigned char map[32] = {0}; //赋初值
//int count;
/* Clear control map */
//for (count = 0; count < 32; count++)
//map[count] = 0;
/* Set bits in delimiter table */ //map为static变量,那么这个分隔符表只需要赋值一次即可
if (NULL != string)
{
do {
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
} while (*ctrl++);
}
/* Initialize str */
/* If string is NULL, set str to the saved
* pointer (i.e., continue breaking tokens out of the string
* from the last strtok call) */
if (string)
str = (unsigned char *)string;
else
str = (unsigned char *)_TOKEN;
/* Find beginning of token (skip over leading delimiters). Note that
* there is no token iff this loop sets str to point to the terminal
* null (*str == '\0') */
while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
str++;
string = (char*)str;
/* Find the end of the token. If it is not the end of the string,
* put a null there. */
for ( ; *str ; str++ )
if ( map[*str >> 3] & (1 << (*str & 7)) ) {
*str++ = '\0';
break;
}
/* Update nextoken (or the corresponding field in the per-thread data
* structure */
_TOKEN = str;
/* Determine if a token has been found. */
if ( string == (char*)str )
return NULL;
else
return string;
}
说明:1、如果不考虑线程安全,map数组可以定义成静态的,那么只需要在string不空(即第一次)时设置分隔符表即可,这样会更快点;
2、另外,可用unsigned char map[32]={0};代替那个for循环;
3、自己写时要注意类型转换。
strspn()、strcspn()和strpbrk()函数的源码中也用了位图来减少存储空间:C语言str系列库函数之strspn()、strcspn()和strpbrk()
参考资料:strtok源码剖析 位操作与空间压缩