返回   cpper编程论坛 > Blog > tomato
注册账号 论坛帮助 会员列表 日历事件 搜索 今日新帖 标记版面已读

为这篇文章评分

CRT源码解析(三)字符串函数(2)

发表于 2006-05-22 11:52 AM 作者: tomato
CRT源码解析(三)

字符串函数(2)

0、代码
c++ 代码:
代码:
int main()
{
    const char* src = "Hello, world";
    char* dest = new char[100];

    strlen(src);
    strchr(src, ‘l’);
    strrchr(src, ‘l’);
    strcpy(dest, src);
    strcat(dest, src);
    _strset(dest, ‘a’);
    strpbrk(dest, src);
    strcmp(src, dest);
    strcoll(src, dest);
    strxfrm(dest, src, 10);
    strcspn(src, "word");
    strspn(dest, src);
    strstr("1452345678", "456");
    _strrev(dest);
    strncmp(src, dest, 10);
    strncpy(dest, src, 10);
    strncat(dest, src, 10);
    _strnset(dest, ‘a’, 10);

    return 0;
}
1、strlen
size_t strlen(const char *s);
strlen.asm中的定义:
asm 代码:
代码:
strlen  proc \
        buf:ptr byte
        OPTION PROLOGUE:NONE, EPILOGUE:NONE
        .FPO    ( 0, 1, 0, 0, 0, 0 )
string  equ     [esp + 4]
先判断是否aligned,若没有先aligned string
按int判断是否下一个4byte中有一个为0(类似memchr的算法)
asm 代码:
代码:
main_loop:
        mov     eax,dword ptr [ecx]     ; read 4 bytes
        mov     edx,7efefeffh
        add     edx,eax
        xor     eax,-1
        xor     eax,edx
        add     ecx,4
        test    eax,81010100h
        je      short main_loop
如果在这个int中找到一个char是0了,则分别
asm 代码:
代码:
test al, al
test ah, ah
test eax, 00ff0000h
test eax, 0ff000000h
依次je到相应的byte_0/byte_1/byte_2/byte_3去

tips:回顾一下memchr的实现,这俩实际是一样的。顺便再参考一下"Hacker’s Delight"的6-1 Find First 0-Byte就ok了



2、strchr & strrchr
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
strchr.asm中:
asm 代码:
代码:
strchr  proc \
        string:ptr byte, \
        chr:byte
先把string给align了
然后弄一个int里面全是这个需要找的byte,判断找到或是遇到串尾的方法和memchr类似,判断原串中是否有一个byte是0和xor上需要找的byte后是否有0。找到后仍然和memchr一样的判断。

strrchr.asm中:
asm 代码:
代码:
strrchr proc \
        uses edi, \
        string:ptr byte, \
        chr:byte
先用repne scasb找到字符串尾,再std了用repnz scasb从后往前找,最后记得cld

tips:
strrchr中的
add ecx, 1
neg ecx
为什么不写成not ecx?



3、strcpy & strcat & strncpy & strncat
char *strcpy(char * restrict s1,
const char * restrict s2);
char *strncpy(char * restrict s1,
const char * restrict s2,
size_t n);
char *strcat(char * restrict s1,
const char * restrict s2);
char *strncat(char * restrict s1,
const char * restrict s2,
size_t n);

strcat.asm
asm 代码:
代码:
strcpy  proc \
        dst:ptr byte, \
        src:ptr byte
先align了,判断\0的方法同其他一样,所以这里只能一个个复制,不能用rep stosd,更别说sse2了
asm 代码:
代码:
strcat  proc \
        dst:ptr byte, \
        src:ptr byte
strcat和strcpy实现一样,除了strcat先把dst走到了串尾,实际上strcpy在push了edi后就直接跳转到strcat的函数内了

strncpy.asm中的strncpy
strncat.asm中的strncat
看了前面那么多实现,这俩已经没有新意了



4、strset
strset.asm
asm 代码:
代码:
_strset proc \
        uses edi, \
        string:ptr byte, \
        val:byte
先repne scasb到串尾,得出长度,再rep stosb



5、strspn、strcspn、strpbrk
size_t strspn(const char *s1, const char *s2);
size_t strcspn(const char *s1, const char *s2);
char *strpbrk(const char *s1, const char *s2);
这3个实际实现大部分是一样的,就是判断是否找到和返回时略有不同,实现都在:strspn.asm中
预处理语句:
asm 代码:
代码:
ifdef SSTRCSPN
    _STRSPN_ equ <strcspn>
elseifdef SSTRPBRK
    _STRSPN_ equ <strpbrk>
else  ; SSTRCSPN
    SSTRSPN equ 1
    _STRSPN_ equ <strspn>
endif  ; SSTRCSPN
同样的处理手法在前一篇文章的memcpy和memmove也用到了
asm 代码:
代码:
_STRSPN_ proc \
        uses esi, \
        string:ptr byte, \
        control:ptr byte
push 8个0,得到一个256bit全是0的map,将control字符串出现的所有byte都在map中置位:
asm 代码:
代码:
lab listnext                    ; init char bit map
        mov     al,[edx]
        or      al,al
        jz      short listdone
        add     edx,1
        bts     map,eax
        jmp     short listnext
lab listdone
找string中第一个不在这个map中的byte:
asm 代码:
代码:
lab dstnext
        mov     al,[esi]
        or      al,al
        jz      short dstdone
        add     esi,1
        bt      map, eax
三个函数判断是否找到后的代码:
asm 代码:
代码:
ifdef SSTRSPN
        jc      short dstnext   ; strspn: found char, continue
elseifdef SSTRCSPN
        jnc     short dstnext   ; strcspn: did not find char, continue
elseifdef SSTRPBRK
        jnc     short dstnext   ; strpbrk: did not find char, continue
        lea     eax,[esi - 1]   ; found char, return address of it
endif  ; SSTRSPN
6、strcmp & strncmp & strcoll
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
int strcoll(const char *s1, const char *s2);
size_t strxfrm(char * restrict s1,
const char * restrict s2,
size_t n);

strcmp.asm
asm 代码:
代码:
strcmp  proc \
        str1:ptr byte, \
        str2:ptr byte
先判断str1是不是4bytes的倍数,如果是,跳转dodwords,否则跳转dopartial

dodwords先读入4bytes到eax,然后从al、ah……的顺序一个个比较,不等跳donene,遇到字符串结束跳doneeq
dopartial先判断str1是不是2bytes的倍数,如果是跳doword,否则从edx(是str1)中一次取一个byte来比较
doword每次取2个bytes放在ax中比较

strncmp.c
c++ 代码:
代码:
int __cdecl strncmp (
    const char * first,
    const char * last,
    size_t count
    )
strcoll.c
c++ 代码:
代码:
extern "C" int __cdecl strcoll (
        const char *_string1,
        const char *_string2
        )
strxfrm.c
c++ 代码:
代码:
extern "C" size_t __cdecl strxfrm (
        char *_string1,
        const char *_string2,
        size_t _count
        )
不懂locale,以后再来补充


tips:
1、donene中将eax置为1/-1的方法
asm 代码:
代码:
sbb     eax,eax
        sal     eax,1
        add     eax,1
执行这一步前是cmp str1 str2的语句,如果str1 < str2会有CF = 1,否则CF = 0

2、在c语言实现中(比如strncmp.c),虽然参数类型是const char*,但是比较的时候要转换为unsigned char*,比如:return (*(unsigned char *)first - *(unsigned char *)last);
因为若当前实现中char是signed(char究竟有没有符号是依实现定义的),当*first = 0而*last < 0的时候,应该返回负数表示str1 < str2,所以要强制转换为unsigned char*


7、strstr
char *strstr(const char *s1, const char *s2);
strstr.asm
asm 代码:
代码:
strstr  proc \
        str1:ptr byte, \
        str2:ptr byte
先判断str2是否为空,再判断str2是否只有一个字符,如果是,跳转strchr_call
取出str2的第一个字符,循环str1直到找到匹配str2第一个字符,再判断第二个字符是否匹配(str2的第二个字符不可能为0),此时保存遇到第一个字符时的地址,再判断剩余的字符串,不匹配时回到最前的循环,当str2到0时返回此时指向str1的位置,当str1到0时返回0

嗯,没有用kmp算法
评论 2 Email文章
评论总数 2

评论

旧
问一个问题,通过调试器我看到过这样一段代码:
0046C834 jmp D4D6:5877D804
0046C83B ja USER32_NULL_THUNK_DATA+1 (46C83Dh)
为什么:
*((PDWORD)0x0046C834) = 0x77D804EA ?
这段代码位于PE文件的.idata段。

Yu Zhao
fishmaster.blogbus.com
发表于 2008-01-22 08:09 PM 作者: Yu Zhao
旧
模拟标准函数strcpy,设计如下的复制字符串的函数:
char *STRCPY(char *s1, const char *s2);
它将字符串s2复制到s1所指向的字符串空间中,函数的返回值就是s1。

[email]hb8991000@yahoo.com.cn[/email]
发表于 2008-01-22 08:10 PM 作者: hbhg006
发表评论 发表评论
作者为 tomato 的最新文章

所有时间均为格林尼治时间 +9。现在的时间是 12:55 PM


Powered by vBulletin® 版本 3.7.0
版权所有 ©2000 - 2008,Jelsoft Enterprises Ltd.
(C) Copy Right All Right Reserved 2001 - 2007

Search Engine Friendly URLs by vBSEO 3.1.0