سؤال

I came across a programming question of which I knew only a part of the answer.

int f( char *p )
{
int n = 0 ;
while ( *p != 0 )
n = 10*n + *p++ - '0' ;
return n ;
}

This is what I think the program is doing. p is a pointer and the while loop is DE-refrencing the values of the pointer until it equals 0. However I don't understand the n assignment line, what is '0' doing? I am assuming the value of p is initially negative, that is the only way it will reach 0 after the increment.

هل كانت مفيدة؟

المحلول

You are confusing the number zero (none, nothing) with the character 0 (a circle, possibly with a slash through it). Notice that zero is in tick marks, so it's the character "0", not the number zero.

'0' - '0' = 0
'1' - '0' = 1
'2' - '0' = 2
...

So by subtracting the character zero from a digit, you get the number that corresponds to that digit.

So, say you have this sequence of digits: '4', '2', '1'. How do you get the number four-hundred and twenty-one from that? You turn the '4' into four. Then you multiply by ten. Now you have fourty. Convert the '2' into two and add it. Now you have fourty-two. Multiply by ten. Convert the '1' into one, and add, now you have four hundred and twenty one.

That's how you convert a sequence of digits into a number.

نصائح أخرى

The n local variable accumulates the value of the decimal number that is passed to this function in the string. This is an implementation of atoi, without the validity checks.

Here is the workings of the loop body:

n = 10*n + *p++ - ‘0';

Assign to n the result of multiplying the prior value of n by ten plus the current character code at the pointer p less the code of zero; increment p after dereferencing.

Since digit characters are encoded sequentially, the *p-'0' expression represents a decimal value of a digit.

Let's say that you are parsing the string "987". As you go through the loop, n starts at zero; then it gets assigned the following values:

n = 10*0 + 9;  // That's 9
n = 10*9 + 8;  // That's 98
n = 10*98 + 7; // That's 987

It's poorly written, to say the least.

0) Use formatting!:

int f(char* p)
{
    int n = 0;

    while (*p != 0)
        n = 10*n + *p++ - ‘0?;

    return n;
}

1) ? there is syntactically invalid. It should probably be a ' as noted by chris (and your existing is wrong too, but that's probably because you copied it from a website and not a source file), giving:

int f(char* p)
{
    int n = 0;

    while (*p != 0)
        n = 10 * n + *p++ - '0';

    return n;
}

2) The parameter type isn't as contrained as it should be. Because *p is never modified (per our goals), we should enforce that to make sure we don't make any mistakes:

int f(const char* p)
{
    int n = 0;

    while (*p != 0)
        n = 10 * n + *p++ - '0';

    return n;
}

3) The original programmer was obviously allergic to readable code. Let's split up our operations:

int f(const char* p)
{
    int n = 0;

    for (; *p != 0; ++p)
    {
        const int digit = *p - '0';
        n = 10 * n + digit;
    }

    return n;
}

4) Now that the operations are a bit more visible, we can see some independent functionality embedded in this function; this should be factored out (this is called reactoring) into a separate function.

Namely, we see the operation of converting a character to a digit:

int todigit(const char c)
{
    // this works because the literals '0', '1', '2', etc. are
    // all guaranteed to be in order. Ergo '0' - '0' will be 0,
    // '1' - '0' will be 1, '2' - '0' will be 2, and so on.

    return c - '0';
}

int f(const char* p)
{
    int n = 0;

    for (; *p != 0; ++p)
        n = 10 * n + todigit(*p);

    return n;
}

5) So now it's clear the function reads a string character by character and generates a number digit by digit. This functionality already exists under the name atoi, and this function is an unsafe implementation:

int todigit(const char c)
{
    // this works because the literals '0', '1', '2', etc. are
    // all guaranteed to be in order. Ergo '0' - '0' will be 0,
    // '1' - '0' will be 1, '2' - '0' will be 2, and so on.

    return c - '0';
}

int atoi_unsafe(const char* p)
{
    int n = 0;

    for (; *p != 0; ++p)
        n = 10 * n + todigit(*p);

    return n;
}

It's left as an exercise to the read to check for overflow, invalid characters (those that aren't digits), and so on. But this should make it much clearer what's going on, and is how such a function should have been written in the first place.

This is a string to number conversion function. Similar to atoi.

A string is a sequence of characters. So "123" in memory would be : '1','2','3',NULL

p Points to it.

Now, according to ASCII, digits are encoded from '0' to '9'. '0' being assigned the value 48 and '9' being assigned the value 57. As such, '1','2','3',NULL in memory is actually : 49, 50, 51, 0

If you wanted to convert from the character '0' to the integer 0, you would have to subtract 48 from the value in memory. Do you see where this is going?

Now, instead of subtracting the number 48, you subtract '0', which makes the code easier to read.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top