С++ strtok в функции изменяет исходное строковое значение в качестве параметра

когда я использую strtok для токенизации строки C++, возникает запутанная проблема, см. простой код ниже:

void a(string s){
    strtok((char*)s.c_str(), " ");
}
int main(){
    string s;
    s = "world hello";
    a(s);
    cout<<s<<endl;
    return 0;
}

программа выводит "мир". Разве он не должен выводить «привет миру»? Поскольку я передаю строку в качестве параметра значения функции a, strtok не должен изменять исходный s... Кто-нибудь может объяснить этот трюк. благодарю вас.


person stackunderflow    schedule 01.10.2011    source источник
comment
Не делайте этого, вы спрашиваете о проблемах с повреждением памяти. Да, strtok изменяет свой ввод. cplusplus.com/reference/clibrary/cstring/strtok   -  person John Carter    schedule 01.10.2011


Ответы (3)


Проблема в том, что (char*)s.c_str() вы отбросили константность и изменили содержимое string не так, как предполагалось. Хотя исходный s не следует изменять, я полагаю, вы могли быть поражены умной оптимизацией, которая ожидает, что вы будете играть по правилам. Например, COW-реализация string показала бы такое поведение.

person K-ballo    schedule 01.10.2011
comment
спасибо. Кстати, знаете ли вы, как безопасно и эффективно преобразовать строку C++ в char*, чтобы я мог без проблем использовать char* в strtok? - person stackunderflow; 01.10.2011
comment
@zwx: если вы собираетесь изменить char*, вам нужно сделать его копию. - person K-ballo; 01.10.2011

c_str() возвращает указатель const, который является обещанием компилятору, что объект, на который он указывает, не будет изменен. И затем вы вызываете strtok, который изменяет его.

Когда вы лжете компилятору, вы будете наказаны.

person Chris Eberle    schedule 01.10.2011
comment
У std::string нет проблем с нулями. - person K-ballo; 01.10.2011

Так работает strtok(). Он использует первый параметр в качестве буфера. Приведя его к char*, вы позволяете ему изменять строку. strtok() ничего не знает об исходном std::string. Он также хранит указатель строки в статической переменной, поэтому в следующий раз вы должны вызывать его с нулевым указателем, чтобы продолжить анализ той же строки.

Кстати, в С++ вместо этого следует использовать std::istringstream. Он не использует внутреннюю статическую переменную, которая не является потокобезопасной. И вы можете извлечь параметры непосредственно в int, double и т. д., как мы делаем с cin. std::ostringstring заменяет sprintf().

person PRouleau    schedule 01.10.2011