Problem with strtok and segmentation fault
Asked Answered
U

2

6

I have two helper functions to break up strings in the format of decimal prices ie. "23.00", "2.30"

Consider this:

char price[4] = "2.20";

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

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

Now when I run the below I get a segmentation fault:

printf("%u\n", getDollars(string));
printf("%u\n", getCents(string));

However when I run them seperately without one following the other, they work fine. What am I missing here? Do I have to do some sort of resetting of strtok??

My solution:

With the knowledge about strtok I gained from the answer I chose below, I changed the implementation of the helper functions so that they copy the passed in string first, thus shielding the original string and preventing this problem:

    #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);
}
Unquestionable answered 8/5, 2011 at 3:24 Comment(0)
R
5

Because strtok() modifies the input string, you run into problems when it fails to find the delimiter in the getCents() function after you call getDollars().

Note that strtok() returns a null pointer when it fails to find the delimiter. Your code does not check that strtok() found what it was looking for - which is always risky.


Your update to the question demonstrates that you have learned about at least some of the perils (evils?) of strtok(). However, I would suggest that a better solution would use just strchr().

First, we can observe that atoi() will stop converting at the '.' anyway, so we can simplify getDollars() to:

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

We can use strchr() - which does not modify the string - to find the '.' and then process the text after it:

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

Quite a lot simpler, I think.


One more gotcha: suppose the string is 26.6; you are going to have to work harder than the revised getCents() just above does to get that to return 60 instead of 6. Also, given 26.650, it will return 650, not 65.

Royo answered 8/5, 2011 at 3:32 Comment(1)
Thanks for the tip, I've taken it on board.Unquestionable
A
5

This:

char price[4] = "2.20";

leaves out the nul terminator on price. I think you want this:

char price[5] = "2.20";

or better:

char price[] = "2.20";

So, you will run off the end of the buffer the second time you try to get a token out of price. You're just getting lucky that getCents() doesn't segfault every time you run it.

And you should almost always make a copy of a string before using strtok on it (to avoid the problem that Jonathan Leffler pointed out).

Aldoaldol answered 8/5, 2011 at 3:32 Comment(1)
I think that both answers/problems are factors. Mine is the generic problem with (re)using strtok() on the same string. Yours is a special case with this particular non-string.Royo

© 2022 - 2024 — McMap. All rights reserved.