3
\$\begingroup\$

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.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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.

\$\endgroup\$
5
  • \$\begingroup\$ Shouldn't you allocate needed+1? I'm using Visual Studio 2005, it doesn't think it has snprintf. It does have sprintf, though. I like the approach of sending it to null. I think I'll go with that (if sprintf supports it). \$\endgroup\$
    – MPelletier
    Commented Jan 26, 2012 at 20:09
  • \$\begingroup\$ If I remember that version of MSVC has _snprintf and it works differently than the standard library version. \$\endgroup\$ Commented Jan 26, 2012 at 20:13
  • \$\begingroup\$ Yup, found _snprintf. Works, but I don't know what's different. \$\endgroup\$
    – MPelletier
    Commented Jan 26, 2012 at 20:19
  • \$\begingroup\$ I cannot say for sure it is, but I remember reading it would sometimes not null terminate. If your tests pass then no worries. \$\endgroup\$ Commented Jan 26, 2012 at 20:21
  • 1
    \$\begingroup\$ That's not reassuring. \$\endgroup\$
    – MPelletier
    Commented Jan 26, 2012 at 20:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.