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. #1
    Registered User
    Join Date
    Jul 2009
    Location
    Croatia
    Posts
    272

    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";
        char test[] = "adfa";
        
        if(htoi(hexalpha)==0) printf("\nHex string overflow or not a hex character.\n");
        else printf("%u\n", htoi(hexalpha));
      
        getchar();	
        return 0;
    }
    Last edited by Tool; 12-17-2009 at 12:27 AM.

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You shouldn't be checking for a to z, right? You want hex, and g through z aren't valid hex.


    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    Registered User
    Join Date
    Jul 2009
    Location
    Croatia
    Posts
    272
    Oh yeah, totally forgot that. Fixed it now. Other then that, is it fine...?

  4. #4
    cas
    cas is offline
    Registered User
    Join Date
    Sep 2007
    Posts
    957
    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. #5
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    None of the 'is' functions take unsigned arguments. Furthermore, since EOF is a valid argument, casting to an unsigned int is wrong.

    Quzah.
    Hope is the first step on the road to disappointment.

  6. #6
    cas
    cas is offline
    Registered User
    Join Date
    Sep 2007
    Posts
    957
    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. #7
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    My bad. Not awake enough. I'll come back later.


    Quzah.
    Hope is the first step on the road to disappointment.

  8. #8
    Registered User
    Join Date
    Jul 2009
    Location
    Croatia
    Posts
    272
    Ok, then ill just change the condition to >='0' && <='9' to avoid all this casting.

    Did i miss anything else?

  9. #9
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,160
    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.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  10. #10
    Registered User
    Join Date
    Jun 2010
    Posts
    1
    What if you try to convert "000"? How can you distinguish it with error return?

  11. #11
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,266
    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.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Popular pages Recent additions subscribe to a feed

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21