user14433201
user14433201

Reputation: 31

Conversion of unsigned int to hex string without use of sprintf

I am trying to implement the following (without sprintf or formatting):

void integer_to_string(unsigned int a, char *string)
{
    char arr[16] = "0123456789abcdef";
    char *p = (char *)&a;
    unsigned int i;

    for (i = 0; i < 4; i++) {
        unsigned char lo = p[i] & 0xf; 
        unsigned char hi = p[i] >> 4;

        string[i * 2] = arr[hi];
        string[i * 2 + 1] = arr[lo];
    }
    string[2 * i] = '\0';
}

This program is converting from an integer a and converting a into hexadecimal and saving the rest as a string. So, it is supposed to return an int like 10 to "0000000a" but instead is giving me a value of "0a000000" instead. What am I doing wrong? How do I move the 0a to the end instead of the beginning.

Upvotes: 3

Views: 1798

Answers (3)

Daniel Massicotte
Daniel Massicotte

Reputation: 1

Maybe not the best, but certainly a fast one...

This function returns a char* containing the HEX version of any unsigned integer of 32 bits or fewer that is passed to it...

example: u32tohex(284598256); will return "0x10F69FF0" as char*

the return char[] must be declared outside the function, because returning the pointer of a local variable will not work!

char dex[11] = "0x00000000";


const char* u32tohex(uint32_t ul)
{
  const char HexC[17]="0123456789ABCDEF";
  for (uint8_t xh=9; xh>1; xh--)
  {
    dex[xh]=HexC[ul&0xf];
    ul>>=4;
  }
  return &dex;
}

or the same without the prefix "0x"... example: u32tohex(121345243); will return "073B94DB" as char*

char dex[9] = "00000000";


const char* u32tohex(uint32_t ul)
{
  const char HexC[17]="0123456789ABCDEF";
  for (uint8_t xh=0; xh<8; xh++) 
  {
    dex[7-xh]=HexC[ul&0xf];
    ul>>=4;
  }
  return &dex;
}

Probably, a bit difficult to understand at first glance, but the less manipulation done to variables, will always yield a fast function... the Unsigned integer of 32 bit passed to the function is stored in ul, the loop starts by the end of the char[] result, and going backward... because I only bitwise AND ul with the 0xf, to get the least significant nibble, in one shot and set the final character form the HexC[]... and right shift the ul of 4 bits, getting rid of the last nibble, and replacing it by the next one... only 8 iterations, and the job is done... It may seem useless to have a fast function for this purpose, for a few conventions, but if you use it in a disassembler, to display the address at every line on the screen, as well as the data from those addresses, the speed of the refresh, while scrolling the screen will be very responsive, compared to many other solutions...

Upvotes: 0

chqrlie
chqrlie

Reputation: 144550

The output is incorrect because your system uses little endian ordering for the bytes that form the int values in memory. This is a very common case nowadays. Casting the address of a as a char * leads to non portable code.

You should instead write portable code that does not depend on the internal representation of integers this way:

// convert an unsigned int to its hexadecimal representation in a string
// assuming str points to an array of at least 9 bytes
// assuming unsigned int type has 32 bits
// returns its second argument for ease of use in chained expressions
char *integer_to_string(unsigned int a, char *str) {
    str[8] = '\0';
    for (int i = 0; i < 8; i++) {
        str[7 - i] = "0123456789abcdef"[a & 0xF];
        a = a >> 4;
    }
    return str;
}

Upvotes: 0

chux
chux

Reputation: 153338

Accessing the correct significant bytes via char *p = (char *)&a; is endian dependent and in OP's case, the wrong order.


Get the order right and avoid endian-ness issues by using math, not casts:

for (i = 0; i < 8; i++) {
    unsigned char ch = (a >> ((32-4) - i*4)) & 0xF; 
    string[i] = arr[ch];
}
string[i] = '\0';

Simplification exist.

Upvotes: 4

Related Questions