Putting strtol() to the Test

Stop using the atoi() function! Yes, it’s a golden oldie, often my go-to function for converting strings to integers, but it’s weak. The strtol() function, on the other hand, not only converts strings to integers, it can validate input as well.

For truly secure code, it helps to know whether the user input the value 0 as a string or input nonsense interpreted as zero. How can you tell?

Well, you could write a routine that looks at the input string to determine if the first character is '0' — or the second character if the user is cheeky enough to type "+0" or "-0", which are both valid forms of input for the value zero. Such overhead isn’t necessary, however, when you use the strtol() function.

The second argument of strtol() is a pointer. When NULL is specified, the argument is ignored. Otherwise, you can set a pointer as the argument and compare that pointer’s address with the start of the input string to determine whether the text input was legitimately interpreted as zero.

Before showing you the code, be aware that the strtol() function ignores any leading whitespace characters before a value. The characters + and – are interpreted as sign operators. And prefix values are also interpreted, for example 0x for a hexadecimal value (providing that the third argument in the function is 16). When the string begins with text, or the string is empty, the value zero is returned — which is a legitimate result, but not exactly what the user supplied.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char x[] = "nonsense";
    char *p;
    int a;

    a = strtol(x,&p,10);
    printf("'%s' evaluates to %d\n",x,a);
    if( p == x )
    {
        printf("'%s' doesn't contain any values\n",x);
    }

    return(0);
}

At Line 6, string x is assigned to a string of all text, not a number. The strtol() function at Line 10 converts the string into the value zero, which is stored in variable a. However, because pointer p is specified as an argument in the strtol() function, the zero value can be verified.

At Line 12, the address stored in variable p is compared with the start of the string x. If both values are equal, the zero return value is invalid; the string doesn’t contain legitimate numeric input.

Here’s a test run:

'nonsense' evaluates to 0
'nonsense' doesn't contain any values

Change Line 6 to read char x[] = "";, and the output becomes:

'' evaluates to 0
'' doesn't contain any values

The empty string is properly interpreted as not being zero; the input is invalid.

Change the string to "0", and the output is:

'0' evaluates to 0

The strtol() function legitimately found zero as input.

Finally, to prove the strtol() function’s worth beyond nonsense values and zero, I changed Line 6 again to read:

char x[] = "      +789";

The strtol() function ignores the leading whitespace, the sign value is interpreted properly, and the value is converted into an integer. Here’s a sample run:

'      +789' evaluates to 789

When teaching C, I default to the atoi() function, which is simple to understand. For advanced courses, however, I use strtol(). And when writing the most secure code, especially for mission-critical input functions, I use the pointer argument and verify the input. You should too.

4 thoughts on “Putting strtol() to the Test

  1. strtol is a big improvement on atoi but I’m still not happy! In particular the second argument seems a very clumsy way to do things so I put together a wrapper function which returns a bool, and takes a long* to write the result of strtol to. It’s a bit rough and maybe I’ll try to combine the return value and second argument into a single pointer. Anyway, this is it.

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>

    bool strtol_wrapper_b10(const char* s, long* n);

    int main()
    {
        char s1[] = “one two three”;
        char s2[] = “123”;
        char* p;
        long n;

        if(strtol_wrapper_b10(s1, &n))
            printf(“\”%s\” is %ld as a number\n”, s1, n);
        else
            printf(“\”%s\” is not a valid number\n”, s1);

        if(strtol_wrapper_b10(s2, &n))
            printf(“\”%s\” is %ld as a number\n”, s2, n);
        else
            printf(“\”%s\” is not a valid number\n”, s2);

        return EXIT_SUCCESS;
    }

    bool strtol_wrapper_b10(const char* s, long* n)
    {
        char* p;

        *n = strtol(s, &p, 10);

        if(p == s)
            return false;
        else
            return true;
    }

  2. Clever. It reminds me of a library I’m using where they deprecated a function similar to strtol() for the same reasons you site. The replacement function used a technique exactly like your wrapper, returning a Boolean. Nice.

  3. Every time I invent something “new” I find somebody else got there first 🙁 Is it GLib that beat me to it?

    I tried to simplify the function by having just a return value or a pointer argument to write the result to. However, in both cases you would need either a long OR some sort of value indicating failure such as NULL or some sort of nan value, neither of which is possible in C. There is probably a messy workaround that would make the function more complex internally and more difficult to use but that would be the opposite of what I wanted to do, ie simplify it.

    In some languages this would be easy, but it’s a reminder that C is actually pretty low level.

Leave a Reply