strlen源码分析

本文分析了strlen函数的高效实现方式,该实现利用数据对齐技术提高性能。通过将指针移动到地址对齐的位置,然后逐字节比较直到找到'',并使用特定的magic_bits进行进位检测,从而确定''的位置。文章探讨了magic_bits的设计原理,包括为何第31位为0以及当b4为10000000时算法失败的原因。
摘要由CSDN通过智能技术生成

strlen函数原型为:

size_t strlen_c(const char *str)

参数为C风格字符串,输出为字符长度,以下为高效的实现代码:

1 typedef unsigned long ulong;
2

3 size_t strlen_c(const char * str) {
4

5 const char * char_ptr;
6 const ulong *
longword_ptr;
7
register ulong longword, magic_bits;
8

9 for (char_ptr = str; ((ulong)char_ptr
10 & (sizeof(ulong) - 1)) != 0
;
11 ++
char_ptr) {
12 if (*char_ptr == '/0'
)
13 return char_ptr -
str;
14
}
15

16 longword_ptr = (ulong* )char_ptr;
17

18 magic_bits = 0x7efefeffL ;
19

20 while (1 ) {
21

22 longword = *longword_ptr++ ;
23

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

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

28 if (cp[0] == 0
)
29 return cp -
str;
30 if (cp[1] == 0
)
31 return cp - str + 1
;
32 if (cp[2] == 0
)
33 return cp - str + 2
;
34 if (cp[3] == 0
)
35 return cp - str + 3
;
36
}
37
}
38 }

这种算法不是每次比较一个字符,而是比较一个机器字(32位机器是4个字节),利用了数据对齐技术。数据对齐是指数据在内存中的开始地址要是数据长度的整数倍,这样存取是最快的。

算法大致步骤:

1、首先把指针移到地址是机器字的整数倍的位置,假如已经遇到为0的字节,则直接返回长度

2、从此位置开始,每次比较一个机器字,直至遇到第一个为0的字节

3、找出这个字节在这个机器字的位置,然后返回长度

下面详细看代码(假设机器为32位机):

9 for (char_ptr = str; ((ulong)char_ptr
10 & (sizeof(ulong) - 1)) != 0
;
11 ++
char_ptr) {
12 if (*char_ptr == '/0'
)
13 return char_ptr -
str;
14
}

这段代码完成步骤1。for成立条件:((ulong)char_ptr & (sizeof(ulong) - 1)) != 0 它的意思是char_ptr是否为4的倍数,意义是把char_ptr移到地址是4的倍数的位置上,在这个过程中如果已有字符'/0',则返回长度;

16 longword_ptr = ( ulong * )char_ptr;

这是令到longword指向已已对齐的地址

这个是魔法位,它的二进制表示是(最低位为第0位):01111110,11111110,11111110,11111111,其中第31、24、16、8位,为0,暂时称呼为判断位,意义是要检测下一个字节是否为0,这个下面再详细讨论

18 magic_bits = 0x7efefeffL ;

这是最为核心的:

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

(longword + magic_bits)

意思是产生进位,是相应的判断位改变

longword是四个字节,分别是b4 b3 b2 b1,对于magic_bits,只要b4~b1有一个不为0,那么必定会产生进位,使判断位为1

例如:
b4 b3 b2 b1
longword : 00011010 01001011 00000000 00110110
magic_bits: 01111110 11111110 11111110 11111111 +
结果: 10010001 01001001 11111111 00110101

b3此字节不为0,令到magic_bits的第24位为1了

^ ~ longword

意思是把结果与longword比较,相同位为1

& ~ magic_bits

取最终结果的第31、24、16、8位

这样,假如最终结果第16位为1,则表示最后结果的第16位与longword的第16位相同,b2字节没有产生进位,b2字节为0
因此,如果longword中含有0字节,最终结果为非0

28 if (cp[ 0 ] == 0 )
29 return cp - str;
30 if (cp[ 1 ] == 0 )
31 return cp - str + 1 ;
32 if (cp[ 2 ] == 0 )
33 return cp - str + 2 ;
34 if (cp[ 3 ] == 0 )
35 return cp - str + 3 ;

找出0字节位置,返回长度;

问题:
1、为什么magic_bits第31位为0?
这是为了检测到b4为0的情况,因为无法检测第32位,因此把31位置0,检测31位,检测b4为00000000的情况
2、当longword中b4为10000000时,算法失败
当longword中b4为10000000时,最终结果的b4字节永远为0,算法失败,幸好参数字符的ascii码范围为0~127,因此算法是可行的。

http://dl.vmall.com/c0ekyk5eag
http://dl.vmall.com/c0omjcd3ej
http://dl.vmall.com/c0u9jou0bl
http://dl.vmall.com/c013dovyi4
http://dl.vmall.com/c0h1zcv6zz
http://dl.vmall.com/c01pttec3c
http://dl.vmall.com/c0f3tsxdna
http://dl.vmall.com/c0mh1lxkdh
http://dl.vmall.com/c0qe1mj16k
http://dl.vmall.com/c0wiyytjv7
http://dl.vmall.com/c0vydak2wa
http://dl.vmall.com/c0vn47jddh
http://dl.vmall.com/c0wnlcims1
http://dl.vmall.com/c06ka9jwp7
http://dl.vmall.com/c0a6misas8
http://dl.vmall.com/c0e90dza2w
http://dl.vmall.com/c0ou6hsp5g
http://dl.vmall.com/c0n3gcv221
http://dl.vmall.com/c0asdcyksb
http://dl.vmall.com/c04deps6fp
http://dl.vmall.com/c0u0k5daqc
http://dl.vmall.com/c07hosrooa
http://dl.vmall.com/c0zw1xa3he
http://dl.vmall.com/c0c9k6w53x
http://dl.vmall.com/c0mu6e23md
http://dl.vmall.com/c0oat2clm4
http://dl.vmall.com/c0jzqy7rxi
http://dl.vmall.com/c0u3zrwirt
http://dl.vmall.com/c07ryicaqf
http://dl.vmall.com/c01omh5jyg
http://dl.vmall.com/c0r6x2xa4l
http://dl.vmall.com/c0ag6mxoy5
http://dl.vmall.com/c0eo8lppnj
http://dl.vmall.com/c0hmyvk3e1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值