tags:

views:

191

answers:

4

I'm writing a recursive function that takes a char array, which represents a number, and a pointer to a digit in that array. The point of the function is to increment the number just like the ++ operator. However, when I try it out on the number '819'. It doesn't increment it to '820' but instead changes it to '810' (it increments the last digit but doesn't do the recursion that I want). Can someone help me out with this? Thanks.

#include <stdio.h>

char* inc(char *num, char* p)
{   
    if( *p>='0' && *p<='8' )
    {
     *p++;
    }
    else if ( *p=='9' )
    {
     *p = '0';
     inc(num, --p);
    }

    return num;
}

main()
{
    char x[] = "819";

    printf("%s\n", inc(x, x+strlen(x)-1) ); //pass the number and a pointer to the last digit
}
+11  A: 

Change *p++ to (*p)++ ; You want to increment the number contained in p.

   char* inc(char *num, char* p)
    {   
        if( *p>='0' && *p<='8' )
        {
            (*p)++;       //==> change
        }
        else if ( *p=='9' )
        {
            *p = '0';
            inc(num, --p);
        }

        return num;
    }

EDIT:

++ operator has higher precedence over * . Hence,

*p++ ==> *p then p++; // p's value before the increment.

Refer the precedence table here.

aJ
I thought that *p++ would return *p's value before p is actually incremented?
TURBOxSPOOL
Your edit is wrong. I mean your precedence statement is right but you don't take into account that the post-increment is delayed until the end of the statement.
paxdiablo
I have split the code bit too much. I have edited the same. @Pax, Thanks
aJ
+4  A: 

It's because *p++ retrieves the character then increments the pointer. You want (*p)++ which increments the character at that pointer:

#include <stdio.h>

char *inc (char *num, char* p) {
    if (*p >= '0' && *p <= '8') {
        (*p)++;
    } else if (*p == '9') {
        *p = '0';
        inc(num, --p);
    }
    return num;
}

You should be very careful on running this on strings above 9xxx since you'll have to ensure you've left room for the next digit up (pre-fill it with 0 if necessary). Otherwise add protection such as the wrap feature below:

#include <stdio.h>

char *inc (char *num, char* p) {
    if (p < num)
        return num;
    if ((*p < '0') || (*p > '9'))
        return num;
    if (*p < '9') {
        (*p)++;
        return num;
    }
    *p = '0';
    return inc(num, --p);
}

int main (int argc, char *argv[]) {
    char x[] = "819";
    char y[] = "8999";
    char z[] = "9999";
    char a[] = "aaa72";
    char b[] = "aaa279";
    char c[] = "aaa9999";
    printf("%s\n", inc(x, x+strlen(x)-1) );
    printf("%s\n", inc(y, y+strlen(y)-1) );
    printf("%s\n", inc(z, z+strlen(z)-1) );
    printf("%s\n", inc(a, a+strlen(a)-1) );
    printf("%s\n", inc(b, b+strlen(b)-1) );
    printf("%s\n", inc(c, c+strlen(c)-1) );
    return 0;
}

This code results in:

820
9000
0000
aaa73
aaa280
aaa0000

as expected.

paxdiablo
+1  A: 

I always follow some guidelines that help achieving correctness when coding in C++:

  • Do not modify and get the value of anything in the same instruction. i.e. inc(num, --p); is banned because the second argument of inc is not const.
  • Never dereference a pointer and do something with it in the same line. i.e. All forms of (*p)++; are banned.
  • Always guarantee const-correctness in function parameters.
  • Command/query separation: Functions should generally either be const or void.
  • Do not use recursion if you can avoid it i.e. always look for a non-recursive alternative first. (This question is an example in which you can avoid it).
  • Design by Contract. Add preconditions to the beginning, and postconditions to the end of your functions.

Applying them to your function may help to remove bugs in it.

Daniel Daranas
That's good advice for safety, but unnecessary for those of us who have been using C so long that the precedence rules are accessible from the neurons in the spinal cord without ever having to trouble the brain :-) I've only just recently forgotten the Wordstar 3.3 exit command from my MSDOS 2.11 days and I expect the neurons containing vi commands will be the last to decay from my rotting corpse. On a serious note, I disagree vehemently with not using recursion although not enough to downvote - it's often the most elegant solution.
paxdiablo
I wasn't born an extremist :) I learnt this advice the hard way, probably because throughout my career the code I've had to maintain wasn't written by those of you who have been using C so long that the precedence rules are accessible from the neurons in the spinal cord without ever having to trouble the brain. (Worse, some of them seemed to _think_ that they knew what they were doing - they didn't, actually.)
Daniel Daranas
Recursion is an absolutely essential tool for solving many everyday problems in a natural and simple manner - banning it out of hand is ridiculous!
anon
@Neil: I edited my guideline to say "always look for alternatives". I don't remember the last time I used recursion. However, my guideline is, if you have to use it, then you'd better use a simple algorithm and it'd better work well - don't just use it and then come asking me why it doesn't work. If you use it, do it in a way as simple as possible. Again, this comes from experience. Again, maybe some great developers can use it without messing things up. As always, YMMV.
Daniel Daranas
A: 

An example of the side-effect of the original program as compiled on gcc, (This side-effect doesn't happen on @Pax program)

int
 main()
{
char x[] = "9";
char z[] = {57,57,57,57};

int t=0;
for(t=0;t<4;++t)
 printf("z == %d\n",z[t]);

inc(x, x+strlen(x)-1);

for(t=0;t<4;++t)
 printf("z == %d\n",z[t]);

}

output: z == 57 z == 57 z == 57 z == 57 z == 48 z == 48 z == 48 z == 48

Liran Orevi