学习高效编程的有效途径之一就是阅读高手写的源代码,CRT(C/C++ Runtime Library)作为底层的函数库,实现必然高效。恰好手中就有glibc和VC的CRT源代码,于是挑了一个相对简单的函数strlen研究了一下,并对各种实现作了简单的效率测试。
strlen的函数原形如下:
size_t strlen(const char *str);
strlen返回str中字符的个数,其中str为一个以'\0'结尾的字符串(a null-terminated string)。
1. 简单实现
如果不管效率,最简单的实现只需要4行代码:

C++ Code
1
2
3
4
5
6
7
size_t strlen_a(const char *str)
{
    size_t length =
0 ;
   
while (*str++ )
        ++ length;
   
return  length;
}

也许可以稍加改进如下:

C++ Code
1
2
3
4
5
6
size_t strlen_b(const char *str)
{
   
const char *cp =  str;
   
while (*cp++ );
   
return (cp - str - 1 );
}

2. 高效实现
很显然,标准库的实现肯定不会如此简单,上面的strlen_a以及strlen_b都是一次判断一个字符直到发现'\0'为止,这是非常低效的。比较高效的实现如下(在这里WORD表示计算机中的一个字,不是WORD类型):
(1) 一次判断一个字符直到内存对齐,如果在内存对齐之前就遇到'\0'则直接return,否则到(2);
(2) 一次读入并判断一个WORD,如果此WORD中没有为0的字节,则继续下一个WORD,否则到(3);
(3) 到这里则说明WORD中至少有一个字节为0,剩下的就是找出第一个为0的字节的位置然后return。
NOTE:
数据对齐(data alignment),是指数据所在的内存地址必须是该数据长度的整数倍,这样CPU的存取速度最快。比如在32位的计算机中,一个WORD为4 byte,则WORD数据的起始地址能被4整除的时候CPU的存取效率比较高。CPU的优化规则大概如下:对于n字节(n = 2,4,8...)的元素,它的首地址能被n整除才能获得最好的性能。
为了便于下面的讨论,这里假设所用的计算机为32位,即一个WORD为4个字节。下面给出在32位计算机上的C语言实现(假设unsigned long为4个字节):源码来着于glibc

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
typedef unsigned long  ulong;

size_t strlen_c(
const char *str)
{

   
const char *char_ptr;
   
const ulong *longword_ptr;
   
register ulong longword, magic_bits;

   
for (char_ptr =  str; ((ulong)char_ptr
                           & (
sizeof(ulong) - 1)) != 0 ;
            ++ char_ptr)
    {
       
if (*char_ptr == '\0' )
           
return char_ptr -  str;
    }

    longword_ptr = (ulong * )char_ptr;

    magic_bits = 0x7efefeffL ;

   
while (1 )
    {

        longword = *longword_ptr++ ;

       
if ((((longword + magic_bits) ^ ~longword) & ~magic_bits) != 0 )
        {

           
const char *cp = (const char *)(longword_ptr - 1 );

           
if (cp[0] == 0 )
               
return cp -  str;
           
if (cp[1] == 0 )
               
return cp - str + 1 ;
           
if (cp[2] == 0 )
               
return cp - str + 2 ;
           
if (cp[3] == 0 )
               
return cp - str + 3 ;
        }
    }
}

3. 源码剖析
上面给出的C语言实现虽然不算特别复杂,但也值得花点时间来弄清楚,先看9-14行:

for (char_ptr = str; ((ulong)char_ptr & (sizeof(ulong) - 1)) != 0; ++char_ptr) {
if (*char_ptr == '\0')
return char_ptr - str;
}

上面的代码实现了数据对齐,如果在对齐之前就遇到'\0'则可以直接return char_ptr - str;
测试代码如下:

ASM Code
1
2
3
4
char szName[]=”Jack”;
char *p=szName;
p++;//p移动一个字节,本身的地址p是按照4个字节对齐的,移动后不再对齐
printf(“%d\n”,strlen(p));

第16行将longword_ptr指向数据对齐后的首地址longword_ptr = (ulong*)char_ptr;

第18行给magic_bits赋值(在后面会解释这个值的意义)

magic_bits = 0x7efefeffL;

相关文章:

  • 2021-10-24
  • 2021-07-14
  • 2021-08-12
  • 2021-10-10
  • 2021-10-17
  • 2021-12-31
  • 2021-10-17
猜你喜欢
  • 2021-12-11
  • 2021-11-15
  • 2022-12-23
  • 2021-10-22
  • 2021-12-07
  • 2022-01-28
  • 2021-09-15
相关资源
相似解决方案