# htoi

This is a discussion on htoi within the C Programming forums, part of the General Programming Boards category; Write the function htoi(s) , which converts a string of hexadecimal digits (including an optional 0x or 0X) into its ...

1. ## htoi

Write the function htoi(s) , which converts a string of hexadecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F .

Is this a good solution? Are all cases covered in this code? I did take care of variable overflow aswell.

Is this how they intended for this function to look like?

Code:
```#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>

unsigned int htoi(char s[])
{
unsigned int val = 0;
int x = 0;

if(s[x] == '0' && (s[x+1]=='x' || s[x+1]=='X')) x+=2;

while(s[x]!='\0')
{
if(val > UINT_MAX) return 0;
else if(s[x] >= '0' && s[x] <='9')
{
val = val * 16 + s[x] - '0';
}
else if(s[x]>='A' && s[x] <='F')
{
val = val * 16 + s[x] - 'A' + 10;
}
else if(s[x]>='a' && s[x] <='f')
{
val = val * 16 + s[x] - 'a' + 10;
}
else return 0;

x++;
}
return val;
}

int main(int argc, char *argv[])
{

char hexalpha[] = "0xFFF";

if(htoi(hexalpha)==0) printf("\nHex string overflow or not a hex character.\n");
else printf("%u\n", htoi(hexalpha));

getchar();
return 0;
}```

2. You shouldn't be checking for a to z, right? You want hex, and g through z aren't valid hex.

Quzah.

3. Oh yeah, totally forgot that. Fixed it now. Other then that, is it fine...?

4. Whether it's a good solution depends on the requirements. If you're looking for a maximally portable solution, you can't assume that 'a' to 'z' are consecutive (you can with '0' to '9'). Odds are, of course, that whatever platform you build this on will have consecutive characters, but it's not guaranteed.

For correct usage of isdigit(), you need to cast a char argument to unsigned char, like so:
Code:
`isdigit((unsigned char)s[x])`
This is an unfortunate (in my opinion) wart in the language, and one that you at least have a chance of running into problems with. The reason for the cast is that a compiler is allowed to do something like:
Code:
`#define isdigit(c) __digit_table[(c) + 1];`
That is, a lookup table can be used for speed, and so the standard says that the argument must be EOF (which this implementation would have as -1) or in the range of unsigned char (that is, positive). If you pass a negative value (other than -1) to this macro, you'll be underrunning the table. From a quick glance, it appears that NetBSD's ctype macros indeed act in a manner similar to this, so it's not just theoretical.

5. None of the 'is' functions take unsigned arguments. Furthermore, since EOF is a valid argument, casting to an unsigned int is wrong.

Quzah.

6. Casting to unsigned int is wrong, but casting to unsigned char is right. When you're passing a char (which is the case I mentioned), you won't be passing EOF. But you might be passing a negative value, which is incorrect. You therefore need to cast char arguments to unsigned char, unless you know they're already in the range of unsigned char--which this function can't know.

If you store the return value of getchar() (or similar) in an int and then pass it on to an is* function, you needn't cast, because getchar() returns the value either as EOF or in the range of unsigned char. But I was not talking about passing an int, I was talking about passing a char.

7. My bad. Not awake enough. I'll come back later.

Quzah.

8. Ok, then ill just change the condition to >='0' && <='9' to avoid all this casting.

Did i miss anything else?

9. The use of isdigit() is locale-specific. On the one hand, you use the character literals 'A' and 'F', which are in source encoding, but the isdigit() function uses the current locale's encoding, which means you're mixing encodings.

On systems where the locale can be configured at runtime, this means the user can break your program by choosing a locale with an encoding that is incompatible with ASCII. If you mean some character between ASCII '0' and ASCII '9' then code it out explicitly.

Sorry, pet peeve.

10. What if you try to convert "000"? How can you distinguish it with error return?

11. val can never be greater than UINT_MAX. Afterall, that's the largest value val can hold.
You probably want UINT_MAX / 16, since you are about to add another digit and just want to check that there is room.

I would use pointer arthmetic rather than a pointer plus an index variable.

This was the second google result for htoi.