HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Space to allocate before sprintf

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sprintfbeforespaceallocate

Problem

I just finished chasing a Heisenbug that was entirely my fault. I'd like to avoid it happening again.

I have a function which formats a date to a certain preset format. Turns out I was not allocating enough space:

char* FormatDate(DT dateTime)
{
    char* formattedDate;
    formattedDate = (char*)malloc(
      6 //That was my bug, I had 5. For the record, I forgot a comma, not the terminal null...
      + numlen(dateTime.Year)
      + numlen(dateTime.Month)
      + numlen(dateTime.Day)
      + numlen(dateTime.Hour)
      + numlen(dateTime.Minute)
      + numlen(dateTime.Second)
      );
      sprintf(formattedDate,"%d,%d,%d,%d,%d,%d", dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
    return formattedDate;
}


numlen calculates the number of characters the number would take if printed in "%d" format.

And yes, the calling function has the duty to free the response.

What I'd like to know is how to avoid having the hardcoded 6, which arguably could change. sprintf does return the numbers of characters written, but that's a lot like the chicken and the egg... Is there another approach with pre-allocating that's safe? And not, say allocate a space of 250 just to make sure anything fits.

Solution

It appears that if have access to the standard library version of snprintf you can do this:

char* FormatDate(DT dateTime)
{
      size_t needed = snprintf(NULL, 0,"%d,%d,%d,%d,%d,%d", dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
      char* formattedDate = (char*)malloc(needed);
      snprintf(formattedDate, needed,"%d,%d,%d,%d,%d,%d", dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
      return formattedDate;
}


Original source obtained from StackOverflow

char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
    char  *buffer = malloc(needed);
    snprintf(buffer, needed, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}


Copied from https://stackoverflow.com/questions/1775403/using-snprintf-to-avoid-buffer-overruns but duplicated to make finding answers quicker.

Code Snippets

char* FormatDate(DT dateTime)
{
      size_t needed = snprintf(NULL, 0,"%d,%d,%d,%d,%d,%d", dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
      char* formattedDate = (char*)malloc(needed);
      snprintf(formattedDate, needed,"%d,%d,%d,%d,%d,%d", dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
      return formattedDate;
}
char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
    char  *buffer = malloc(needed);
    snprintf(buffer, needed, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}

Context

StackExchange Code Review Q#8341, answer score: 3

Revisions (0)

No revisions yet.