Где находится реализация strlen () в GCC?

может ли кто-нибудь указать мне на определение strlen() в GCC? Я был grepping релиз 4.4.2 около получаса (в то время как гуглить, как сумасшедший), и я не могу найти, где strlen() фактически реализован.

10 ответов


вы должны смотреть в glibc, а не GCC -- похоже, он определен в strlen.c -- вот ссылка на strlen.c для glibc версии 2.7... И вот ссылка на glibc SVN репозиторий Онлайн для strlen.c.

причина, по которой вы должны смотреть на glibc и не gcc является:

библиотека GNU C используется как the библиотека C в системе GNU и большинстве систем с Linux ядро.


Я понимаю, что этот вопрос 4yrs старый, но 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 (среди прочего) расширяется и складывается (на основе ранее упомянутой конфигурации/платформы)


здесь bsd реализация

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

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

Это то, что вы ищете? strlen () source. Вижу репозиторий git для получения дополнительной информации. The страница ресурсов glibc имеет ссылки на репозитории git, если вы хотите захватить их, а не смотреть на веб-представление.


хотя исходный плакат, возможно, не знал этого или искал это, gcc внутренне выравнивает ряд так называемых "встроенных" функций c, которые он определяет самостоятельно, включая некоторые функции mem*() и (в зависимости от версии gcc) strlen. В таких случаях версия библиотеки по существу никогда не используется, и указание человека на версию в glibc не является строго говоря правильным. (Он делает это по соображениям производительности-в дополнение к улучшению, которое встраивается сам производит, gcc "знает" определенные вещи о функциях, когда он их предоставляет, например, что strlen является чистой функцией и что он может таким образом оптимизировать несколько вызовов, или в случае функций mem* (), которые не имеют псевдонимов.)

для получения дополнительной информации об этом см. http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html


Поиск Кода Google является хорошей отправной точкой для таких вопросов. Обычно они указывают на различные источники и реализации функции.

в вашем конкретном случае: GoogleCodeSearch (strlen)

поиск кода Google был полностью закрыт в марте 2013


определена в 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 == '')
      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)

Я понимаю, что это старый вопрос, вы можете найти источники ядра linux в github здесь, а 32-разрядная реализация для strlen () может быть найдена в strlen_32.c на 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);

вы можете использовать этот код, чем проще, тем лучше !

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

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] = '';
    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 с датой запуска Q1 2017 и поддержкой AVX2, и поддержкой AVX2 является самой передовой из реализаций сборки, с датой запуска Q2 2013, в то время как с SSE2 гораздо древнее с 2004 года.

это где большая часть hardcoreness glibc приходит от: Она имеет много оптимизированную сводом рукописную сборку код.

протестировано в Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

-O3

TODO: с -O3, gcc не использует glibc's 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