dietlibc中的strcpy算法浅析
我们将代码稍作修改,让一些宏定义变成函数更容易理解一些:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #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.wikipedia.org/wiki/Data_structure_alignment)。
有的同学已经看出来了,如果源指针目标指针都没对齐,xor结果也是零,那不就错了么?
OK,不还有一段代码么,在STRALIGN里面,会对目标字符串指针地址取模,然后将余数返回,比如我们运行时人为地修改s1以及s2地址将其+1。debug运行如下图,得到p以及str地址,可以看到都是对齐在unsigned long边界上的( p & 3 一定是0)。
我们在Autos窗口里直接修改地址,让其加一,如下图:
这样两个指针就都没有对齐了。继续运行:
果然如我们预计的retVal的值为0。
xRet返回值为4 – 1,也就是3。
3个字符串(“aaa”)被拷贝到目标字符串里面,这时候目标字符串指针位置是对齐的了。
这是如果有编程经验的朋友可能已经有疑问,开头有可能没对齐,也有可能结尾部分没对齐啊,也就是尾巴部分一定是4的倍数么?未必,这时候这一段代码就起作用了。
unsigned long key1 = MKW(0x1ul);
unsigned long key2 = MKW(0x80ul);
运算结果key1是0×01010101,key2结果是0×80808080,如果你看过Tony Bai写的strlen源码分析http://bigwhite.blogbus.com/logs/37753065.html ,就会发现这两个有意思的数字同样出现在glibc标准库当中。
((l – key1) & ~l) & key2我就不分析了,可以猜测到,这是对源字符串中NULL结尾符的检测。当检测到有结尾符的时候,就做按char拷贝,然后返回。感兴趣的可以参考TonyBai那篇文章,然后自己写几个test case测试一下。
整个函数就是这样,分析完毕。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器