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

发布于 2024-08-11 03:45:26 字数 120 浏览 9 评论 0 原文

谁能给我指出 GCC 中 strlen() 的定义吗?我已经 grep 4.4.2 版本大约半个小时了(同时疯狂地谷歌搜索),我似乎找不到 strlen() 实际实现的位置。

Can anyone point me to the definition of strlen() in GCC? I've been grepping release 4.4.2 for about a half hour now (while Googling like crazy) and I can't seem to find where strlen() is actually implemented.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

活泼老夫 2024-08-18 03:45:26

您应该查看 glibc,而不是 GCC - 它似乎是在 strlen.c 中定义的 - 这是 strlen.c for glibc version 2.7... 这里是 strlen.c 的 glibc SVN 在线存储库

您应该查看 glibc 而不是 gcc 的原因是:

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

You should be looking in glibc, not GCC -- it seems to be defined in strlen.c -- here's a link to strlen.c for glibc version 2.7... And here is a link to the glibc SVN repository online for strlen.c.

The reason you should be looking at glibc and not gcc is:

The GNU C library is used as the C library in the GNU system and most systems with the Linux kernel.

青朷 2024-08-18 03:45:26

这是 bsd 实现

size_t
strlen(const char *str)
{
        const char *s;

        for (s = str; *s; ++s)
                ;
        return (s - str);
}

Here's the bsd implementation

size_t
strlen(const char *str)
{
        const char *s;

        for (s = str; *s; ++s)
                ;
        return (s - str);
}
沉溺在你眼里的海 2024-08-18 03:45:26

我意识到这个问题已经有 4 年历史了,但是如果您不 #include 并且没有答案(包括接受的答案)对此进行解释。如果你忘记了,你会得到一个警告:

file_name:line_number: warning: incompromedimplicit statements ofbuilt-in function 'strlen'

并且gcc将内联它的副本,该副本在x86上是repnz scasb asm变体,除非您传递 -Werror 或 -fno-builtin。与此相关的文件在 gcc/config//.{c,md} 中,

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

I realize this question is 4yrs old, but gcc will often include its own copy of strlen if you do not #include <string.h> and none of the answers (including the accepted answer) account for that. If you forget, you will get a warning:

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

and gcc will inline its copy which on x86 is the repnz scasb asm variant unless you pass -Werror or -fno-builtin. The files related to this are in gcc/config/<platform>/<platform>.{c,md}

It is also controlled by gcc/builtins.c. In case you wondered if and how a strlen() was optimized to a constant, see the function defined as tree c_strlen(tree src, int only_value) in this file. It also controls how strlen (amongst others) is expanded and folded (based on the previously mentioned config/platform)

夜吻♂芭芘 2024-08-18 03:45:26

定义于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)

defined in 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)
笑饮青盏花 2024-08-18 03:45:26

glibc 2.26 有几个手工优化的 strlen 汇编实现 从

glibc-2.26 开始,快速:

git ls-files | grep strlen.S

在 glibc 树中显示了十几个汇编手工-所有主要拱门和变体的优化实现。

特别是,x86_64 本身就有 3 种变体:

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

Off the bat:

disass main

contains:

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 年第一季度,支持 AVX2,以及 AVX2 是最先进的汇编实现,发布日期为 2013 年第二季度,而 SSE2 从 2004 年开始就更加古老。

这就是 glibc 的核心性很大一部分来自于:它有很多经过架构优化的手写汇编代码。

在 Ubuntu 17.10、gcc 7.2.0、glibc 2.26 中测试。

-O3

TODO:使用-O3,gcc不使用glibc的strlen,它只是生成内联汇编,这提到的是: 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

glibc 2.26 has several hand optimized assembly implementations of strlen

As of glibc-2.26, a quick:

git ls-files | grep strlen.S

in the glibc tree shows a dozen of assembly hand-optimized implementations for all major archs and variations.

In particular, x86_64 alone has 3 variations:

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

A quick and dirty way to determine which one is used, is to step debug a test program:

#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;
}

compiled with:

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

Off the bat:

disass main

contains:

callq  0x555555554590 <strlen@plt>

so the libc version is being called.

After a few si instruction level steps into that, GDB reaches:

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

which tells me that strlen-avx2.S was used.

Then, I further confirm with:

disass __strlen_avx2

and compare the disassembly with the glibc source.

It is not surprising that the AVX2 version was used, since I have an i7-7820HQ CPU with launch date Q1 2017 and AVX2 support, and AVX2 is the most advanced of the assembly implementations, with launch date Q2 2013, while SSE2 is much more ancient from 2004.

This is where a great part of the hardcoreness of glibc comes from: it has a lot of arch optimized hand written assembly code.

Tested in Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

-O3

TODO: with -O3, gcc does not use glibc's strlen, it just generates inline assembly, which is mentioned at: https://stackoverflow.com/a/19885891/895245

Is it because it can optimize even better? But its output does not contain AVX2 instructions, so I feel that this is not the case.

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

Deficiencies of GCC's optimizer

glibc has inline assembler versions of various string functions; GCC has some, but not necessarily the same ones on the same architectures. Additional optab entries, like the ones for ffs and strlen, could be provided for several more functions including memset, strchr, strcpy and strrchr.

My simple tests show that the -O3 version is actually faster, so GCC made the right choice.

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

So尛奶瓶 2024-08-18 03:45:26

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

有关这方面的更多信息,请参阅 http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

Although the original poster may not have known this or been looking for this, gcc internally inlines a number of so-called "builtin" c functions that it defines on its own, including some of the mem*() functions and (depending on the gcc version) strlen. In such cases, the library version is essentially never used, and pointing the person at the version in glibc is not strictly speaking correct. (It does this for performance reasons -- in addition to the improvement that inlining itself produces, gcc "knows" certain things about the functions when it provides them, such as, for example, that strlen is a pure function and that it can thus optimize away multiple calls, or in the case of the mem*() functions that no aliasing is taking place.)

For more information on this, see http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

南笙 2024-08-18 03:45:26

这是您要找的吗? 斯特伦() 来源。有关详细信息,请参阅 git 存储库glibc 资源页面 有 git 存储库的链接(如果您想获取它们)而不是看网页视图。

Is this what you are looking for? strlen() source. See the git repository for more information. The glibc resources page has links to the git repositories if you want to grab them rather than looking at the web view.

甜`诱少女 2024-08-18 03:45:26

Google 代码搜索 是解决此类问题的一个很好的起点。它们通常指向函数的各种不同来源和实现。

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

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

Google Code Search is a good starting point for questions like that. They usually point to various different sources and implementations of a function.

In your particular case: GoogleCodeSearch(strlen)

Google Code Search was completely shut down on March 2013

野心澎湃 2024-08-18 03:45:26

我意识到这是老问题了,你可以在 github 这里 找到 linux 内核源代码,以及 32 位内核源代码strlen() 的实现可以在 strlen_32.c< 中找到/a> 在 github 上。提到的文件有这个实现。

#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);

I realize that this is old question, you can find the linux kernel sources at github here, and the 32 bit implementation for strlen() could be found in strlen_32.c on github. The mentioned file has this implementation.

#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);
小兔几 2024-08-18 03:45:26

您可以使用此代码,越简单越好!

size_t Strlen ( const char * _str )
{
    size_t i = 0;
    while(_str[i++]);
    return i;
}

You can use this code, the simpler the better !

size_t Strlen ( const char * _str )
{
    size_t i = 0;
    while(_str[i++]);
    return i;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文