Почему strtok () считается небезопасным?

какая особенность(ы)strtok небезопасно (с точки зрения переполнения буфера), что мне нужно остерегаться?

что немного странно для меня это strtok_s (что является" безопасным") в Visual C++ имеет дополнительный параметр" context", но похоже, что это то же самое и в других отношениях... это одно и то же или на самом деле другое?

4 ответов


в соответствии с разделом strtok_s документ:

6.7.3.1 функция strtok_s функция strtok_s устраняет две проблемы в функции strtok:

  1. новый параметр s1max предотвращает хранение strtok_s за пределами строки лексемы. (Строка была разделена на жетоны и вход и выход функции since strtok_s хранит нулевые символы в строка.)
  2. A новый параметр ptr устраняет статическое внутреннее состояние, которое предотвращает повторный вход strtok (Подпункт 1.1.12). (ISO / IEC 9899 функция wcstok и ISO / IEC 9945 (POSIX) функция strtok_r исправить это проблема тож.)

в этом нет ничего опасного. Нужно просто понять как это работает и как это использовать. После написания кода и модульного теста требуется всего несколько дополнительных минут для повторного запуска модульного теста с valgrind, чтобы убедиться, что вы работаете с границами памяти. На man-странице говорит все это:

ошибки

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

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

strtok безопасен в Visual C++ (но нигде больше), поскольку он использует локальное хранилище потоков для сохранения своего состояния между вызовами. Во всем остальном глобальная переменная используется для сохранения состояния strtok ().

однако даже в VC++, где strtok является потокобезопасным, это все еще немного странно - вы не можете использовать strtok () s на разных строках в одном потоке одновременно. Например, это не будет работать хорошо:

     token = strtok( string, seps );
     while(token)
     {
        printf("token=%s\n", token)
        token2 = strtok(string2, seps);
        while(token2)  
        {
            printf("token2=%s", token2);
            token2 = strtok( NULL, seps );
        }
        token = strtok( NULL, seps );
     }

причина, почему это не будет работать хорошо только для каждого потока одно состояние можно сохранить в локальном хранилище потоков, и здесь потребуется 2 состояния - для первой строки и для второй строки. Поэтому, хотя strtok является потокобезопасным с VC++, он не является реентерабельным.

то, что strtok_s (или strtok_r везде) предоставляет - явное состояние, и с этим strtok становится reentrant.


Если у вас нет правильно завершенной строки null; вы окажетесь в переполнении буфера. Также обратите внимание (это то, что я узнал на горьком опыте) strtok, похоже, не заботится о внутренних строках. Т. е. наличие "hello"/"world"будет анализировать"hello"/"world", тогда как "hello / world"будет анализировать " hello world". Обратите внимание, что он разбивается на / и игнорирует тот факт, что он находится в круглой скобке.