Как работает функция strtok в C?

Я нашел этот образец программы, который объясняет функцию strtok:

#include <stdio.h>
#include <string.h>

int main ()
{
    char str[] ="- This, a sample string.";
    char * pch;
    printf ("Splitting string \"%s\" into tokens:\n",str);
    pch = strtok (str," ,.-");
    while (pch != NULL)
    {
        printf ("%s\n",pch);
        pch = strtok (NULL, " ,.-");
    }
    return 0;
}

Однако я не понимаю, как это возможно работать.

Как возможно, что pch = strtok (NULL, " ,.-"); возвращает новый токен. Я имею в виду, мы звоним strtokс NULL . Это не имеет большого смысла для меня.


person user2426316    schedule 13.01.2014    source источник
comment
en.cppreference.com/w/c/string/byte/strtok   -  person Sander De Dycker    schedule 13.01.2014
comment
Я нашел этот образец программы, который объясняет функцию strtok это не пример, который объясняет, а документация, так что вы можете прочитать здесь: man7.org/linux/man-pages/man3/strtok.3.html   -  person alk    schedule 13.01.2014
comment
Да и смысла никому нет.. так strtok_r() создали...   -  person vrdhn    schedule 13.01.2014
comment
ОТ: Кстати, это int main (void).   -  person alk    schedule 13.01.2014


Ответы (4)


Две вещи, которые нужно знать о strtok. Как уже упоминалось, он «сохраняет внутреннее состояние». Кроме того, он искажает строку, которую вы ему передаете. По сути, он напишет '\0', где найдет указанный вами токен, и вернет указатель на начало строки. Внутри он поддерживает местоположение последнего токена; и в следующий раз, когда вы его вызовете, он начнется оттуда.

Важным следствием этого является то, что вы не можете использовать strtok для строки типа const char* "hello world";, так как вы получите нарушение прав доступа при изменении содержимого строки const char*.

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

Пример - если у вас есть "this,is,a,string", последовательные вызовы strtok будут генерировать указатели следующим образом (^ - это возвращаемое значение). Обратите внимание, что '\0' добавляется там, где находятся токены; это означает, что исходная строка изменена:

t  h  i  s  ,  i  s  ,  a  ,  s  t  r  i  n  g \0         this,is,a,string

t  h  i  s  \0 i  s  ,  a  ,  s  t  r  i  n  g \0         this
^
t  h  i  s  \0 i  s  \0 a  ,  s  t  r  i  n  g \0         is
               ^
t  h  i  s  \0 i  s  \0 a  \0 s  t  r  i  n  g \0         a
                        ^
t  h  i  s  \0 i  s  \0 a  \0 s  t  r  i  n  g \0         string
                              ^

Надеюсь, это имеет смысл.

person Floris    schedule 13.01.2014

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

Из-за того, как работает strtok, вам необходимо убедиться, что вы связываетесь с многопоточной версией среды выполнения C, если вы пишете многопоточное приложение. Это гарантирует, что каждый поток получит собственное внутреннее состояние для strtok.

person Sean    schedule 13.01.2014

Функция strtok() сохраняет данные между вызовами. Он использует эти данные, когда вы вызываете его с указателем NULL.

Из http://www.cplusplus.com/reference/cstring/strtok/:

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

person Andy Thomas    schedule 13.01.2014
comment
Большинство современных сред выполнения хранят состояние в локальном хранилище потока. Это означает, что он потокобезопасен, но небезопасен при повторном использовании. - person David Heffernan; 13.01.2014
comment
Спасибо за исправление. - person Andy Thomas; 13.01.2014

Функция strtok хранит данные во внутренней статической переменной, которая используется всеми потоками.

Для безопасности потоков вы должны использовать strtok_r

С http://www.opensource.apple.com/source/Libc/Libc-167/string.subproj/strtok.c

Обратите внимание на static char *last;

char *
strtok(s, delim)
    register char *s;
    register const char *delim;
{
    register char *spanp;
    register int c, sc;
    char *tok;
    static char *last;


    if (s == NULL && (s = last) == NULL)
        return (NULL);

    /*
     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
     */
cont:
    c = *s++;
    for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
        if (c == sc)
            goto cont;
    }

    if (c == 0) {       /* no non-delimiter characters */
        last = NULL;
        return (NULL);
    }
    tok = s - 1;

    /*
     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
     * Note that delim must have one NUL; we stop if we see that, too.
     */
    for (;;) {
        c = *s++;
        spanp = (char *)delim;
        do {
            if ((sc = *spanp++) == c) {
                if (c == 0)
                    s = NULL;
                else
                    s[-1] = 0;
                last = s;
                return (tok);
            }
        } while (sc != 0);
    }
    /* NOTREACHED */
}
person JuanR    schedule 13.01.2014
comment
strtok_s в Windows - person David Heffernan; 13.01.2014