Правильно ли всегда сохранять первый параметр strtok_s как NULL? - PullRequest
0 голосов
/ 28 июня 2018

Раньше я считал, что при первом вызове strtok_s() строка первого токена должна содержать строку, содержащую токен, например, следующий код:

char testString[100] = "1|2|3";
char *context = testString;
const char *token = strtok_s( testString, "|", &context );
while ( token )
    token = strtok_s( NULL, "|", &context );

Однако я видел, что кто-то всегда сохраняет первый параметр как NULL, например, следующий код:

char testString[100] = "1|2|3";
char *context = testString;
const char *token = strtok_s( NULL, "|", &context );
while ( token )
    token = strtok_s( NULL, "|", &context );

Я знаю, что это работает и как это работает. Потому что context указывает на тот же буфер, что и testString. Но я чувствую себя немного странно, и я сомневаюсь:

  1. Это хорошая практика использования strtok_s()? С какими потенциальными ошибками он может столкнуться?
  2. Если это хорошая практика, почему strtok_s() все еще нужно сохранять первый параметр? Это может быть NULL как всегда, не так ли?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018
  1. Это хорошая практика использования strtok_s()?

Нет, это плохая практика.

Даже если это работает (как здесь), это плохо , потому что вы должны задать вопрос. Вы задаете вопрос, потому что код выглядит удивительно, а это означает, что автор этого кода потратил впустую ваше время, усложнив понимание, чем необходимо.

... С какими потенциальными ошибками он может столкнуться?

  • другая библиотека C могла бы реализовать эту функцию по-другому
  • другой разработчик может переместить или изменить назначение на context, потому что это необычно использовать как это
  1. Если это хорошая практика, почему strtok_s () по-прежнему должен сохранять первый параметр?

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

Согласованные интерфейсы менее удивительны, их легче рассуждать и легче избежать путаницы. Этот прототип сохраняет согласованность с другими существующими интерфейсами (хотя я вижу, что вы используете MS strtok_s, а не стандартную версию C11) - если вы удалите первый аргумент, очевидное упорядочение исходной строки и аргументов разделителя будет изменено по сравнению с другие strtok функции.

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

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

Дальнейшие цитаты из стандарта C11 (выделено мое), раздел K.3.7.3.1 (стр. 616):

  1. Последовательность вызовов функции strtok_s разбивает строку, указанную s1, в последовательность токенов, каждый из которых ограничен символом из строки, указанной на по s2. Четвертый аргумент указывает на предоставленный вызывающим указатель на символ, в который Функция strtok_s хранит информацию, необходимую для продолжения сканирования. строка .

  2. Первый вызов в последовательности имеет ненулевой первый аргумент , а s1max указывает на объект значение которого равно количеству элементов в массиве символов, указанном первым аргумент. Первый вызов сохраняет начальное значение в объекте, на который указывает ptr и обновляет значение, на которое указывает s1max, для отражения количества элементов, которые остаются в отношение к ptr. Последующие вызовы в последовательности имеют нулевой первый аргумент, а объекты, на которые указывают s1max и ptr, должны иметь значения, хранящиеся в предыдущий вызов в последовательности, которые затем обновляются . Строка разделителя, на которую указывает s2 может отличаться от звонка к звонку.

Таким образом, стандарт говорит, что правильное использование заключается в том, чтобы вызывать strtok_s с ненулевым первым аргументом, а затем вызывать его с пустым первым аргументом. При первом вызове функция инициализирует некоторое состояние и использует предоставленный указатель (последний аргумент) для его сохранения.

Стандарт не упоминает, как следует использовать последний аргумент, вместо того, чтобы хранить состояние, чтобы функция могла продолжить поиск той же строки при вызове с неизмененным указателем. В основном это устраняет необходимость во внутреннем состоянии strtok, так что вы можете, например, токенизировать несколько строк одновременно.

Способ использования пространства состояний, таким образом, определяет реализацию . Вполне может быть, что в некоторых реализациях он просто помещает туда начальную строку и всегда вызывает ее с первым аргументом NULL, как вы показали. Но нет никакой гарантии, что это произойдет во всех реализациях или что такое поведение останется таким же в будущих версиях библиотеки.

Чтобы прямо ответить на ваш вопрос, да, это может сработать, но нет, это не очень хорошая идея.

...