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? More readable? Any feedback gladly appreciated. 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 <stdio.h>
unsigned int num_hex_digits(unsigned int n) {
int ret = 0;
while(n) {
n >>= 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 & 0xF;
if(tmp < 10)
*p = tmp + '0';
else
*p = tmp + 'A' - 10;
invokeid >>= 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 < sz; ++i) {
make_hex_string_easy(testdata[i], &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 < sz; ++i) {
make_hex_string_learning(testdata[i], &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
UPDATE.
I can't thank you all enough for the brilliant feedback. Choosing an answer was tough and I chose William Morris' because of the zero and hex lookup idea which I like.
Here is my update which could probably go through more cycles. I left in some old, less optimal code to remind me of my iterations. Maybe constructive for others to view too.
/* 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 <stdio.h>
#include <math.h>
unsigned num_hex_digits_simple(unsigned n) {
int ret = 0;
while(n) {
n >>= 4;
++ret;
}
return ret;
}
/* should be faster than simple form above */
static unsigned num_hex_digits(unsigned n) {
return n ? floor(log(n) / log(16.0)) + 1 : 1;
}
unsigned make_hex_string_learning_ugly(unsigned n, char* s)
{
char* p = s;
int pad = 0;
int len = num_hex_digits(n);
/* if odd number, add 1 - zero pad number */
if(len % 2)
pad = 1;
/* caller may wish to determine how much to allocate for string and pass null */
if(s) {
/* move to end of number string */
p+= len + pad - 1;
if(n == 0) {
*p-- = '0';
}
else {
while(n) {
int tmp = n & 0xF;
*p = tmp + (tmp < 10 ? '0' : 'A' - 10);
n >>= 4;
p--;
}
}
if(pad)
*p = '0';
}
return len + pad;
}
unsigned make_hex_string_learning(unsigned n, char *s)
{
const char hex_lookup[] = "0123456789ABCDEF";
int len = num_hex_digits(n);
int i = len;
if(s) {
if (i & 1) {
*s++ = '0';
}
s[i] = '\0';
for (--i; i >= 0; n >>= 4, --i) {
s[i] = hex_lookup[n & 0xf];
}
}
return len + (len & 1);
}
int main() {
unsigned testdata[] = {0, ~0, 1, 255, 256, 0xFFFE, 0xFFFF, 0x10000, 0xABC };
int sz = sizeof(testdata) / sizeof(unsigned);
int i;
char test[50] = {0};
printf("Using sprintf method\n");
for(i = 0; i < sz; ++i) {
sprintf(test, "%s%X", num_hex_digits(testdata[i]) % 2 ? "0" : "", testdata[i]);
printf("hex string of %#10x = \t%10s\n", testdata[i], test);
memset(test, 0, sizeof(test));
}
printf("\nUsing homegrown method\n");
for(i = 0; i < sz; ++i) {
make_hex_string_learning(testdata[i], test);
printf("hex string of %#10x = \t%10s\n", testdata[i], test);
memset(test, 0, sizeof(test));
}
return 0;
}
invokeid
to something more clear and relevant to its role in the function. I'll write an actual answer in a few minutes. – busy_wait Aug 31 '13 at 18:03