Повторная реализация strlen (C) - PullRequest
0 голосов
/ 02 июня 2018

Предупреждение Noob:

для целей обучения, мне было дано задание повторно реализовать функцию strlen (), я понял, что лучше всего это сделать с помощью функции, такой как макрос, а нефункция, я бы рассуждал так: при использовании макроса мне не нужно было бы передавать строку в функцию.

что вы думаете?в этом случае лучше создать правильную функцию или макрос?

1 Ответ

0 голосов
/ 02 июня 2018

Макросы раскрываются ровно один раз, когда программа компилируется.Поскольку путешествие во времени не является частью языка Си, для будущего выполнения программы невозможно задним числом изменить последствия макроса.Таким образом, если вычисление, такое как вычисление длины строки, зависит от информации, неизвестной при компиляции программы, макрос совершенно бесполезен.Если строка не является литералом, это будет иметь место.И я рискну заявить, что в подавляющем большинстве случаев строка, длина которой требуется, не существовала на момент компиляции программы.

Ясное понимание того, что на самом деле делают макросы, - изменить текст программы перед компиляцией- поможет избежать отвлекающих факторов, таких как предложение в этом вопросе.

Иногда бывает полезно использовать strlen для константного строкового литерала, чтобы избежать ошибок, которые могут появиться в будущем, когдаСтроковый литерал изменен.Например, следующее (которое проверяет, начинается ли line с текста Hello):

/* Code smell: magic number */
if (strncmp(line, "Hello", 5) == 0) { ... }

Было бы лучше записать как:

/* Code smell: redundant repetition, see below */
if (strncmp(line, "Hello", strlen("Hello")) == 0) { ... }

Очевидно, если вычислениеможно выполнить один раз во время компиляции, лучше сделать это, чем делать это несколько раз при запуске программы.Когда-то, когда компиляторы были примитивны и почти не могли понять поток управления, имело смысл беспокоиться о таких вещах, хотя даже тогда многие ручные оптимизации были слишком сложными для незначительных преимуществ.

Сегодня даже это оправдание недоступно для преждевременного оптимизатора.Большинство современных C-компиляторов идеально подходят для замены strlen("Hello"); на константу 5, так что библиотечная функция никогда не вызывается.Для достижения этой оптимизации не требуется макромагия.

Как указано, в тесте в примере все еще есть ненужное повторение строки префикса.Что мы действительно хотим написать:

 if (startsWith(line, "Hello")) { ... }

Здесь соблазн определить startsWith в качестве макроса будет очень сильным, поскольку кажется, что подстановки во время компиляции будет достаточно.Этого искушения следует избегать.Современные компиляторы также способны «включать» вызовы функций;то есть вставка тела вызова непосредственно в код.

Таким образом, определение:

static int startsWith(const char* line, const char* prefix) {
  return strncmp(line, prefix, strlen(prefix)) == 0;
}

будет таким же быстрым, как его макроэквивалент, и в отличие от макроса не будетприводят к проблемам, когда он вызывается со вторым аргументом с побочными эффектами:

/* Bad style but people do it */
if (startsWith(line, prefixes[++i])) { doAction(i); }

Как только вызов встроен, компилятор может затем продолжить применять другие оптимизации, такие как устранение вызова к strlen в случае, если аргумент префикса является строковым литералом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...