glibc字符串小函数

来源:互联网 发布:杭州编程培训 编辑:程序博客网 时间:2024/05/16 15:31

1.  strlen函数

    自己先写一个看看:

    版本一

int strlen(const char *str)
{
    int len = 0;
    const char *p = str;
    while(*(p++) != '\0')
        ++len;
    return len;
}

版本二

int strlen(const char *str)
{
    int len = 0;
    const char *p = str;
    while(*(p++) != '\0');
        return (int)(p - str - 1);
}


对比一下glibc中的strlen函数:(保留原文缩进格式,删除了注释,ascii字符范围0~2^7-1)

#include <string.h>
#include <stdlib.h>

#undef strlen

size_t  strlen (str)
     const char *str;
{
  const char *char_ptr;
  const unsigned long int *longword_ptr;
  unsigned long int longword, himagic, lomagic;

  for (char_ptr = str; ((unsigned long int) char_ptr
            & (sizeof (longword) - 1)) != 0;
       ++char_ptr)
    if (*char_ptr == '\0')
      return char_ptr - str;

  longword_ptr = (unsigned long int *) char_ptr;

  himagic = 0x80808080L;
  lomagic = 0x01010101L;
  if (sizeof (longword) > 4)
    {
      himagic = ((himagic << 16) << 16) | himagic;
      lomagic = ((lomagic << 16) << 16) | lomagic;
    }
  if (sizeof (longword) > 8)
    abort ();

  for (;;)
    {
      longword = *longword_ptr++;

      if (((longword - lomagic) & ~longword & himagic) != 0)
    {
      const char *cp = (const char *) (longword_ptr - 1);

      if (cp[0] == 0)
        return cp - str;
      if (cp[1] == 0)
        return cp - str + 1;
      if (cp[2] == 0)
        return cp - str + 2;
      if (cp[3] == 0)
        return cp - str + 3;
      if (sizeof (longword) > 4)
        {
          if (cp[4] == 0)
        return cp - str + 4;
          if (cp[5] == 0)
        return cp - str + 5;
          if (cp[6] == 0)
        return cp - str + 6;
          if (cp[7] == 0)
        return cp - str + 7;
        }
    }
    }
}
libc_hidden_builtin_def (strlen)


    差别很大,首先处理没有在unsigned long对齐的几个字节,然后当地址在对齐的位置后,就以unsigned long为单位前进,也就是说每次处理4个字节(如果unsigned long是4个字节)或8个字节(如果unsigned long是8个字节)。

    核心在于这个分支语句

    if (((longword - lomagic) & ~longword & himagic) != 0)

    以unsigned long为4字节为例,longword只有两种可能:第一种longword包含的4个字节都是该字符串的内容;第二种是longword包含的前面i个字节是该字符串的内容,后面4-i个字节不属于该字符串,那么第i个字节必然是0。

    先看第一种,4个字节都是该字符串内容,那么~longword & himagic == 0x80808080,此时有两种情况,如果最后一个字节是0,那么longword - lomagic的前三个字节的地一个最高bit都是0,最后一个字节的最高bit为1,从而结果不为0;如果最后一个字符不是0,那么longword - lomagic的4个字节的最高bit都是0,从而结果为0。

    再看第二种,4个字节中前i个字节是该字符串的内容,后面4-i个字节不属于该字符串,那么第i个字节必然为0,此时~longword & himagic的前i个字节都满足最高bit为1,后4-i个字节最高bit是否为0未知,但没关系,因为此时第i个字节为0,那么longword - lomagic的第i个字节的最高bit为1,从而结果不为0。

    综合这两种情况,可以看出,当longword的4个字节中有0时,(longword - lomagic) & ~longword & himagic不为0,否则为0。如此,除了最后一次外,每处理4个字节的内容,需要的操作就是一个if判断,该if判断代价是一次减法,两次位与。与4次比较操作(可能包括若干次寄存器操作和4次位异或操作,加载4次的问题相信编译器早就优了),孰优孰劣呢?


2. strcmp函数

自己先写个看看:

int strcmp(const char *str1, const char *str2)
{
    const char *p1 = str1;
    const char *p2 = str2;
    while(*p1 != '\0')
    {
        if(*p1 == *p2)
        {
            ++p1;
            ++p2;
        }
        else
            break;
    }
    return *(p1) - *(p2);
}


看看glibc的:

#include <string.h>
#include <memcopy.h>

#undef strcmp

int

strcmp (p1, p2)
     const char *p1;
     const char *p2;
{
  register const unsigned char *s1 = (const unsigned char *) p1;
  register const unsigned char *s2 = (const unsigned char *) p2;
  unsigned char c1, c2;

  do
    {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0')
    return c1 - c2;
    }
  while (c1 == c2);

  return c1 - c2;
}
libc_hidden_builtin_def (strcmp)


还是有区别的吧,glibc中无论当前的字符是否相等,都一样让指针前进,但是用临时变量存储当前指针指向的字符。


3. strcpy函数

自己写个看看:

char* strcpy(char* dest, const char* src)
{
    if((dest == NULL) || (src == NULL))
        return NULL;
    char* pdest = dest;
    while((*pdest++ = *src++) != '\0');
    return dest;
}


glibc咋样呢:

#include <stddef.h>
#include <string.h>
#include <memcopy.h>
#include <bp-checks.h>

#undef strcpy

char *
strcpy (dest, src)
     char *dest;
     const char *src;
{
  char c;
  char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
  const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
  size_t n;

  do
    {
      c = *s++;
      s[off] = c;
    }
  while (c != '\0');

  n = s - src;
  (void) CHECK_BOUNDS_HIGH (src + n);
  (void) CHECK_BOUNDS_HIGH (dest + n);

  return dest;
}
libc_hidden_builtin_def (strcpy)


首先检查dest和src是否在要求的虚拟内存地址空间范围内,然后计算dest和src之间的距离,然后依次复制没一个字符,最后检查源字符串和目标字符串是否超出了要求的虚拟内存地址空间,最后返回目标字符串地址。


4. strcat函数

自己写个看看:

char* strcat(char* dest, const char* src)
{
    char* p1 = dest;
    const char* p2 = src;
    while(*p1++ != '\0')
        ;
    --p1;
    while((*p1++ = *p2++) != '\0')
        ;
    return dest;
}


glibc咋样呢:

#include <string.h>
#include <memcopy.h>

#undef strcat

char *
strcat (dest, src)
     char *dest;
     const char *src;
{
  char *s1 = dest;
  const char *s2 = src;
  char c;

  do
    c = *s1++;
  while (c != '\0');

  s1 -= 2;

  do
    {
      c = *s2++;
      *++s1 = c;
    }
  while (c != '\0');

  return dest;
}
libc_hidden_builtin_def (strcat)


总是用一个临时变量作为中介,在前面的函数也是;另注意原来对s1 -= 2这一句的注释:通过这一处理,使得后面可以在循环中用*++s1 = c,对于流水线cpu来说,在读内存的同时可以递增s1;前置++要比后置++高效。




















  

   

 

   

原创粉丝点击