Проблема со strtok и ошибкой сегментации

#c #segmentation-fault #strtok

#c #ошибка сегментации #strtok

Вопрос:

У меня есть две вспомогательные функции для разбиения строк в формате десятичных цен, т.Е. «23.00», «2.30»

Рассмотрим это:

 char price[4] = "2.20";

    unsigned getDollars(char *price)
    {
       return atoi(strtok(price, "."));
    }

    unsigned getCents(char *price)
    {
       strtok(price, ".");
       return atoi(strtok(NULL, "."));
    }
  

Теперь, когда я запускаю приведенное ниже, я получаю ошибку сегментации:

 printf("%un", getDollars(string));
printf("%un", getCents(string));
  

Однако, когда я запускаю их по отдельности, без того, чтобы один следовал за другим, они работают нормально. Чего я здесь не понимаю? Должен ли я выполнять какой-то сброс strtok??

Мое решение:

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

     #define MAX_PRICE_LEN 5 /* Assumes no prices goes over 99.99 */

unsigned getDollars(char *price)
{
   /* Copy the string to prevent strtok from changing the original */
   char copy[MAX_PRICE_LEN];
   char tok[MAX_PRICE_LEN];

   /* Create a copy of the original string */
   strcpy(copy, price);

   strcpy(tok, strtok(copy, "."));

   /* Return 0 if format was wrong */
   if(tok == NULL) return 0;
   else return atoi(tok);
}

unsigned getCents(char *price)
{
   char copy[MAX_PRICE_LEN];
   char tok[MAX_PRICE_LEN];
   strcpy(copy, price);

   /* Skip this first part of the price */
   strtok(copy, ".");
   strcpy(tok, strtok(NULL, "."));

   /* Return 0 if format was wrong */
   if(tok == NULL) return 0;
   else return atoi(tok);
}
  

Ответ №1:

Поскольку strtok() изменяется входная строка, вы сталкиваетесь с проблемами, когда не удается найти разделитель в getCents() функции после вашего вызова getDollars() .

Обратите внимание, что strtok() возвращает нулевой указатель, когда не удается найти разделитель. Ваш код не проверяет, что strtok() найдено то, что он искал, что всегда рискованно.


Ваше обновление вопроса демонстрирует, что вы узнали по крайней мере о некоторых опасностях (пороках?) strtok() . Однако я бы предположил, что лучшим решением было бы использовать just strchr() .

Во-первых, мы можем заметить, что atoi() преобразование все равно прекратится на ‘ . ‘, поэтому мы можем упростить getDollars() до:

 unsigned getDollars(const char *price)
{
    return(atoi(price));
}
  

Мы можем использовать strchr() — который не изменяет строку — чтобы найти '.' и затем обработать текст после него:

 unsigned getCents(const char *price)
{
    const char *dot = strchr(price, '.');
    return((dot == 0) ? 0 : atoi(dot 1));
}
  

Я думаю, все намного проще.


Еще одно уточнение: предположим, что строка равна 26.6; вам придется работать усерднее, чем в исправленном getCents() чуть выше варианте, чтобы вернуть значение 60 вместо 6. Кроме того, учитывая 26.650, он вернет 650, а не 65.

Комментарии:

1. Спасибо за совет, я принял его к сведению.

Ответ №2:

Это:

 char price[4] = "2.20";
  

не включается нулевой терминатор price . Я думаю, вы хотите этого:

 char price[5] = "2.20";
  

или лучше:

 char price[] = "2.20";
  

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

И вы почти всегда должны создавать копию строки перед использованием strtok для нее (чтобы избежать проблемы, на которую указал Джонатан Леффлер).

Комментарии:

1. Я думаю, что оба ответа / проблемы являются факторами. У меня общая проблема с (повторным) использованием strtok() в одной и той же строке. Ваш случай — особый случай с этим конкретным нестроковым файлом.