代码之家  ›  专栏  ›  技术社区  ›  Chris Tonkinson

gcc中strlen()的实现在哪里?

  •  18
  • Chris Tonkinson  · 技术社区  · 15 年前

    有人能给我指一下 strlen() 在海湾合作委员会?我已经在4.4.2版上花了大约半个小时了(疯狂地谷歌),我似乎找不到在哪里。 斯特朗() 实际实现。

    10 回复  |  直到 6 年前
        1
  •  30
  •   Mark Rushakoff    15 年前

    你应该看glibc,而不是gcc——它似乎是在 strlen.c --这里有一个链接指向 strlen.c for glibc version 2.7 …这里有一个链接指向 glibc SVN repository online for strlen.c .

    你应该关注的原因 glibc 而不是GCC是:

    GNU C库用作 这个 GNU系统中的C库和大多数具有Linux内核的系统。

        2
  •  10
  •   technosaurus    11 年前

    我知道这个问题已经存在4年了,但GCC通常会将其 拥有 如果没有,请复制strlen #include <string.h> 所有的答案(包括公认的答案)都不能说明这一点。如果你忘记了,你会得到一个警告:

    file_name:line_number: warning: incompatible implicit declaration of built-in function 'strlen'

    GCC将内联它的副本,它在x86上是repnz scasb asm变体,除非您传递-werror或-fno builtin。与此相关的文件位于 gcc/config/<platform>/<platform>.{c,md}

    它还由gcc/builtins.c.控制。如果您想知道strlen()是否以及如何优化为常量,请参见定义为 tree c_strlen(tree src, int only_value) 在这个文件中。它还控制如何扩展和折叠strlen(以及其他方法)(基于前面提到的配置/平台)

        3
  •  7
  •   Ultimater    9 年前

    这里是 bsd 实施

    size_t
    strlen(const char *str)
    {
            const char *s;
    
            for (s = str; *s; ++s)
                    ;
            return (s - str);
    }
    
        4
  •  3
  •   faran    15 年前

    这就是你要找的吗? strlen() source . 见 git repository 更多信息。这个 glibc resources page 如果您想获取Git存储库的链接,而不是查看Web视图,则可以使用这些链接。

        5
  •  3
  •   Perry    13 年前

    尽管最初的海报可能不知道这一点,或者一直在寻找这一点,但GCC内部会引入许多它自己定义的所谓“内置”C函数,包括一些mem*()函数和(取决于GCC版本)strlen。在这种情况下,库版本基本上从未被使用过,并且在glibc中指出这个版本的人严格来说是不正确的。(它这样做是出于性能原因——除了内联本身所产生的改进之外,GCC在提供函数时“知道”一些关于函数的事情,例如,strlen是一个纯函数,因此它可以优化多个调用,或者在mem*()函数没有发生别名的情况下。)

    有关详细信息,请参阅 http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

        6
  •  3
  •   martinec cschol    8 年前

    Google Code Search 对于这样的问题是一个很好的起点。它们通常指向函数的各种不同的源和实现。

    在您的特定情况下: GoogleCodeSearch(strlen)

    谷歌代码搜索于2013年3月完全关闭

        7
  •  3
  •   Beyondo    6 年前

    定义在 glibc/string/strlen.c

    #include <string.h>
    #include <stdlib.h>
    
    #undef strlen
    
    #ifndef STRLEN
    # define STRLEN strlen
    #endif
    
    /* Return the length of the null-terminated string STR.  Scan for
       the null terminator quickly by testing four bytes at a time.  */
    size_t
    STRLEN (const char *str)
    {
      const char *char_ptr;
      const unsigned long int *longword_ptr;
      unsigned long int longword, himagic, lomagic;
    
      /* Handle the first few characters by reading one character at a time.
         Do this until CHAR_PTR is aligned on a longword boundary.  */
      for (char_ptr = str; ((unsigned long int) char_ptr
                & (sizeof (longword) - 1)) != 0;
           ++char_ptr)
        if (*char_ptr == '\0')
          return char_ptr - str;
    
      /* All these elucidatory comments refer to 4-byte longwords,
         but the theory applies equally well to 8-byte longwords.  */
    
      longword_ptr = (unsigned long int *) char_ptr;
    
      /* Bits 31, 24, 16, and 8 of this number are zero.  Call these bits
         the "holes."  Note that there is a hole just to the left of
         each byte, with an extra at the end:
    
         bits:  01111110 11111110 11111110 11111111
         bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
    
         The 1-bits make sure that carries propagate to the next 0-bit.
         The 0-bits provide holes for carries to fall into.  */
      himagic = 0x80808080L;
      lomagic = 0x01010101L;
      if (sizeof (longword) > 4)
        {
          /* 64-bit version of the magic.  */
          /* Do the shift in two steps to avoid a warning if long has 32 bits.  */
          himagic = ((himagic << 16) << 16) | himagic;
          lomagic = ((lomagic << 16) << 16) | lomagic;
        }
      if (sizeof (longword) > 8)
        abort ();
    
      /* Instead of the traditional loop which tests each character,
         we will test a longword at a time.  The tricky part is testing
         if *any of the four* bytes in the longword in question are zero.  */
      for (;;)
        {
          longword = *longword_ptr++;
    
          if (((longword - lomagic) & ~longword & himagic) != 0)
        {
          /* Which of the bytes was the zero?  If none of them were, it was
             a misfire; continue the search.  */
    
          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)
    
        8
  •  1
  •   A.B.    8 年前

    我知道这是个老问题,你可以在Github上找到Linux内核源代码。 here 和32位的strlen()实现可以在 strlen_32.c 在吉瑟布上。上述文件具有此实现。

    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/module.h>
    
    size_t strlen(const char *s)
    {
        /* Get an aligned pointer. */
        const uintptr_t s_int = (uintptr_t) s;
        const uint32_t *p = (const uint32_t *)(s_int & -4);
    
        /* Read the first word, but force bytes before the string to be nonzero.
         * This expression works because we know shift counts are taken mod 32.
         */
        uint32_t v = *p | ((1 << (s_int << 3)) - 1);
    
        uint32_t bits;
        while ((bits = __insn_seqb(v, 0)) == 0)
            v = *++p;
    
        return ((const char *)p) + (__insn_ctz(bits) >> 3) - s;
    }
    EXPORT_SYMBOL(strlen);
    
        9
  •  0
  •   Alisher Aliev    7 年前

    你可以用这个代码,越简单越好!

    size_t Strlen ( const char * _str )
    {
        size_t i = 0;
        while(_str[i++]);
        return i;
    }
    
        10
  •  0
  •   Ciro Santilli OurBigBook.com    6 年前

    glibc 2.26有几个手工优化的装配实现 strlen

    至于 glibc-2.26 ,快速:

    git ls-files | grep strlen.S
    

    在glibc树中,显示了许多针对所有主要架构和变体的手工装配优化实现。

    特别是,仅x86_64有三种变体:

    sysdeps/x86_64/multiarch/strlen-avx2.S
    sysdeps/x86_64/multiarch/strlen-sse2.S
    sysdeps/x86_64/strlen.S
    

    确定使用哪一种测试程序的一种快速而肮脏的方法是逐步调试测试程序:

    #include <assert.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    int main(void) {
        size_t size = 0x80000000, i, result;
        char *s = malloc(size);
        for (i = 0; i < size; ++i)
            s[i] = 'a';
        s[size - 1] = '\0';
        result = strlen(s);
        assert(result == size - 1);
        return EXIT_SUCCESS;
    }
    

    编译时使用:

    gcc -ggdb3 -std=c99 -O0 a.c
    

    蝙蝠:

    disass main
    

    包含:

    callq  0x555555554590 <strlen@plt>
    

    因此正在调用libc版本。

    几分钟后 si 指令级进入,gdb达到:

    __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:52                                         
    52      ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
    

    这告诉我 strlen-avx2.S 使用。

    然后,我进一步确认:

    disass __strlen_avx2
    

    并与glibc源代码进行了比较。

    使用avx2版本并不奇怪,因为我有一个 i7-7820HQ CPU,上市日期为2017年第1季度,支持AVX2,以及 AVX2 是最先进的组件实现,发布日期为2013年第2季度,而 SSE2 从2004年开始更古老。

    这就是glibc的核心核心部分的来源:它有很多优化的手工编写的汇编代码。

    在Ubuntu 17.10、GCC 7.2.0、Glibc 2.26中测试。

    -O3

    托多:与 -臭氧 ,gcc不使用glibc 斯特伦 ,它只生成内联程序集,如下所述: https://stackoverflow.com/a/19885891/895245

    是因为它可以更好地优化吗?但它的输出不包含AVX2指令,所以我觉得情况并非如此。

    https://www.gnu.org/software/gcc/projects/optimize.html 提到:

    GCC优化器的缺陷

    glibc有各种字符串函数的内联汇编程序版本;gcc有一些,但不一定在相同的体系结构上有相同的函数。其他optab条目,如ffs和strlen的条目,可以为其他几个函数提供,包括memset、strchr、strcpy和strrchr。

    我的简单测试表明 -O3 版本实际上更快,所以GCC做出了正确的选择。

    问: https://www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs-when-using-optimization-level-O3