The interface to numtostring()
is a C-like interface. Apart from the choice of I/O operations in the main()
test program shown in the question, the code could perfectly well be C instead of C++.
The requirement not to change the interface to numtostring()
and for it to be recursive makes it messier and less efficient than it should be, doubly so if you're not allowed to use other library functions. In C++, you really shouldn't be mucking around with raw arrays of char
and the like either. However, rules are rules. The code can be implemented without the numreverse()
function — which fails when used to re-reverse numbers that end with one or more zeros (for example, 1000 reversed is 1, but 1 reversed is still 1, not 1000, or was that 100, 10, or even 1,000,000,000?).
The key trick with the recursion is to ensure that you have the base case identified and handled properly, and then that the recursive case builds on the base case. Here, the base case needs to be 'when the value is less than 10' and the recursive case (value at least 10) needs to get the string for the leading digits (corresponding to the value divided by 10) and then append a final digit.
What makes it tricky is that the function can't assume anything about str
except 'the pointer is to modifiable memory' and 'there is enough space to hold the number being formatted plus a terminal null' when it is first called. In particular, it can't assume that str
has a null byte at the beginning. So, the base case must ensure that the string is null terminated; the recursive case must append to the 'known to be null terminated' string. This means that the code runs slower than it might with less stringent constraints because the recursive case has to find the end of the string to append its digits.
#include <iostream>
#include <cstring> // memset() - used in test code
using namespace std;
void numtostring(unsigned int n, char str[]);
int main()
{
int v;
while (cin >> v)
{
char n[32];
memset(n, '\xA5', sizeof(n));
numtostring(v, n);
cout << v << " == [" << n << "]\n";
}
}
void numtostring(unsigned int n, char str[])
{
if (n > 9)
{
numtostring(n / 10, str);
while (*str != '\0')
str++;
*str++ = n % 10 + '0';
*str = '\0';
}
else
{
*str++ = n % 10 + '0';
*str = '\0';
}
}
Sample run:
$ n2s < data.n2s
0 == [0]
1 == [1]
2 == [2]
3 == [3]
4 == [4]
5 == [5]
6 == [6]
7 == [7]
8 == [8]
9 == [9]
10 == [10]
11 == [11]
99 == [99]
100 == [100]
999 == [999]
1000 == [1000]
1001 == [1001]
1002 == [1002]
$
(The program source was in n2s.cpp
and was compiled into program n2s
, and the numbers to be converted were stored in data.n2s
. The square brackets, and the memset()
operation, simply ensure that the data is properly set by the formatting function.)
Code review helps
There's an ugly repetition in numtostring()
above, and a little more thought shows that it is not only ugly but also unnecessary. A more streamlined version of the code is:
void numtostring(unsigned int n, char str[])
{
if (n > 9)
{
numtostring(n / 10, str);
while (*str != '\0')
str++;
}
*str++ = n % 10 + '0';
*str = '\0';
}
The while
loop is a nuisance but necessary.
Using a helper function
Since numreverse()
was allowed, it seems clear that the code could use functions that were written as part of the solution, so a more efficient solution uses a recursive helper function, but this solution also contravenes the rules because numreverse()
is no longer itself recursive.
#include <iostream>
#include <cstring> // memset() - used in test code
using namespace std;
void numtostring(unsigned int n, char str[]);
int main()
{
int v;
while (cin >> v)
{
char n[32];
memset(n, '\xA5', sizeof(n));
numtostring(v, n);
cout << v << " == [" << n << "]\n";
}
}
static char *n2s_helper(unsigned int n, char *str)
{
if (n > 9)
str = n2s_helper(n / 10, str);
*str = n % 10 + '0';
return str + 1;
}
void numtostring(unsigned int n, char str[])
{
str = n2s_helper(n, str);
*str = '\0';
}
For the same input, it produces the same output. It is more efficient because it only writes one null byte into the result string, and because it doesn't need to find the end of the string each time it needs to add a digit. It would take a lot of conversions to spot the difference in performance, of course.
In both solutions, the operational code (numtostring()
and n2s_helper()
) are valid C functions, just as they are valid C++ functions, and their behaviour is the same in either language.