Rahul Raghunath
Rahul Raghunath

Reputation: 175

Determining character array size effeciently to use snprintf()

We have a small assignment from college that requires us to perform some job X in C. Part of that problem is to convert an unsigned long number, that is generated in the course of the program and hence without any way to predetrmine it, to a string. Naturally, i made use of snprintf. I initialized an array (str[50]) that was generously sized to avoid any sort of buffer errors.

On submission, however, my professor said that my method of avoiding buffer errors was ineffecient.

My question now is, when i create an char array to hold the unsigned long value, what size do i make it as? Is there some C macro to help determind the max number of characters that an unsigned long can hold?

something maybe like,

char str[MAX_NUMBER_OF_DIGITS_OF_UNSIGNED_LONG_ON_MACHINE];

I've skimmed throught limits.h and a few blogs and this forum but with no accord. Any help would be appreciated!

Upvotes: 1

Views: 398

Answers (3)

chux
chux

Reputation: 153498

Go with @BLUEPIXY for brevity.

A deeper answer.

C allows various "locales" such that, in theory, snprintf(..., "%lu",...) could print a longer string than expected. Instead of "1234567", the output could be "1,234,567".

Recommend:
1. Determine the size in bits, n, of the maximum integer.
2. n * log2(10) rounded-up + 1 to get then char count.
3. Set-up a buffer that is 2x max need.
4. Check snprintf result.
5. Niche concern: Using the double call with snprintf() needs to insure the "locale" and number do not change between calls - not use here as snprintf() is a functionally expensive call.

char *ulong_to_buf(char *buf, size_t size, unsigned long x) {
    int n = snprintf(buf, size, "%lu", x);
    if (n < 0 || n >= size) return NULL;
    return buf;
}

// Usage example
void foo(unsigned long x)
  // 1/3 --> ~log2(10)
  #define ULONG_PRT_LEN (sizeof(unsigned long)*CHAR_BIT/3 + 2)
  char buf[ULONG_PRT_LEN*2 + 1];  // 2x for unexpected locales
  if (ulong_to_buf(, sizeof buf, x)) {
    puts(buf);
  }

If code is really concerned, simple write your own

#include <stdlib.h>
#include <limits.h>
#include <string.h>
#define PRT_ULONG_SIZE (sizeof(unsigned long) * CHAR_BIT * 10 / 33 + 3)

char *ulong_strnull(int x, char *dest, size_t dest_size) {
  char buf[PRT_ULONG_SIZE];
  char *p = &buf[sizeof buf - 1];
  // Form string
  *p = '\0';
  do {
    *--p = x % 10 + '0';
    x /= 10;
  } while (x);
  size_t src_size = &buf[sizeof buf] - p;
  if (src_size > dest_size) {
    // Not enough room
    return NULL;
  }
  return memcpy(dest, p, src_size); // Copy string
}

Upvotes: 1

ilent2
ilent2

Reputation: 5241

From the documetation for snprintf:

Concerning the return value of snprintf(), SUSv2 and C99 contradict each other: when snprintf() is called with size=0 then SUSv2 stipulates an unspecified return value less than 1, while C99 allows str to be NULL in this case, and gives the return value (as always) as the number of characters that would have been written in case the output string has been large enough.

If you are using C99 you can determine the size using snprintf (as BLUEPIXY commented):

int size = snprintf(NULL, 0, "%lu", ULONG_MAX);

However if you can't use C99 then you can determine the string size by determining how many digits you require and adding an additional character for the terminating \0 character:

int size = (int) log10((double) ULONG_MAX) + 1;

In order to allocate your array with size bytes you can simply use

char str[size];

However this only works if your compiler/version supports VLAs, if you compiler doesn't support this you can dynamically allocate the array with

char *str = malloc(size);    //< Allocate the memory dynamically
// TODO: Use the str as you would the character array
free(str);                   //< Free the array when you are finished

Upvotes: 1

BLUEPIXY
BLUEPIXY

Reputation: 40145

#if ULONG_MAX == 4294967295UL
#  define SIZE (10 + 1)
#elif ULONG_MAX <= 18446744073709551615ULL
#  define SIZE (20 + 1)
#endif

Upvotes: 1

Related Questions