欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内核-strcpy

程序员文章站 2022-03-07 11:36:30
...

看libc里面实现的strcpy

说明一下,这个libc代码的来源貌似比较复杂,有来自BSD的,有来自GNU,还有一些其他的

#define UNALIGNED(x,y) (((unsigned long)x & (sizeof (unsigned long)-1)) ^ ((unsigned long)y & (sizeof (unsigned long)-1)))
#define STRALIGN(x) (((unsigned long)x&3)?4-((unsigned long)x&3):0)
# define MKW(x) (x|x<<8|x<<16|x<<24)
# define GFC(x) ((x)&0xff)
# define INCSTR(x) do { x >>= 8; } while (0);
 
char *
strcpy (char *s1, const char *s2)
{
    char           *res = s1;
#ifdef WANT_SMALL_STRING_ROUTINES
    while ((*s1++ = *s2++));
    return (res);
#else
    int             tmp;
    unsigned long   l;
 
    if (UNALIGNED(s1, s2)) {
    while ((*s1++ = *s2++));
    return (res);
    }
    if ((tmp = STRALIGN(s1))) {
    while (tmp-- && (*s1++ = *s2++));
    if (tmp != -1) return (res);
    }
 
    while (1) {
    l = *(const unsigned long *) s2;
    if (((l - MKW(0x1ul)) & ~l) & MKW(0x80ul)) {
        while ((*s1++ = GFC(l))) INCSTR(l);
        return (res);
    }
    *(unsigned long *) s1 = l;
    s2 += sizeof(unsigned long);
    s1 += sizeof(unsigned long);
    }
#endif
}

那些需要的宏我也给拎出来了,好看一些.
先来看这个简单的实现:WANT_SMALL_STRING_ROUTINES
while ((*s1++ = *s2++));
return (res);
就两行代码,也确实够简单的…这个不说.

再看后面的实现,前面两个宏的判断我也不想看,貌似是什么是否是四字节对齐的判断,猜的…
真正有意思的在这里while(1)这里,这里才是问题的关键.
DEBUG几次大约就能知道if语句里面判断字符串是否包含’\0’.
如果包含’\0’,那么就用while把剩余的字符串拷贝过去;
如果不包含’\0’,那么就用long拷贝,因为一次可以拷贝四个字节.
if语句外面的,基本上大家都知道意思了,问题就在里面的,里面猜是能猜到拷贝剩余的(不足四个字节的)字符串.
先来看:
while ((*s1++ = GFC(l))) INCSTR(l);
return (res);
这两句代码吧.
while( *si++ = GetFirstChar(l) ) //获取l的第一个字符
INC_STR(l); //往后偏移一个字节

那么他是怎么判断这个long里面是否有’\0’的呢?问题的关键就在
((l - MKW(0x1ul)) & ~l) & MKW(0x80ul)
这句代码上!
这句代码看这个很费解,我看了很长时间才看懂了.OK,提个问题,你怎么判断一个字节是否是0呢?
你也许会用==0,可是这样的代码只能对一个字节有效,这个libc用了一种比较复杂的办法:
byte i;
((i - 0x1) & ~i) & 0x80
他是这么判断的.
那句代码可以这么写(i-1) & (0xFF-i) & 0x80,我们画一个表就明白是怎么回事了.

i   0   1  2   … 127 128 129 … 253 254 255
i-1   255 0  1   … 126 127 128 … 252 253 254
255-i  255 254 253 … 128 127 126 … 2   1  0
&   255 <=0 <=1 … <=126 <=127 <=126 … <=2 <=1 <=0

有一个命题,N >= M,那么,(N & M) <= M 必然成立.(谁帮忙证明一下_)
那么(i-1) & (255-i)里面最大的数,也就指望中间这就几个数了,很可惜
127 & 127 = 127 < 128 = 0x80
所以他就用这个算法,去衡量一个byte是否是’\0’,也就是0.
是0的话,会返回255;否则返回0.
一个long里面有四个byte,只要有一个byte出现0,也就是出现字符串的终结符,都会是那个表达式变成一个非0的数字,从而他的目的达到了.

OK,再来回顾一下他是怎么做的呢?

  1. 读取四个字节,构成一个long
  2. 判断这个long里面有没有包含C String的终结符’\0’
  3. 包含的话,按byte拷贝
  4. 不包含的话,按long拷贝
  5. 回到1
相关标签: 内核