当前位置:编程学习 > C/C++ >>

dietlibc中的strcpy算法浅析

我们将代码稍作修改,让一些宏定义变成函数更容易理解一些:

#include "stdafx.h"
#include <stdio.h>
/* fast strcpy -- Copyright (C) 2003 Thomas M. Ogrisegg <tom@hi-tek.fnord.at> */
//#include <string.h>
//#include "dietfeatures.h"
//#include "dietstring.h"
// ----following are dietstring.h content.
//#include <endian.h>
//# define MKW(x) (x|x<<8|x<<16|x<<24)
int MKW(int x)
{
x = x | x<<8 | x << 16 | x << 24;
return x;
}

//# define STRALIGN(x) (((unsigned long)x&3)?4-((unsigned long)x&3):0)
unsigned long STRALIGN(unsigned long xPtr)
{
unsigned long xRet = (unsigned long)xPtr & 3;
if (xRet)
xRet = 4 - ((unsigned long) xPtr & 3);
else
xRet = 0;
return xRet;
}

/* GFC(x)    - returns first character */
/* INCSTR(x) - moves to next character */
# define GFC(x) ((x)&0xff)
# define INCSTR(x) do { x >>= 8; } while (0)

//#define UNALIGNED(x,y) (((unsigned long)x & (sizeof (unsigned long)-1)) ^ ((unsigned long)y & (sizeof (unsigned long)-1)))
unsigned long MyUnaligned(unsigned long xPtr, unsigned long yPtr)
{
unsigned long valN1 = sizeof (unsigned long)-1;
unsigned long xVal = (unsigned long) xPtr & valN1;
unsigned long yVal = (unsigned long) yPtr & valN1;
unsigned long retVal = xVal ^ yVal;
return retVal;
}
// ----above are dietstring.h content.

char *
strcpy2 (char *s1, const char *s2)
{
    char           *res = s1;
    int             tmp;
    unsigned long   l;

    if (MyUnaligned((unsigned long)s1, (unsigned long)s2)){
while ((*s1++ = *s2++));
return (res);
    }

    if ((tmp = STRALIGN((unsigned long)s1))){
while (tmp-- && (*s1++ = *s2++));
if (tmp != -1) return (res);
    }

    while (1) {
unsigned long key1 = MKW(0x1ul);
unsigned long key2 = MKW(0x80ul);

l = *(const unsigned long *) s2;

if (((l - key1) & ~l) & key2) {
while ((*s1++ = GFC(l))) INCSTR(l);
return (res);
}

*(unsigned long *) s1 = l;
s2 += sizeof(unsigned long);
s1 += sizeof(unsigned long);
}
}

int _tmain(int argc, _TCHAR* argv[])
{
char* p = (char*)malloc(50* sizeof p);
char* str = "aaaabbbbbcccccc";
strcpy2(p, str);
free(p);
return 0;
}
 
为了不和标准库的strcpy名字冲突,我将其改为strcpy2.

如果你把上面的程序编译运行一下就会发现,快的原因在于strcpy2这个函数最后一部分while循环里面的这几行:

 *(unsigned long *) s1 = l;

s2 += sizeof(unsigned long);

s1 += sizeof(unsigned long);

对C语言指针了解的朋友都知道,第一行是把l这个unsigned long类型变量值赋值给s1为地址的一个unsigned long型指针指向的内容。

在我的i386cpu PC机上,第二第三行分别是将s2以及s1指针增加了4(而不是通常函数实现里面的++)。这也就实现了每次拷贝4个char(也就是一个unsigned long)而不是只拷贝一个char。

而strcpy2前面的函数就是确保这个拷贝可以正确执行。

我们先看MyUnaligned这个函数(在dietlibc中原为UNALIGNED宏)。

先取了一个值是sizeof(unsigned long) – 1,然后将源字符串指针以及目标字符串指针都与这个值做与操作(xPtr & valN1),最后两个结果做一个异或xor操作(xVal ^ yVal)。

其实说白了很简单,xPtr & valN1相当于一个取模操作,i386 cpu上valN1的值为3,也就是与的结果可能为0,1,2,3,当xPtr或者yPtr的值为4的倍数时候,与操作得到结果为0。两个与操作结果做一下异或,只有都为0或者都为1的时候,返回为0。也就是只要有一个指针没对齐,就老老实实的做一个个char的拷贝(*s1++ = *s2++),然后从strcpy2返回。

这个算法就是为了保证xPtr以及yPtr指针都是在内存上是对齐的(aligned),如果没有对齐还要一次赋值4个char,那可能导致写入内存出错(参考这篇http://en.易做图.org/wiki/Data_structure_alignment)。

有的同学已经看出来了,如果源指针目标指针都没对齐,xor结果也是零,那不就错了么?

OK,不还有一段代码么,在STRALIGN里面,会对目标字符串指针地址取模,然后将余数返回,比如我们运行时人为地修改s1以及s2地址将其+1。debug运行如下图,得到p以及str地址,可以看到都是对齐在unsigned long边界上的( p & 3 一定是0)。

image

我们在Autos窗口里直接修改地址,让其加一,如下图:

image

这样两个指针就都没有对齐了。继续运行:

CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,