Ошибка памяти при копировании частей строки с помощью strtok

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

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

    int:float, int:float, int:float

Я хочу хранить эти значения каким-то образом, который сопоставляет их друг с другом, при этом целые числа являются ключами, а числа с плавающей запятой — значениями. Код, который я написал для этого, работает именно так, как я хочу, пока либо первый int представляет собой только одну цифру, либо присутствует несколько пар int:float. Если в строке есть только одна пара int:float, а первое целое состоит из двух цифр, то функция будет выполняться несколько раз без проблем, но в конечном итоге в строки index_token и ratio_token будет прочитан мусор, и программа выдаст ошибку сегментации. Если я запускаю программу в valgrind, этого не происходит, значит, это какая-то ошибка памяти. Строка считывается из файла каждый раз, когда эта функция выполняется. Когда я распечатываю const_ratios и ratios, они всегда такие, какими должны быть.

Вот мой код:

const char * const_ratios = m_world->GetConfig().NON_1_RESOURCE_RATIOS.Get();
cout << "Const_ratios: " << const_ratios;

char * ratios = new char[strlen(const_ratios)]; #make non const version of ratios
strcpy(ratios, const_ratios);                   #so that I can use strtok
cout << ", Ratios: " << ratios;

map<int, float> ratioMap;
char * ratio_tokens = strtok((char *)ratios, ",:");
while (ratio_tokens != NULL){

  char * index_token = new char[strlen(ratio_tokens)];
  strcpy(index_token, ratio_tokens);
  cout <<", Index token: " << index_token;

  ratio_tokens = strtok(NULL, ",:");
  char * value_token = new char[strlen(ratio_tokens)];
  strcpy(value_token, ratio_tokens);
  cout << ", Value token: " << value_token << endl;

  ratioMap[atoi(index_token)] = atof(value_token);
  ratio_tokens = strtok(NULL, ",:");

Кто-нибудь знает, почему это может происходить? Я предполагаю, что это должно быть связано с strtok (возможно, в связи с strcpy), но я не вижу, что мне не хватает.


person seaotternerd    schedule 20.11.2013    source источник


Ответы (2)


Для строки C требуется завершающий нулевой символ в конце строки, поэтому для вашего кода:

char * ratios = new char[strlen(const_ratios)]; 
strcpy(ratios, const_ratios);

Завершающий нулевой символ не добавляется к строке «соотношения», что вызовет проблемы для других функций. Например, функция 'strcpy()', если вы проверите реализацию функции, она проверит завершающий нулевой символ исходной строки, чтобы решить, завершен ли процесс копирования. Поэтому, если нет завершающего нулевого символа, это вызовет ошибки памяти.

Таким образом, приведенный выше код должен быть таким:

int n = strlen(const_ratios) +1
char * ratios = new char[n]; 
strcpy(ratios, const_ratios);
ratios[n-1] = '\0'
person Matt    schedule 20.11.2013

Вы не выделяете достаточно памяти. Вы выделяете strlen(ratio_tokens), но затем копируете еще один байт. Это одна из неприятностей со строками в стиле C — стиль C всегда на один байт больше, чем количество символов в строке. Поскольку вы пишете код на C++, почему бы не использовать std::string?

person David Schwartz    schedule 20.11.2013
comment
Спасибо за быстрый ответ! Дополнительный символ является нулевым терминатором? Я застрял в использовании строк в стиле C, потому что это часть гораздо более крупного (и более старого) проекта. Я попытался выделить дополнительный символ для index_token и value_token, но это не помогло. - person seaotternerd; 20.11.2013
comment
О, неважно, я понял это. Я тоже забыл добавить байт к отношениям. Спасибо! - person seaotternerd; 21.11.2013