Страницы

Поиск по вопросам

среда, 3 октября 2018 г.

Реализация strlen() на Си в одну строку

Давеча побывал на собеседовании, одним из заданий было реализовать функционал strlen() без применения сторонних функций, то есть руками. Как полный дилетант в Си я изобразил простой цикл. Мне было указано что данная реализация неэкономна, а также что ее можно записать без лишних переменных и в одну строку. Разумеется не в смысле засовывания все в одну строку, а именно иной алгоритм. Речь не шла о производительности или оптимизации с точки зрения практики, и как я понимаю, вопрос носил чисто академический характер. Это вызвало у меня интерес, так как на собеседовании я сделать этого не смог. Общаясь с более компетентным коллегой получил от него реализацию:
size_t str_len (const char *str) { return (*str) ? str_len(++str) + 1 : 0; }
Я бы пожалуй пока до такого сам не додумался бы, но теперь вопрос у коллеги - существует ли вариант в одну строку без рекурсии?
PS: завел также топик на SO на одноименную тему. Надеюсь будет интересно участвовавшим в дискуссии.


Ответ

Попробую тоже добавить небольшой комментарий к ответу Дож: действительно, если не прибегать к рекурсии, эти самые N итерации должны быть осуществлены с помощью некоего цикла, будь то for, while или do while, откуда возникает принципиальная невозможность сделать алгоритм в одну строку: если бы даже кому-то удалось написать такой однострочник при помощи любого из этих циклов, в любом случае понадобится как минимум ещё одна строка для того, чтобы вернуть результат: size_t custom_strlen(...) { for (крутой однострочник) {} return длина; // от этой строки никуда не убежать } С учётом этого соображения и того, что условие чётко оговаривает лишь "реализовать функционал strlen() без применения сторонних функций", я попробовал немного схитрить: void stanislaw_len(char * str, size_t *len) { for (*len = 0; *str; ++str, (*len)++); } P.S. Я знаю, что это хак, просто попробовал из спортивного интереса. Более интересно другое: я сравнил предложенные здесь методы и strlen между собой: void stanislaw_len(char * str, size_t *len) { for (*len = 0; *str; ++str, (*len)++); }
size_t KoVadim_len(char * t) { return (strchr(t, 0) - t) / sizeof(char); }
size_t avp_len (const char *str) { register const char *s = str;
while(*str++); return (size_t)(str - s - 1); }
char *STRING = "This is a rather long string! This is a rather long string! This is a rather long string!";
size_t N = 1000000; size_t M = 10;
__block size_t counter = 0;
Benchmark(M, ^{ for (int i = 0; i < N; i++) { size_t length;
stanislaw_len(STRING, &length);
counter += length; } });
NSLog(@"stanislaw_len %ld", counter);
... остальные аналогично Вот результаты для O3: The block have been run 10 times. Average is: 67.163414 milliseconds 2014-03-12 16:59:10.429 SandboxCommandLineApp[17606:303] stanislaw_len() 890000000 The block have been run 10 times. Average is: 72.448875 milliseconds 2014-03-12 16:59:11.156 SandboxCommandLineApp[17606:303] avp_len() 890000000 The block have been run 10 times. Average is: 12.458966 milliseconds 2014-03-12 16:59:11.281 SandboxCommandLineApp[17606:303] KoVadim_len() 890000000 The block have been run 10 times. Average is: 8.514538 milliseconds 2014-03-12 16:59:11.367 SandboxCommandLineApp[17606:303] strlen() 890000000 Вопрос к знатокам: что такого может быть вжато в Apple-овских libc dylib-ах, что такая большая разница есть в скорости? Особенно это непонятно в связи с тем, что последняя опубликованная open source - версия strlen.c содержит очень схожие посимвольные проходы, как у меня и @avp (вариант KoVadim тут не в счёт, так как он опять же полагается на системную библиотеку) ?

Комментариев нет:

Отправить комментарий