程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> dietlibc中的strcpy算法淺析

dietlibc中的strcpy算法淺析

編輯:關於C語言

我們將代碼稍作修改,讓一些宏定義變成函數更容易理解一些:

#include "stdafx.h" #include <stdio.h> /* fast strcpy -- Copyright (C) 2003 Thomas M. Ogrisegg <[email protected]> */ //#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.wikipedia.org/wiki/Data_structure_alignment)。

有的同學已經看出來了,如果源指針目標指針都沒對齊,xor結果也是零,那不就錯了麼?

OK,不還有一段代碼麼,在STRALIGN裡面,會對目標字符串指針地址取模,然後將余數返回,比如我們運行時人為地修改s1以及s2地址將其+1。debug運行如下圖,得到p以及str地址,可以看到都是對齊在unsigned long邊界上的( p & 3 一定是0)。

image

我們在Autos窗口裡直接修改地址,讓其加一,如下圖:

image

這樣兩個指針就都沒有對齊了。繼續運行:

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved