Discussion:
[c-prog] String to unsigned long
'Richard Cavell' richardcavell@mail.com [c-prog]
7 years ago
Permalink
Hi everyone.
 
For my program (which is in C89, no external libraries allowed) I need to accept a command line argument and convert it to an unsigned numeric value. The lowest possible value is always 0. The highest possible value is a parameter to the function. I want to accept as many valid inputs as possible, and detect all possible errors. For the purpose of my project, I want a number with a leading $ sign to be interpreted as hexadecimal.
 
This is the best that I've been able to come up with, with my present level of skill.  Is there any error that I am not detecting, or feature that is easily added that I have not added?
 

static unsigned long int
string_to_unsigned_long(const char         *pstring,
                        boolean_type       *ok,
                        unsigned long int  max)
{
    unsigned long int l = 0;
    char *endptr = NULL;
    int base = 0;
 
    errno = 0;
 
    if (pstring != NULL)
        while (*pstring == ' ')
            ++pstring;
 
    if (pstring != NULL && pstring[0] == '$')
    {
        base = 16;
        ++pstring;
    }
    else
        base = 0;
 
    if (pstring != NULL)
        l = strtoul(pstring, &endptr, base);
 
    *ok = (     pstring    != NULL
            && *pstring    != '\0'
            &&  endptr     != NULL
            && *endptr     == '\0'
            &&  errno      == 0
            &&  pstring[0] != '-'
            &&  l <= max );
 
    return l;
}
 
andrew clarke mail@ozzmosis.com [c-prog]
7 years ago
Permalink
...
At a casual glance it looks okay.

One thing that I noticed is you're comparing pstring to NULL multiple times.
Once is enough.

You've also set base = 0 twice, although the compiler will optimise that out.

You should write tests for your code. Something like:

static void test(char *str, unsigned long max, unsigned long expected)
{
unsigned long x;
boolean_type ok;

x = string_to_unsigned_long(str, &ok, max);

if (!ok)
{
...
}

if (x != expected)
{
...
}
}

int main(void)
{
test(NULL, ULONG_MAX, 0);
test("1234", ULONG_MAX, 1234);
test("$beef", ULONG_MAX, 0xbeef);
test("abcd", ULONG_MAX, 0);
test("a word", INT_MAX, 0);
test("100", 42, 0); /* overflow */
test("", LONG_MAX, 0); /* empty string */
test("3.14159265", INT_MAX, 0); /* real number */
test("0xdead", INT_MAX, 0); /* hex number beginning with 0x, not $ */
test("-42", ULONG_MAX, 0); /* negative number */
test("42, 0, 0); /* maximum is zero */
...

return 0;
}

You can probably think of more tests!

Regards
Andrew

Loading...