>You mean that I should check it for NULL?
You can if you want. Or you can just assume that it's not null, just as you have to assume that it's a pointer to memory that you can modify. I think most itoa implementations allocate a buffer and return it if the argument is null.
>I still don't know how to fix what I previously said. Any ideas?
You're seriously over-engineering this function. Let's walk through some design stages using some itoa reference material. First is the signature, and an incremental design means starting with a stub. Because itoa may exist in your implementation, a unique name is best:
Code:
char *jsw_itoa ( int value, char *buffer, int radix )
{
}
Then a good idea is to add comments to the stub that outline your tasks. First is to deal with critical errors that we have control over. With the reference given, I can see one that relates to a range check:
Numeral radix in which value has to be represented,
between 2 and 36.
The reference states that we can assume buffer points to writable memory large enough to hold the string representation of the value, but here's an interesting one:
If radix is 10 and value is negative the string is preceded by the minus sign (-). With any other radix, value is always considered unsigned.
Negative values are only accepted if the radix is 10, otherwise the sign is ignored. This can be tricky if it doesn't fall out of your algorithm naturally, so that's a deciding factor in how to go about doing the conversion. Ideally, if you can keep it to a boolean flag then you're doing good. So let's look at the stub with task comments added for the ideal solution:
Code:
char *jsw_itoa ( int value, char *buffer, int radix )
{
// Check the radix for an out of range error
// Determine whether to handle the sign
// Convert value to a string; ignore the sign
// Handle the sign
// Reverse the string
}
The last comment is where experience comes in. I know that the easiest way to break apart a numeric value is to iteratively chop off the least significant digit. But it's awkward to build a string from end to beginning, which would be required if the sequence of digits comes in from least significant to most significant. So the least awkward solution is to build the string backwards and the reverse it before returning.
With an idea of what you want to do, you can fill in the blanks for the easy stuff:
Code:
#include <algorithm>
#include <stdexcept>
namespace {
const int min_radix = 2;
const int max_radix = 36;
}
char *jsw_itoa ( int value, char *buffer, int radix )
{
// Check the radix for an out of range error
if ( radix < min_radix || max_radix < radix )
throw std::range_error ( "Radix out of range" );
// Determine whether to handle the sign
bool sign = value < 0 && radix == 10;
// Convert value to a string; ignore the sign
// Handle the sign
// Reverse the string
std::reverse ( buffer, buffer + i );
return buffer;
}
For notational convenience, the min and max for the radix should be given meaningful names. Magic numbers should be avoided wherever possible. The actual range check is straightforward. Since we're hoping that a boolean flag will be enough, the check for handling the sign is as simple as matching the description of when to deal with negative numbers: if value is negative and radix is 10. The string reversal is trivial thanks to the standard library, and on success the filled buffer is returned. So far so good, right? Now for the hard part.
If you were doing an itoa that just worked with decimal then the first thing that would come to mind is a quickie calculation:
Code:
do
buffer[i++] = (char)( value % 10 ) + '0';
while ( ( value /= radix ) != 0 );
However, because you need to handle more than one radix, all the way through 36, it's probably better to use a table lookup. Consider it an extension of the itox-style conversion trick (which is another place where experience comes in):
Code:
#include <algorithm>
#include <cstdlib>
#include <stdexcept>
namespace {
const int min_radix = 2;
const int max_radix = 36;
const char *digit = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
char *jsw_itoa ( int value, char *buffer, int radix )
{
// Check the radix for an out of range error
if ( radix < min_radix || max_radix < radix )
throw std::range_error ( "Radix out of range" );
// Determine whether to handle the sign
bool sign = value < 0 && radix == 10;
// Convert value to a string; ignore the sign
int i = 0;
do {
int j = std::abs ( value % radix );
buffer[i++] = digit[j];
} while ( ( value /= radix ) != 0 );
// Handle the sign
buffer[i] = '\0';
// Reverse the string
std::reverse ( buffer, buffer + i );
return buffer;
}
This is a fantastic solution. Remember how I said that handling negative values should just fall out of the algorithm naturally? This is an excellent example of that because if value is negative, we need to take the absolute value to ensure a safe index for the digit string. Conveniently enough, that also correctly handles INT_MIN for two's complement, so you don't have to think hard about that fix like you would with a calculation (even though the solution is the same). Also, since we've locked down how characters are added to the string, we can safely terminate it with '\'0' at the end.
That was the hard part. With all of the current framework in place, handling the sign is trivial. Since the algorithm completely ignores the sign aside from this point, if the boolean flag is not true then the algorithm effectively treats the value as unsigned, as per the requirements. With that done you have the final version, and it's far from complicated:
Code:
#include <algorithm>
#include <cstdlib>
#include <stdexcept>
namespace {
const int min_radix = 2;
const int max_radix = 36;
const char *digit = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
char *jsw_itoa ( int value, char *buffer, int radix )
{
// Check the radix for an out of range error
if ( radix < min_radix || max_radix < radix )
throw std::range_error ( "Radix out of range" );
// Determine whether to handle the sign
bool sign = value < 0 && radix == 10;
// Convert value to a string; ignore the sign
int i = 0;
do {
int j = std::abs ( value % radix );
buffer[i++] = digit[j];
} while ( ( value /= radix ) != 0 );
// Handle the sign
if ( sign )
buffer[i++] = '-';
buffer[i] = '\0';
// Reverse the string
std::reverse ( buffer, buffer + i );
return buffer;
}
Designing a function like this is a combination of familiarity with similar solutions and a keen eye for detail. Reference material is important if the function already exists elsewhere, and a solid set of requirements if it's a completely new function. As long as you strictly adhere to the requirements, and the requirements are reasonable, you can usually pull an elegant solution out of them.