Проблема с ошибкой strtok и сегментации - PullRequest
6 голосов
/ 08 мая 2011

У меня есть две вспомогательные функции для разбивки строк в формате десятичных цен, т.е.«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("%u\n", getDollars(string));
printf("%u\n", 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);
}

Ответы [ 2 ]

5 голосов
/ 08 мая 2011

This:

char price[4] = "2.20";

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

char price[5] = "2.20";

или лучше:

char price[] = "2.20";

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

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

5 голосов
/ 08 мая 2011

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

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


Ваше обновление вопроса показывает, что вы узнали хотя бы о некоторых опасностях (пороках?) из strtok().Тем не менее, я бы предположил, что лучшее решение будет использовать только 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.

...