dietlibc中的strcpy算法浅析
http://sunxiunan.com/?p=1640
我們將代碼稍作修改,讓一些宏定義變成函數(shù)更容易理解一些:
#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; }
為了不和標(biāo)準(zhǔn)庫的strcpy名字沖突,我將其改為strcpy2.
如果你把上面的程序編譯運(yùn)行一下就會發(fā)現(xiàn),快的原因在于strcpy2這個(gè)函數(shù)最后一部分while循環(huán)里面的這幾行:
*(unsigned long *) s1 = l;?
s2 += sizeof(unsigned long);
s1 += sizeof(unsigned long);
對C語言指針了解的朋友都知道,第一行是把l這個(gè)unsigned long類型變量值賦值給s1為地址的一個(gè)unsigned long型指針指向的內(nèi)容。
在我的i386cpu PC機(jī)上,第二第三行分別是將s2以及s1指針增加了4(而不是通常函數(shù)實(shí)現(xiàn)里面的++)。這也就實(shí)現(xiàn)了每次拷貝4個(gè)char(也就是一個(gè)unsigned long)而不是只拷貝一個(gè)char。
而strcpy2前面的函數(shù)就是確保這個(gè)拷貝可以正確執(zhí)行。
我們先看MyUnaligned這個(gè)函數(shù)(在dietlibc中原為UNALIGNED宏)。
先取了一個(gè)值是sizeof(unsigned long) – 1,然后將源字符串指針以及目標(biāo)字符串指針都與這個(gè)值做與操作(xPtr & valN1),最后兩個(gè)結(jié)果做一個(gè)異或xor操作(xVal ^ yVal)。
其實(shí)說白了很簡單,xPtr & valN1相當(dāng)于一個(gè)取模操作,i386 cpu上valN1的值為3,也就是與的結(jié)果可能為0,1,2,3,當(dāng)xPtr或者yPtr的值為4的倍數(shù)時(shí)候,與操作得到結(jié)果為0。兩個(gè)與操作結(jié)果做一下異或,只有都為0或者都為1的時(shí)候,返回為0。也就是只要有一個(gè)指針沒對齊,就老老實(shí)實(shí)的做一個(gè)個(gè)char的拷貝(*s1++ = *s2++),然后從strcpy2返回。
這個(gè)算法就是為了保證xPtr以及yPtr指針都是在內(nèi)存上是對齊的(aligned),如果沒有對齊還要一次賦值4個(gè)char,那可能導(dǎo)致寫入內(nèi)存出錯(參考這篇http://en.wikipedia.org/wiki/Data_structure_alignment)。
有的同學(xué)已經(jīng)看出來了,如果源指針目標(biāo)指針都沒對齊,xor結(jié)果也是零,那不就錯了么?
OK,不還有一段代碼么,在STRALIGN里面,會對目標(biāo)字符串指針地址取模,然后將余數(shù)返回,比如我們運(yùn)行時(shí)人為地修改s1以及s2地址將其+1。debug運(yùn)行如下圖,得到p以及str地址,可以看到都是對齊在unsigned long邊界上的( p & 3 一定是0)。
我們在Autos窗口里直接修改地址,讓其加一,如下圖:
這樣兩個(gè)指針就都沒有對齊了。繼續(xù)運(yùn)行:
果然如我們預(yù)計(jì)的retVal的值為0。
xRet返回值為4 – 1,也就是3。
3個(gè)字符串(“aaa”)被拷貝到目標(biāo)字符串里面,這時(shí)候目標(biāo)字符串指針位置是對齊的了。
這是如果有編程經(jīng)驗(yàn)的朋友可能已經(jīng)有疑問,開頭有可能沒對齊,也有可能結(jié)尾部分沒對齊啊,也就是尾巴部分一定是4的倍數(shù)么?未必,這時(shí)候這一段代碼就起作用了。
unsigned long key1 = MKW(0x1ul);
??????? unsigned long key2 = MKW(0x80ul);
運(yùn)算結(jié)果key1是0×01010101,key2結(jié)果是0×80808080,如果你看過Tony Bai寫的strlen源碼分析http://bigwhite.blogbus.com/logs/37753065.html ,就會發(fā)現(xiàn)這兩個(gè)有意思的數(shù)字同樣出現(xiàn)在glibc標(biāo)準(zhǔn)庫當(dāng)中。
((l – key1) & ~l) & key2我就不分析了,可以猜測到,這是對源字符串中NULL結(jié)尾符的檢測。當(dāng)檢測到有結(jié)尾符的時(shí)候,就做按char拷貝,然后返回。感興趣的可以參考TonyBai那篇文章,然后自己寫幾個(gè)test case測試一下。
整個(gè)函數(shù)就是這樣,分析完畢。
轉(zhuǎn)載于:https://www.cnblogs.com/lua5/archive/2010/12/09/1901591.html
總結(jié)
以上是生活随笔為你收集整理的dietlibc中的strcpy算法浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 脚本备份数据库
- 下一篇: vim为何打开文件乱码,而且之后sete