7

n.b. I know that this question has been asked on StackOverflow before in a variety of different ways and circumstances, but the search for the answer I seek doesn't quite help my specific case. So while this initially looks like a duplicate of a question such as How can I convert an integer to a hexadecimal string in C? the answers given, are accurate, but not useful to me.

My question is how to convert a decimal integer, into a hexadecimal string, manually. I know there are some beat tricks with stdlib.h and printf, but this is a college task, and I need to do it manually (professor's orders). We are however, permitted to seek help.

Using the good old "divide by 16 and converting the remainder to hex and reverse the values" method of obtaining the hex string, but there must be a big bug in my code as it is not giving me back, for example "BC" for the decimal value "188".

It is assumed that the algorithm will NEVER need to find hex values for decimals larger than 256 (or FF). While the passing of parameters may not be optimal or desirable, it's what we've been told to use (although I am allowed to modify the getHexValue function, since I wrote that one myself).

This is what I have so far:

/* Function to get the hex character for a decimal (value) between
 * 0 and 16.  Invalid values are returned as -1.
 */
char getHexValue(int value) 
{
   if (value < 0) return -1;
   if (value > 16) return -1;
   if (value <= 9) return (char)value;
   value -= 10;
   return (char)('A' + value);
}


/* Function asciiToHexadecimal() converts a given character (inputChar) to
 * its hexadecimal (base 16) equivalent, stored as a string of
 * hexadecimal digits in hexString. This function will be used in menu
 * option 1.
 */
void asciiToHexadecimal(char inputChar, char *hexString)
{
   int i = 0;
   int remainders[2];
   int result = (int)inputChar;
   while (result) {
      remainders[i++] = result % 16;
      result /= (int)16;
   }
   int j = 0;
   for (i = 2; i >= 0; --i) {
      char c = getHexValue(remainders[i]);
      *(hexString + (j++)) = c;
   }
}

The char *hexString is the pointer to the string of characters which I need to output to the screen (eventually). The char inputChar parameter that I need to convert to hex (which is why I never need to convert values over 256).

If there is a better way to do this, which still uses the void asciiToHexadecimal(char inputChar, char *hexString) function, I am all ears, other than that, my debugging seems to indicate the values are ok, but the output comes out like \377 instead of the expected hexadecimal alphanumeric representation.

Sorry if there are any terminology or other problems with the question itself (or with the code), I am still very new to the world of C.

Update: It just occurred to me that it might be relevant to post the way I am displaying the value in case its the printing, and not the conversion which is faulty. Here it is:

char* binaryString = (char*) malloc(8);
char* hexString = (char*) malloc(2);
asciiToBinary(*(asciiString + i), binaryString);
asciiToHexadecimal(*(asciiString + i), hexString);
printf("%6c%13s%9s\n", *(asciiString + i), binaryString, hexString);

(Everything in this code snip-pit works except for hexString)

Community
  • 1
  • 1
Ash
  • 24,276
  • 34
  • 107
  • 152
  • 9
    +1 for a well presented question and for making a lot of effort with the assignment before seeking help - I wish more `homework` questions were of a similar quality. – Paul R Mar 08 '11 at 11:02
  • 2
    .. and excellent thinking about the _output_, too! – sarnold Mar 08 '11 at 11:10
  • "the output comes out like \377" -- `\377` is `(char)-1`. Your `getHexValue` routine returns a negative int as an error code but is typed to return a `char`, and then you never check for the error code -- that's doubly bad. – Jim Balter Mar 08 '11 at 13:33
  • None of the casts in your code are necessary. Unnecessary casts are a bad idea because they can hide bugs. – Jim Balter Mar 08 '11 at 13:36

6 Answers6

2
char getHexValue(int value)
{
   if (value < 0) return -1;
   if (value > 16) return -1;
   if (value <= 9) return (char)value;
   value -= 10;
   return (char)('A' + value);
}

You might wish to print out the characters you get from calling this routine for every value you're interested in. :) (printf(3) format %c.)

When you call getHexValue() with a number between 0 and 9, you return a number between 0 and 9, in the ASCII control-character range. When you call getHexValue() with a number between 10 and 15, you return a number between 65 and 75, in the ASCII letter range.

The sermon? Unit testing can save you hours of time if you write the tests about the same time you write the code.

Some people love writing the tests first. While I've never had the discipline to stick to this approach for long, knowing that you have to write tests will force you to write code that is easier to test. And code that is easier to test is less coupled (or 'more decoupled'), which usually leads to fewer bugs!

Write tests early and often. :)

Update: After you included your output code, I had to comment on this too :)

char* binaryString = (char*) malloc(8);
char* hexString = (char*) malloc(2);
asciiToBinary(*(asciiString + i), binaryString);
asciiToHexadecimal(*(asciiString + i), hexString);
printf("%6c%13s%9s\n", *(asciiString + i), binaryString, hexString);

hexString has been allocated one byte too small to be a C-string -- you forgot to leave room for the ASCII NUL '\0' character. If you were printing hexString by the %c format specifier, or building a larger string by using memcpy(3), it might be fine, but your printf() call is treating hexString as a string.

In general, when you see a

char *foo = malloc(N);

call, be afraid -- the C idiom is

char *foo = malloc(N+1);

That +1 is your signal to others (and yourself, in two months) that you've left space for the NUL. If you hide that +1 in another calculation, you're missing an opportunity to memorize a pattern that can catch these bugs every time you read code. (Honestly, I found one of these through this exact pattern on SO just two days ago. :)

sarnold
  • 102,305
  • 22
  • 181
  • 238
  • This is great, and thanks for taking the time to help me find the bug. I think I see what your getting at I will make the appropriate changes. As for unit tests, I'ma big fan of unit testing in C# and Ruby/Rails where most of my experience is from but I have no idea how to wrap a testing framework around this C code, the base of which was supplied to us from our professor, and told not to change. Lastly, on the idea of the `malloc(N+1);` idea; I love this. I hadn't considered it before, and you are absolutely correct that it makes it easier to read. Thankyou! :) – Ash Mar 08 '11 at 11:54
  • WOOT! I got it working, thanks for your pointers (haha excuse the pun!) - I also discovered another bug with the line `for (i = 2; i >= 0; --i) {` which SHOULD be `for (i = 1; i >= 0; --i) {` :) But again, I've tested it, and its working great! Thanks. – Ash Mar 08 '11 at 12:06
  • @Ash, ha! Sorry I missed that one. Still, congrats. :) – sarnold Mar 08 '11 at 12:08
  • 1
    Unit testing is helpful, but so is writing code in a natural and readable style in the first place, like `return c < 10? c + '0' : c - 10 + 'A'`. – Jim Balter Mar 08 '11 at 13:29
2

Is the target purely hexadecimal, or shall the function be parametizable. If it's constrained to hex, why not exploit the fact, that a single hex digit encodes exactly four bits?

This is how I'd do it:

#include <stdlib.h>

#include <limits.h> /* implementation's CHAR_BIT */

#define INT_HEXSTRING_LENGTH (sizeof(int)*CHAR_BIT/4)
/* We define this helper array in case we run on an architecture
   with some crude, discontinous charset -- THEY EXIST! */
static char const HEXDIGITS[0x10] = 
    {'0', '1', '2', '3', '4', '5', '6', '7',
     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
void int_to_hexstring(int value, char result[INT_HEXSTRING_LENGTH+1])
{
    int i;
    result[INT_HEXSTRING_LENGTH] = '\0';

    for(i=INT_HEXSTRING_LENGTH-1; value; i--, value >>= 4) {
        int d  = value & 0xf;
        result[i] = HEXDIGITS[d];
    }

    for(;i>=0;i--){ result[i] = '0'; }
}
int main(int argc, char *argv[])
{
    char buf[INT_HEXSTRING_LENGTH+1];

    if(argc < 2)
        return -1;

    int_to_hexstring(atoi(argv[1]), buf);
    puts(buf);
    putchar('\n');

    return 0;
}
datenwolf
  • 159,371
  • 13
  • 185
  • 298
1

I made a librairy to make Hexadecimal / Decimal conversion without the use of stdio.h. Very simple to use :

char* dechex (int dec);

This will use calloc() to to return a pointer to an hexadecimal string, this way the quantity of memory used is optimized, so don't forget to use free()

Here the link on github : https://github.com/kevmuret/libhex/

kevmuret
  • 51
  • 5
1
#include<stdio.h>

char* inttohex(int);
main()
{
    int i;
    char *c;
    printf("Enter the no.\n");
    scanf("%d",&i);
    c=inttohex(i);
    printf("c=%s",c);
}

char* inttohex(int i)
{
    int l1,l2,j=0,n;
    static char a[100],t;

    while(i!=0)
    {
    l1=i%16;
    if(l1>10)
    {
        a[j]=l1-10+'A';
    }

    else
        sprintf(a+j,"%d",l1);

    i=i/16;
    j++;
    }
    n=strlen(a);
    for(i=0;i<n/2;i++)
    {
        t=a[i];
        a[i]=a[n-i-1];
        a[n-i-1]=t;
    }

    //printf("string:%s",a);
    return a;
    //
}
Anil
  • 31
  • 2
0

In complement of the other good answers....

If the numbers represented by these hexadecimal or decimal character strings are huge (e.g. hundreds of digits), they won't fit in a long long (or whatever largest integral type your C implementation is providing). Then you'll need bignums. I would suggest not coding your own implementation (it is tricky to make an efficient one), but use an existing one like GMPlib

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

You're very close - make the following two small changes and it will be working well enough for you to finish it off:

(1) change:

if (value <= 9) return (char)value;

to:

if (value <= 9) return '0' + value;

(you need to convert the 0..9 value to a char, not just cast it).

(2) change:

void asciiToHexadecimal(char inputChar, char *hexString)

to:

void asciiToHexadecimal(unsigned char inputChar, char *hexString)

(inputChar was being treated as signed, which gave undesirable results with %).

A couple of tips:

  • have getHexValue return '?' rather than -1 for invalid input (make debugging easier)

  • write a test harness for debugging, e.g.

    int main(void)
    {
        char hexString[256];
    
        asciiToHexadecimal(166, hexString);
    
        printf("hexString = %s = %#x %#x %#x ...\n", hexString, hexString[0], hexString[1], hexString[2]);
    
        return 0;
    }
    
Paul R
  • 208,748
  • 37
  • 389
  • 560
  • Thank you for your answer, it is appreciated, but I cannot change the signature of asciiToHexadecimal. :) – Ash Mar 08 '11 at 11:49