0

I'm sorry if my question is quite vague, because it is without context. So I was trying to solve a question: Write a recursive version of the function itoa(i) which converts an integer i into a string.

As I ran out of idea how to solve it, I started looking online to find solutions and I came across some of them which usually use this line: itoa(n/10, s);. For example, from this StackOverflow question: itoa recursively. I can't understand what it does to i.

So I went on and searched for more solutions and I found one that actually works, the program looks like this:

#include <stdio.h>

char *itoa(int n, char s[]);

int main()
{
    char number[100];

    printf("-90 to string = %s\n", itoa(-90, number));

    return 0;
}

char *itoa(int n, char s[])
{
    static int i = 0;

    if (n < 0) {
        s[i++] = '-';
        n = -n; /* does not work for largest negative number in two complement */
    }

    if (n / 10)
        itoa(n /10, s);

    s[i++] = n % 10 + '0';
    s[i] = '\0';

    return s;
}

Problem is, according to the solutions I found on other websites, people said that we should avoid using static int i. I did not read why we should do so because I don't know how static works so I don't know if this program is fine or needs improvements.

Xet46
  • 39
  • 3
  • The `static` keyword changes the storage duration for the object `int i` here, making the storage persist for the duration of the program. See [C11 Standard - 6.2.4 Storage durations of objects](http://port70.net/~nsz/c/c11/n1570.html#6.2.4). The kicker is you can only have one of those objects for the life of your program. (having only one `i` variable may be problematic) Perhaps choosing a more unique name for `i` would be better. – David C. Rankin Dec 08 '19 at 05:30
  • The `n/10` in `itoa(n /10, s);` just passes `n` divided by `10` as the first parameter to `itoa()` on each successive call (which is what separates each digit in the integer for you -- in reverse order -- which is the reason for the recursive call here -- to put them back together in the normal order) – David C. Rankin Dec 08 '19 at 05:36
  • 1
    To add to the static: what happens when you call it again? What is the value of `i` then? It’s a single use function then. You don’t need the static variable at all here. Have to think about the logic. – Sami Kuhmonen Dec 08 '19 at 05:37
  • So, from what I understand, after every loop, the `static` will make `i` keep the value that it was updated in the loop before, right? If I want to avoid `static`, is there any method to keep the updated value of i instead of resetting it back to `0`? – Xet46 Dec 08 '19 at 06:12
  • Think about what `i` is being used for and think about the scope within which that value is needed. Without `static` then `int i` will have *local storage duration* for the life of the function. In this case without `static`, you will need some way of making the value of `i` available to each of your recursive calls. You can either pass another parameter (or make the existing `char *itoa` a wrapper that calls a `doitoa()` function with 3-parameter so your original call doesn't change) -- or just keep it `static`. Other than the name `i`, it's not a bad use here. – David C. Rankin Dec 08 '19 at 06:20
  • That `static` variable makes this function *non reentrant*, meaning that if you call it from different threads contemporarily they will not work (at least). I would make it without recursion, anyway avoiding that static variable is not so difficult. Think about how `i` is used. – Roberto Caboni Dec 08 '19 at 08:12

1 Answers1

0

Your function pretty much almost right, that is for a recursive method. If you were going to parse the digit to string backward, it is right. Otherwise, I just did a couple fix.

For parsing digit to string, the digits that are being parsed are from the right digits to left digits as the remainder is what being used. Thus, when storing those into a string, we will need to go from high to low indexes. If we use remainders for parsing, we will not know the length of the number that is being parsed. Thus, in most parse cases there will be some extra spaces at the beginning of your string or char array.

For using the static i, you can pass a version of it around but it would make it harder to use as you would need to know to always have to pass i at 11. "i" is at 11 because the maximum digits for an int is 10(digits) + 1 sign and the 12th character which not counted by "i" is the null char. To make it easier to use the function, I configured the third parameter to be a void pointer. However, do not pass it an actual pointer, pass it a NULL. When it see NULL as the third parameter, it know that, that is the first call.

#include <stdio.h>
#include <string.h>
char *itoa(int n, char s[], void * );

int main()
{
    char number[100] = {0};

    printf("-90 to string = %s\n", itoa(154, number, NULL));
    printf("-15 to string = %s\n", itoa(-15, number, NULL));
    printf("-2147483648 to string = %s\n", itoa(-2147483648, number, NULL));

    return 0;
}

// The reason why I pass a void pointer is because
// instead of passing the value is because it easier to use function without needing to know that the string have to go from right left.
// When you first call it just use NULL as the third parameter. Anything else can ruin it.
char *itoa(int n, char s[], void * firstCall )
{
    static int i;
    if ( firstCall == NULL ) {
        i = 11;
        s[11] = 0;
    }

    int neg = 0;

    if (n < 0) {
        if ( n == -2147483648 ) {

          strcpy(s, "-2147483648");
          return s;
        }
        neg=1;
        n = -n; /* does not work for largest negative number in two complement */
    } 

    s[--i] = (n % 10) ^ 48;

    if ( n / 10 ) itoa(n / 10, s, s);

    if ( neg == 1 ) s[--i] = '-';

    return &s[i];
}
Kevin Ng
  • 2,146
  • 1
  • 13
  • 18