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!

3
  • 2
    1) int size = snprintf(NULL,0,"%lu", ULONG_MAX); 2) int size = (int)log10((double)ULONG_MAX) + 1; then +1 for NUL. Commented Sep 6, 2015 at 6: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! Commented Sep 6, 2015 at 6: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) Commented Sep 6, 2015 at 6:55

3 Answers 3

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 } 
Sign up to request clarification or add additional context in comments.

Comments

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

3 Comments

Couldn't you use something like 1 << sizeof(type) instead of using magic numbers?
can't use sizeof in macro condition.
#elif line replace with #else
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 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.