3

I am attempting the htoi(char*) function from The C Programming Language by K&R (Excercise 2-3, pg. 43).

The function is meant to convert a hexadecimal string to base 10.

I believe I have it working. This is my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

enum {hexbase = 16};
typedef enum{false, true} bool;

unsigned int htoi(char* s);
bool hasHexPrefix(char* s);

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

    if(argc <= 1) {
        printf("Error: Not enough arguments.\n");
        return EXIT_FAILURE;
    }else {
        for(int i = 1; i < argc; i++) {
            unsigned int numericVal = htoi(argv[i]);
            printf("%s => %u\n",argv[i],numericVal);
        }
    }
}

unsigned int htoi(char* s) {
    unsigned int output = 0;
    unsigned int len = (unsigned int)(strlen(s));

    unsigned short int firstIndex = hasHexPrefix(s) ? 2 : 0;

    /* start from the end of the str (least significant digit) and move to front */
    for(int i = len-1; i >= firstIndex; i--) {
        int currentChar = s[i];
        unsigned int correspondingNumericVal = 0;
        if(currentChar >= '0' && currentChar <= '9') {
            correspondingNumericVal = currentChar - '0';
        }else if(currentChar >= 'a' && currentChar <= 'f') {
            correspondingNumericVal = (currentChar - 'a') + 10;
        }else if(currentChar >= 'A' && currentChar <= 'F') {
            correspondingNumericVal = (currentChar - 'A') + 10;
        }else {
            printf("Error. Invalid hex digit: %c.\n",currentChar);
        }
        /* 16^(digitNumber) */
        correspondingNumericVal *= pow(hexbase,(len-1)-i);
        output += correspondingNumericVal;
    }

    return output;
}

bool hasHexPrefix(char* s) {
    if(s[0] == '0')
        if(s[1] == 'x' || s[1] == 'X')
            return true;

    return false;
}

My issue is with the following line from the htoi(char*) function:

unsigned short int firstIndex = hasHexPrefix(s) ? 2 : 0;

When I remove short to make firstIndex into an unsigned int rather than an unsigned short int, I get an infinite loop.

So when I start from the back of s in htoi(char* s), i >= firstIndex never evaluates to be false.

Why does this happen? Am I missing something trivial or have I done something terribly wrong to cause this undefined behavior?

aanrv
  • 2,159
  • 5
  • 25
  • 37

1 Answers1

4

When firstIndex is unsigned int, in i >= firstIndex then i is converted to unsigned int because of the usual arithmetic conversions. So if i is negative it becomes a large integer in the comparison expression. When firstIndex is unsigned short int in i >= firstIndex, firstIndex is promoted to int and two signed integers are compared.

You can change:

for(int i = len-1; i >= firstIndex; i--)

to

for(int i = len-1; i >= (int) firstIndex; i--)

to have the same behavior in both cases.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • I would rather do for(unsigned i = len-1; i >= firstIndex; i--) –  Mar 08 '15 at 21:27
  • 3
    @GRC which won't work as you will fall down in the same issue OP had. Take for example a `firstIndex` value of `0`, then `i >= firstIndex` will be always true. – ouah Mar 08 '15 at 21:38
  • Another possible structure is `for (unsigned i = len; i--; )` – M.M Mar 08 '15 at 21:46
  • `unsigned char` and `unsigned short` promoting to signed `int` has caused so much trouble over the years, in some ways it has turned out to be one of the worst design decisions. (Of course there is no way they could have foreseen all the consequences). – M.M Mar 08 '15 at 21:48
  • @ouah you are right :) I would use following i = len-1; i >= firstIndex || i > ~0; --i) –  Mar 08 '15 at 22:09