I want to create a buffer for sprintf
ing a integer (in this case an unsigned int
). A simple and misguided approach would be:
char b
The cases where something like this is needed is rare: perhaps some microcontroller code, transferring a value over some serial protocol. In such cases, using any of the printf()
family of functions may increase the size of the final binary.
(In typical C development environments, the C library is dynamically loaded, and there is absolutely no benefit in trying to avoid standard C library functions. It will not decrease the program size.)
So, if I needed such code, I might write a header file,
#if defined(INTTYPE) && defined (UINTTYPE) && defined (FUNCNAME)
#ifndef DECIMAL_DIGITS_IN
#define DECIMAL_DIGITS_IN(x) ((CHAR_BIT * sizeof (x) * 28) / 93 + 2)
#endif
char *FUNCNAME(const INTTYPE value)
{
static char buffer[DECIMAL_DIGITS_IN(value) + 1];
char *p = buffer + sizeof buffer;
UINTTYPE left = (value < 0) ? -value : value;
*(--p) = '\0';
do {
*(--p) = '0' + (left % 10);
left /= 10;
} while (left > 0);
if (value < 0)
*(--p) = '-';
return p;
}
#undef FUNCNAME
#undef INTTYPE
#undef UINTTYPE
#endif
and for each type I'd need, I'd use
#define FUNCNAME int2str
#define INTTYPE int
#define UINTTYPE unsigned int
#include "above.h"
In more ordinary code, the best approach is to use snprintf() to avoid buffer overruns, with the buffer size "guesstimated". For example,
unsigned int x;
char buffer[256];
int len;
len = snprintf(buffer, sizeof buffer, "Message with a number %u", x);
if (len < 0 || (size_t)len >= sizeof buffer - 1) {
/* Abort! The buffer was (almost certainly) too small! */
} else {
/* Success; we have the string in buffer[]. */
}
Whether buffer[]
is a few dozen or even few hundred bytes larger than necessary, is completely irrelevant in typical programs. Just make it large enough, and output an error message in the error case that tells which buffer (file and function) was not long enough, so it'll be easy to fix in the unlikely case it ever is too short.
As mentioned by dbush, asprintf()
GNU extension is a viable alternative. It returns a dynamically allocated string.
Outside of GNU systems -- and this is what I suggest OP considers, too -- one can implement their own asprintf()
, using
vsnprintf() (available in C99 and later C libraries, and also in POSIX.1 C libraries).
I prefer the variant that acts like POSIX.1 getline(), i.e. takes pointers to a pointer to a dynamically allocated buffer and the size of that buffer as extra parameters, and resizes that buffer if necessary:
#include
#include
#include
#include
#include
size_t dynamic_printf(char **dataptr, size_t *sizeptr, const char *format, ...)
{
va_arg args;
char *data;
size_t size;
int len;
if (!dataptr || !sizeptr || !format) {
errno = EINVAL;
return 0;
}
if (!*sizeptr) {
*dataptr = NULL;
*sizeptr = 0;
}
data = *dataptr;
size = *sizeptr;
va_start(args, format);
len = vsnprintf(data, size, format, args);
va_end(args);
if (len < 0) {
errno = EINVAL;
return 0;
} else
if ((size_t)len < size) {
errno = 0;
return (size_t)len;
}
/* Need to reallocate the buffer. */
size = (size_t)len + 1;
data = realloc(data, size);
if (!data) {
errno = ENOMEM;
return 0;
}
*dataptr = data;
*sizeptr = size;
va_start(args, format);
len = vsnprintf(data, size, format, args);
va_end(args);
if (len != (int)(size - 1)) {
errno = EINVAL;
return 0;
}
errno = 0;
return (size_t)len;
}
The idea is that you can reuse the same dynamic buffer across several dynamic_printf()
calls:
char *data = NULL;
size_t size = 0;
size_t len;
/* Some kind of loop for example */
len = dynamic_printf(&data, &size, "This is something I need in a buffer");
if (errno) {
/* Abort! Reason is strerror(errno) */
} else {
/* data is non-NULL, and has len chars in it. */
}
/* Strings are no longer used, so free the buffer */
free(data);
data = NULL;
size = 0;
Note that it is perfectly safe to run free(data); data = NULL; size = 0;
between calls. free(NULL)
does nothing, and if the buffer pointer is NULL
and size zero, the function will just dynamically allocate a new buffer.
In the worst case (when the buffer is not long enough), the function does "print" the string twice. This is perfectly acceptable, in my opinion.