2012-07-03 1 views
1

Я беру строку ввода, которая разделяется пробелом и пытается прочитать данные на две целые переменные.Разбиение std :: string на два const char * s, в результате чего второй const char * перезаписывает первый

например: "0 1" должно указывать child1 == 0, child2 == 1.

код я использую следующим образом:

int separator = input.find(' '); 
const char* child1_str = input.substr(0, separator).c_str(); // Everything is as expected here. 
const char* child2_str = input.substr(
    separator+1, //Start with the next char after the separator 
    input.length()-(separator+1) // And work to the end of the input string. 
    ).c_str();  // But now child1_str is showing the same location in memory as child2_str! 
int child1 = atoi(child1_str); 
int child2 = atoi(child2_str);  // and thus are both of these getting assigned the integer '1'. 
// do work 

То, что происходит озадачивает меня нет конца. Я отслеживаю последовательность с отладчиком Eclipse (gdb). Когда функция запускается, отображаются child1_str и child2_str, которые имеют разные ячейки памяти (как и должно быть). После разделения строки на separator и получения первого значения, child1_str удерживает '0', как ожидалось.

Однако следующая строка, которая присваивает значение child2_str, не только присваивает правильное значение child2_str, но также перезаписывает child1_str. Я даже не имею в виду, что значение символа перезаписывается, я имею в виду, что отладчик показывает child1_str и child2_str для совместного использования того же места в памяти.

Что?

1) Да, я буду рад услышать другие предложения по преобразованию строки в int - так я научился это делать давно, и у меня никогда не было проблем с этим , поэтому никогда не нужно было менять:

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

3) Да, я знаю, что std :: string является C++ и const char * является стандартным C. atoi требует строку c. Я отмечаю это как C++, потому что вход будет абсолютно соответствовать как std :: string из используемой структуры.

ответ

4

Во-первых, превосходные решения.

В C++ 11 вы можете использовать новомодную std::stoi функцию:

int child1 = std::stoi(input.substr(0, separator)); 

В противном случае, вы можете использовать boost::lexical_cast:

int child1 = boost::lexical_cast<int>(input.substr(0, separator)); 

Теперь объяснение.

input.substr(0, separator) создает объект временныйstd::string, что умирает в точке с запятой. Вызов c_str() на этом временном объекте дает вам указатель, который действителен только до тех пор, пока не будет проживать. Это означает, что на следующей строке указатель уже недействителен. Выражение разыменования этого указателя имеет неопределенное поведение. Затем происходят странные вещи, как это часто бывает с неопределенным поведением.

+0

Спасибо за ответ с двойным заголовком. Муй полезен. –

4

Значение, возвращаемое c_str(), недействительно после разрушения строки.Так что, когда вы запустите эту строку:

const char* child1_str = input.substr(0, separator).c_str(); 

substr функция возвращает временную строку. После выполнения строки эта временная строка разрушается, а указатель child1_str становится недействительным. Доступ к этому указателю приводит к неопределенному поведению.

Что нужно сделать, это присвоить результат substr локальной переменной std::string. Затем вы можете вызвать c_str() на эту переменную, и результат будет действителен до тех пор, пока переменная не будет разрушена (в конце блока).

+0

Это кажется ох таким очевидным сейчас. Спасибо. –

1

input.substr возвращает временное std::string. Поскольку вы нигде не сохраняете его, он уничтожается. Все, что происходит потом, зависит только от вашей удачи.

Я рекомендую использовать istringstream.

3

Другие специалисты уже указали проблему с вашим текущим кодом. Вот как бы я сделать преобразование:

std::istringstream buffer(input); 

buffer >> child1 >> child2; 

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

+0

Мне нравится этот метод намного лучше, чтобы быть уверенным! –