Integer-to-hex string generator I am using this as a learning exercise primarily and want to ensure my code is optimal.  The main thing is that it is reliable and I am made aware of any flaws.

Could it be made more efficient or more readable?  How about style?

I thought about returning a char*, but then the caller would have to think to deallocate the returned string.  I saw this as a problem, so I left to caller to allocate and deallocate.  Is that the right decision?  Any comments?

/* Generate hex string from integer.  Odd number of characters must be preceded 
   with zero character
   Eg. 9 becomes "09", 10 becomes "0A" and 16 becomes "10"
*/

#include &lt;stdio.h&gt;

unsigned int num_hex_digits(unsigned int n) {
    int ret = 0;
    while(n) {
        n &gt;&gt;= 4;
        ++ret;
    }
    return ret;
}

void make_hex_string_easy(unsigned int invokeid, char** xref) 
{ 
    int pad = 0;
    int len = num_hex_digits(invokeid);
    /* if odd number, add 1 - zero pad number */
    if(len % 2 != 0)
        pad = 1;

    sprintf(*xref, "%s%X", pad ? "0" : "", invokeid);
}

void make_hex_string_learning(unsigned int invokeid, char** xref) 
{                                                                                                                                                                                                                               
    char* p = *xref;
    int pad = 0;
    int len = num_hex_digits(invokeid);
    /* if odd number, add 1 - zero pad number */
    if(len % 2 != 0)
        pad = 1;

    /* move to end of number string */
    p+= len + pad - 1;

    while(invokeid) {
        int tmp = invokeid &amp; 0xF;
        if(tmp &lt; 10)
            *p = tmp + '0';
        else 
            *p = tmp + 'A' - 10;

        invokeid &gt;&gt;= 4;

        p--;    
    } 

    if(pad) {
        *p = '0';
    }
}


int main() {
    unsigned int testdata[] = {~0, 1, 255, 256, 0xFFFE, 0xFFFF, 0x10000, 0xABC };
    int sz = sizeof(testdata) / sizeof(int);
    int i;

    char* test = (char*) calloc (20, 1);
    printf("Using sprintf method\n");
    for(i = 0; i &lt; sz; ++i) {
        make_hex_string_easy(testdata[i], &amp;test);
        printf("hex string of %#10x = \t%10s\n", testdata[i], test);
        memset(test, 0, 20);
    }

    printf("\nUsing homegrown method\n");
    for(i = 0; i &lt; sz; ++i) {
        make_hex_string_learning(testdata[i], &amp;test);
        printf("hex string of %#10x = \t%10s\n", testdata[i], test);
        memset(test, 0, 20);
    }
    free(test);

    return 0;
}


Output on my PC:




Using sprintf method
hex string of 0xffffffff =        FFFFFFFF
hex string of        0x1 =              01
hex string of       0xff =              FF
hex string of      0x100 =            0100
hex string of     0xfffe =            FFFE
hex string of     0xffff =            FFFF
hex string of    0x10000 =          010000
hex string of      0xabc =            0ABC

Using homegrown method
hex string of 0xffffffff =        FFFFFFFF
hex string of        0x1 =              01
hex string of       0xff =              FF
hex string of      0x100 =            0100
hex string of     0xfffe =            FFFE
hex string of     0xffff =            FFFF
hex string of    0x10000 =          010000
hex string of      0xabc =            0ABC
Nothing jumps out:

Rather than this:

   if(tmp &lt; 10)
        *p = tmp + '0';
    else 
        *p = tmp + 'A' - 10;


I would have used:

   *p = tmp + (tmp &lt; 10) ? '0' : 'A' - 10;


Even though you pass around a pointer to a pointer (for re-alloc I assume). You don't actually do any reallocation. So I would simplify the code and just pass a pointer.

void make_hex_string_easy(unsigned int invokeid, char** xref) 
                                                 ^^^^^^   I would just use char*


Note: A lot of common interfaces when you pass a NULL as the destination return how many characters it would have used in the buffer. This leads to a lot of C code that looks like this:

size_t  s = make_hex_string_easy(number, NULL); // assuming you changed your code to return a value.
char*   b = malloc(s+1);
make_hex_string_easy(number, b);


But Since you don't use dynamic buffers I would remove the calloc.

printf("Using sprintf method\n");
for(i = 0; i &lt; sz; ++i) {
    char  test[50] = {0}; // inits to 0
    make_hex_string_easy(testdata[i], test);  // assuming you change to just a pointer
    printf("hex string of %#10x = \t%10s\n", testdata[i], test);
}