Here is a safe_printf()
function which never calls malloc()
!
I ended up writing a safe_printf()
function which never calls malloc()
, in case anyone ever needs it. I've tested it, and it works fine inside malloc()
. It uses the write()
system call (not std C) to write the chars to stdout.
Here it is:
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#ifdef DEBUG
/// Debug printf function.
/// See: https://stackoverflow.com/a/1941336/4561887
#define DEBUG_PRINTF(...) safe_printf("DEBUG: "__VA_ARGS__)
#define DEBUG_ASSERT(test_condition) assert(test_condition)
#else
#define DEBUG_PRINTF(...) \
do \
{ \
} while (0)
#define DEBUG_ASSERT(test_condition) \
do \
{ \
} while (0)
#endif
/// \brief A **safe** version of `printf()`, meaning it NEVER calls `malloc()`, unlike
/// `printf()`, which may.
/// \details This is achieved by using a fixed-size buffer to format strings into. You may see
/// a much simpler implementation from the Hacettepe University in Turkey, here, as a
/// basic example:
/// https://web.cs.hacettepe.edu.tr/~bbm341/codes/ecf/signals/safe_printf.c
#define SAFE_PRINTF_BUF_SIZE 2048
__attribute__((__format__(printf, 1, 2)))
int safe_printf(const char *format, ...)
{
static char buf[SAFE_PRINTF_BUF_SIZE];
// number of chars successfully written to `buf` and which we need to print
int chars_to_print = 0;
va_list args;
va_start(args, format);
int char_count_or_error = vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
DEBUG_ASSERT(char_count_or_error >= 0); // if fails: ENCODING ERROR
DEBUG_ASSERT(char_count_or_error < (int)sizeof(buf)); // if fails: BUFFER IS TOO SMALL
if (char_count_or_error < 0)
{
// ERROR: ENCODING ERROR
return char_count_or_error;
}
else if (char_count_or_error >= (int)sizeof(buf))
{
// ERROR: BUFFER IS TOO SMALL to write the full character sequence!
chars_to_print = sizeof(buf) - 1;
char_count_or_error = -char_count_or_error; // make negative to show it was an error
}
else // char_count_or_error >= 0 && char_count_or_error < sizeof(buf)
{
// No error
chars_to_print = char_count_or_error;
}
ssize_t num_bytes_written = write(STDOUT_FILENO, buf, chars_to_print);
DEBUG_ASSERT(num_bytes_written >= 0); // If fails: failure to write
// Return BUFFER IS TOO SMALL error
if (char_count_or_error < 0)
{
return char_count_or_error;
}
return num_bytes_written;
}
References:
- The comment by @kaylum:
write(STDOUT_FILENO, str, strlen(str))
- The answer by @Employed Russian
- A much simpler implementation from the Hacettepe University in Turkey, here, as a basic example: https://web.cs.hacettepe.edu.tr/~bbm341/codes/ecf/signals/safe_printf.c
- Linux
write(2)
reference page: https://man7.org/linux/man-pages/man2/write.2.html
- The
vsnprintf()
usage example here: http://www.cplusplus.com/reference/cstdio/vsnprintf/
stdarg.h
reference: https://www.cplusplus.com/reference/cstdarg/
See also:
- [my answer: this is where I identified the infinite recursion problem where
printf()
calls malloc()
, which calls printf()
, repeatedly; that is what motivated me to write the safe_printf()
implementation above!] "Segmentation fault (core dumped)" for: "No such file or directory" for libioP.h, printf-parse.h, vfprintf-internal.c, etc