Reputation: 175
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
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
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
Reputation: 40145
#if ULONG_MAX == 4294967295UL
# define SIZE (10 + 1)
#elif ULONG_MAX <= 18446744073709551615ULL
# define SIZE (20 + 1)
#endif
Upvotes: 1