1

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!

melpomene
  • 84,125
  • 8
  • 85
  • 148
Rahul Raghunath
  • 175
  • 1
  • 4
  • 15
  • 2
    1) `int size = snprintf(NULL,0,"%lu", ULONG_MAX);` 2) `int size = (int)log10((double)ULONG_MAX) + 1;` then +1 for NUL. – BLUEPIXY Sep 06 '15 at 06:22
  • While this givem me a good estimate on how many digits it holds, i cannot use this information to create an array of that size in c! – Rahul Raghunath Sep 06 '15 at 06:32
  • Do you convert the number from `unsigned long` to `string` in decimal base or hexadecimal? Because in case of hexadecimal you may uses something generic like `#define MAX_NUMBER_OF_DIGITS_FOR_STR (sizeof(your_used_type_t)*2 + 1)` – Alex Lop. Sep 06 '15 at 06:55

3 Answers3

1

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
Community
  • 1
  • 1
ilent2
  • 5,171
  • 3
  • 21
  • 30
1
#if ULONG_MAX == 4294967295UL
#  define SIZE (10 + 1)
#elif ULONG_MAX <= 18446744073709551615ULL
#  define SIZE (20 + 1)
#endif
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
1

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
}
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256