Где находится реализация 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 сделал правильный выбор.