【原创性声明】在调试到strlen的汇编代码时,我发现其汇编代码略显“古怪”,当时并未有精力去关注,在研究过后重新查看了strlen的汇编代码。对strlen的原理解释并非原创,而是参考了《strlen源码剖析》一文的解释,在理解后感觉有必要写此文记之。
首先我们创建一个测试项目,来比较以下我们自己用常规方法写的的strlen函数和<string.h> 中的strlen的时间消耗水平,这里我用 GetTickCount()做一个初步的估算(并不精确),测试代码如下:
strlen时间消耗测试
#include
"
stdafx.h
"
#include < stdlib.h >
#include < stdio.h >
#include < string .h >
#include < windows.h >
// 这是我们自己按常规算法实现的strlen,即逐个byte判断是否为0
size_t mystrlen( char * s)
{
char * p = s;
while ( * p) ++ p;
return (p - s);
}
int main( int argc, char * argv[])
{
char * s;
size_t result, i;
DWORD tickcount;
s = ( char * )malloc( 4096 );
memset(s, ' a ' , 4096 );
s[ 4091 ] = 0 ;
// 粗略测试两种strlen的时间
// 标准库中的strlen:
tickcount = GetTickCount();
for (i = 0 ; i < 20000 ; i ++ )
{
result = strlen(s);
}
printf( " strlen : %ld ms, result = %ld\n " , GetTickCount() - tickcount, result);
// 我们自己编写的strlen:
tickcount = GetTickCount();
for (i = 0 ; i < 20000 ; i ++ )
{
result = mystrlen(s);
}
printf( " mystrlen: %ld ms, result = %ld\n " , GetTickCount() - tickcount, result);
free(s);
return 0 ;
}
#include < stdlib.h >
#include < stdio.h >
#include < string .h >
#include < windows.h >
// 这是我们自己按常规算法实现的strlen,即逐个byte判断是否为0
size_t mystrlen( char * s)
{
char * p = s;
while ( * p) ++ p;
return (p - s);
}
int main( int argc, char * argv[])
{
char * s;
size_t result, i;
DWORD tickcount;
s = ( char * )malloc( 4096 );
memset(s, ' a ' , 4096 );
s[ 4091 ] = 0 ;
// 粗略测试两种strlen的时间
// 标准库中的strlen:
tickcount = GetTickCount();
for (i = 0 ; i < 20000 ; i ++ )
{
result = strlen(s);
}
printf( " strlen : %ld ms, result = %ld\n " , GetTickCount() - tickcount, result);
// 我们自己编写的strlen:
tickcount = GetTickCount();
for (i = 0 ; i < 20000 ; i ++ )
{
result = mystrlen(s);
}
printf( " mystrlen: %ld ms, result = %ld\n " , GetTickCount() - tickcount, result);
free(s);
return 0 ;
}
该测试的输出结果如下:
strlen : 16 ms, result = 4091
mystrlen: 218 ms, result = 4091
首先毫无疑问两种方法的时间复杂度相同(O(n)),区别在于常数系数不同,在这里我们自己写的函数的时间消耗大约是标准库版本的 13 倍之多。下面我们可以分别看下两个版本的汇编代码。
首先是我们自己编写的mystrlen版本,编译器产生的汇编代码如下:
代码
. text: 00401020 mystrlen proc near
. text: 00401020
. text: 00401020 var_44 = dword ptr -44h
. text: 00401020 var_4 = dword ptr - 4
. text: 00401020 arg_0 = dword ptr 8
. text: 00401020
. text: 00401020 push ebp
. text: 00401021 mov ebp, esp
. text: 00401023 sub esp, 44h
. text: 00401026 push ebx
. text: 00401027 push esi
. text: 00401028 push edi
. text: 00401029 lea edi, [ebp+var_44]
. text: 0040102C mov ecx, 11h
. text: 00401031 mov eax, 0CCCCCCCCh
. text: 00401036 rep stosd
. text: 00401038 mov eax, [ebp+arg_0] ;
. text: 0040103B mov [ebp+var_4], eax ; char* p = s;
. text: 0040103E
. text: 0040103E loc_40103E: ;
. text: 0040103E mov ecx, [ebp+var_4]
. text: 00401041 movsx edx, byte ptr [ecx] ; edx = *p;
. text: 00401044 test edx, edx ; if ( edx == 0 )
. text: 00401046 jz short loc_401053 ; break;
. text: 00401048 mov eax, [ebp+var_4]
. text: 0040104B add eax, 1
. text: 0040104E mov [ebp+var_4], eax ; ++p;
. text: 00401051 jmp short loc_40103E ;
. text: 00401053 ;
. text: 00401053
. text: 00401053 loc_401053: ; CODE XREF: mystrlen+26
. text: 00401020 mystrlen proc near
. text: 00401020
. text: 00401020 var_44 = dword ptr -44h
. text: 00401020 var_4 = dword ptr - 4
. text: 00401020 arg_0 = dword ptr 8
. text: 00401020
. text: 00401020 push ebp
. text: 00401021 mov ebp, esp
. text: 00401023 sub esp, 44h
. text: 00401026 push ebx
. text: 00401027 push esi
. text: 00401028 push edi
. text: 00401029 lea edi, [ebp+var_44]
. text: 0040102C mov ecx, 11h
. text: 00401031 mov eax, 0CCCCCCCCh
. text: 00401036 rep stosd
. text: 00401038 mov eax, [ebp+arg_0] ;
. text: 0040103B mov [ebp+var_4], eax ; char* p = s;
. text: 0040103E
. text: 0040103E loc_40103E: ;
. text: 0040103E mov ecx, [ebp+var_4]
. text: 00401041 movsx edx, byte ptr [ecx] ; edx = *p;
. text: 00401044 test edx, edx ; if ( edx == 0 )
. text: 00401046 jz short loc_401053 ; break;
. text: 00401048 mov eax, [ebp+var_4]
. text: 0040104B add eax, 1
. text: 0040104E mov [ebp+var_4], eax ; ++p;
. text: 00401051 jmp short loc_40103E ;
. text: 00401053 ;
. text: 00401053
. text: 00401053 loc_401053: ; CODE XREF: mystrlen+26