I want to create a buffer for sprintfing a integer (in this case an unsigned int). A simple and misguided approach would be:
char b
OP's solution minimally meets design goals.
Is there a better way to size a buffer for printing integers?
Even a short analysis indicates the the number of bits needed with a unsigned grows by a factor of log10(2) or about 0.30103.... for each value bit when printing decimal and by 1/3 for printing octal. OP's code uses a factor of one-third or 0.33333...
unsigned x;
char buf[(CHAR_BIT*sizeof(unsigned)+5)/3];
sprintf(buf, "%u", x);
Considerations:
If buffer tightness concerns are real, then a buffer for decimal printing deserves a separate consideration than printing in octal.
Correctness: Unless code uses a strange locale with sprintf(), the conversion of the widest unsigned, which is UINT_MAX works for all platforms.
Clarity: the ...5)/3 is unadorned and does not indicate the rational for 5 and 3.
Efficiency. The buffer size is modestly excessive. This would not be an issues for a single buffer, but for an array of buffers a tighter value is recommended.
Generality: macro is crafted to only one type.
Potential hazard: With code re-use, a code extrapolation could use the same 5 and 3 for int without due consideration. OP's 5/3 works for int too, so this is not an issue.
Corner case: Using 5/3 for signed types and octal is a problem as (CHAR_BIT*sizeof(unsigned)+5)/3 should be (CHAR_BIT*sizeof(unsigned) + 5)/3 + 1. Example: problem occurs when trying to convert an int -32768 to base 8 text: "-100000" via some function (not sprintf(... "%o" ...)). That buffer needed is 8 where as CHAR_BIT*sizeof(unsigned)+5)/3 could be 7.
Is there a better way to do this?
Candidate for base 10:
28/93 (0.301075...) is a very close, and greater, approximation of log10(2). Of course code could use a more obvious fraction like 30103/100000.
Generality: A good macro would also adapt to other types. Below is one for various unsigned types.
#define LOG10_2_N 28
#define LOG10_2_D 93
// 1 for the ceiling 1 for \0
#define UINT_BUFFER10_SIZE(type) (1 + (CHAR_BIT*sizeof(type)*LOG10_2_N)/LOG10_2_D + 1)
unsigned x;
char bufx[UINT_BUFFER10_SIZE(x)];
sprintf(bufx, "%u", x);
size_t z;
char bufz[UINT_BUFFER10_SIZE(z)];
sprintf(bufz, "%zu", z);
The 28/93 fraction give the same answer integer results as log10(2) for integer sizes 1 to 92 bits and so is space efficient for arrays of buffers. It is never too small.
A macro for signed type could use
#define INT_BUFFER_SIZE(type) (1+1+ (CHAR_BIT*sizeof(type)-1)*LOG10_2_N)/LOG10_2_D + 1)
Avoid an off-by-one issue: I recommend using SIZE in the macro name to convey the buffer size needed and not the maximum string length.
Candidate for base 8:
Once a computed size for non-base 10 is needed, applications I've made usually need a a buffer to handle any base 2 and up. Consider printf() may allow %b someday too. So for a general purpose buffer to handle integer to text, any base, any sign-ness suggest:
#define INT_STRING_SIZE(x) (1 /* sign */ + CHAR_BIT*sizeof(x) + 1 /* \0 */)
int x = INT_MIN;
char buf[INT_STRING_SIZE(x)];
my_itoa(buf, sizeof buf, x, 2);
puts(buf); --> "-10000000000000000000000000000000" (34 char were needed)