strtok_r сохранить состояние поведения - PullRequest
0 голосов
/ 20 февраля 2019

правильный способ использования strtok_r заключается в следующем:

char* str = strdup(string);
char* save;
char* ptr = strtok_r(str, delim, &save);
while(ptr) {
  puts(ptr);
  ptr = strtok_r(NULL, delim, &save);
}

При попытке проверить, что на самом деле хранится в save, я обнаружил, что это просто остальныенеразобранной строки.Поэтому я попытался сделать второй вызов похожим на первый и написал обертку следующим образом:

char* as_tokens(char** str, const char* const delim) {
  return strtok_r(NULL, delim, str);
}

Это можно использовать, как показано ниже, что гораздо менее многословно.Нам не нужно проводить различие между первым вызовом и отдыхом.

char* str = strdup(string);
char* ptr;
while(ptr = as_tokens(&str, delim))
  puts(ptr);

Есть ли недостатки в этом подходе?Я вызываю неопределенное поведение?Я пробовал некоторые крайние случаи, и оба подхода работают одинаково.

Онлайн-компилятор: https://wandbox.org/permlink/rkGiwXOUtzqrbMpP

PS Игнорирование утечек памяти для краткости.


Обновление

Уже существует функция, почти аналогичная моей as_tokens: strsep .Отличается в случае, когда есть последовательные разделители.strsep возвращает пустую строку, тогда как as_tokens (т.е. strtok_r) обрабатывает их как единое целое.

1 Ответ

0 голосов
/ 21 февраля 2019

Есть ли недостатки в этом подходе?

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

Кроме того, как было отмечено в комментариях, он не соответствует спецификациямstrtok_r в том смысле, что поведение вызова strtok_r с первым аргументом NULL определяется только в контексте предыдущего вызова strtok_r, который предоставил значение, на которое указывает третий аргумент.

Кроме того, он отходит от идиоматического, хорошо понятного использования strtok_r, даже заходя настолько далеко, что скрывает его в другой функции.Нормальная идиома не обременительна, и она хорошо известна и понятна.Из-за того, что он сообразителен, ваш код становится немного сложнее поддерживать.

Я вызываю какое-то неопределенное поведение?

Да, в смысле «поведение, которое не определено»", в отличие от поведения, которое явно вызывается как un определено.Но соответствующие стандарты придают такое же значение этим альтернативам.Смотри выше.

...