What about this?
char *join_int_list(const unsigned int *list, size_t n_items)
{
enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
char *space = malloc(SIZEOF_INT_AS_STR * n_items);
if (space != 0)
{
size_t i;
char *pad = "";
char *dst = space;
char *end = space + SIZEOF_INT_AS_STR * n_items;
for (i = 0; i < n_items; i++)
{
snprintf(dst, end - dst, "%s%u", pad, list[i]);
pad = ",";
dst += strlen(dst);
}
space = realloc(space, dst - space + 1);
}
return(space);
}
It is the responsibility of the caller to release the returned pointer - and to check that it is not null before using it. The 'realloc()' releases extra space if the amount allocated was enough too large to make it worth while. This code is blithely assuming that the values are indeed 32-bit unsigned integers; if they can be bigger, then the enum needs appropriate adjustment.
Tested code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *join_int_list(const unsigned int *list, size_t n_items)
{
enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
char *space = malloc(SIZEOF_INT_AS_STR * n_items);
if (space != 0)
{
size_t i;
char *pad = "";
char *dst = space;
char *end = space + SIZEOF_INT_AS_STR * n_items;
for (i = 0; i < n_items; i++)
{
snprintf(dst, end - dst, "%s%u", pad, list[i]);
pad = ",";
dst += strlen(dst);
}
space = realloc(space, dst - space + 1);
}
return(space);
}
int main(void)
{
static unsigned int array[]= { 1, 2, 3, 49, 4294967295U, 0, 332233 };
char *str = join_int_list(array, sizeof(array)/sizeof(array[0]));
printf("join: %s\n", str);
free(str);
return(0);
}
Checked with valgrind - seems to be OK.
Discussion of converting INT_MAX
or UINT_MAX
to string:
You can use sizeof("," STRINGIZE(INT_MAX)) instead of hard coding it. The stringize macro is a common cpp tool that can be defined as #define STRINGIZE_(v) #v and #define STRINGIZE(v) STRINGIZE_(v). – R. Pate
@R Pate: good idea - yes, you could do that quite effectively. Actually, there are two interesting ideas in there: the use of string concatenation with sizeof() (parentheses required for clarity - but string concatenation happens early enough that the compiler isn't worried) and the use of a stringize operation on INT_MAX
. – Jonathan Leffler
Using a stringize operation on INT_MAX
is not a good idea - it just has to be a "constant expression", not necessarily a sequence of digits. It could be defined as ((1<<32)-1), or even something whacky like __int_max, as long as the compiler lets you use it anywhere you can use a constant expression. – caf
And @caf is right. Consider this code:
#include <limits.h>
#include <stdio.h>
#undef INT_MAX
#define INT_MAX (INT_MIN-1 - 100 + 100)
#define QUOTER(x) #x
#define STRINGIZER(x) QUOTER(x)
enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
enum { SIZEOF_INT_AS_STR_1 = sizeof(STRINGIZER(INT_MAX) ",")-1 };
int main(void)
{
printf("size = %d = %d\n", SIZEOF_INT_AS_STR, SIZEOF_INT_AS_STR_1);
printf("INT_MAX = %d\n", INT_MAX);
printf("UINT_MAX = %u\n", UINT_MAX);
return(0);
}
This doesn't even compile on MacOS X 10.5.8 with GCC 4.0.1 - because the identifier INT_MAX
is not defined. A preliminary version of the code that did not print INT_MAX
or UINT_MAX
worked; it showed that the value of SIZEOF_INT_AS_STR_1
was 31 - so @caf was correct. Adding the double-checking on the values of INT_MAX
and UINT_MAX
then failed to compile, which did surprise me. A look at the output from gcc -E
reveals why:
enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
enum { SIZEOF_INT_AS_STR_1 = sizeof("((-INT_MAX - 1)-1 - 100 + 100)" ",")-1 };
int main(void)
{
printf("size = %d = %d\n", SIZEOF_INT_AS_STR, SIZEOF_INT_AS_STR_1);
printf("INT_MAX = %d\n", ((-INT_MAX - 1)-1 - 100 + 100));
printf("UINT_MAX = %u\n", (((-INT_MAX - 1)-1 - 100 + 100) * 2U + 1U));
return(0);
}
As predicted, the string for SIZEOF_IN_AS_STR_1
is not a digit string at all. The pre-processor can evaluate the expression (as much as it needs to), but does not have to produce a digit string.
The expansion of INT_MAX
turns out to be in terms of INT_MIN
, and INT_MIN
is, in turn, defined in terms of INT_MAX
, so when the rewritten INT_MAX
macro is evaluated, the 'recursive expansion' is prevented by the C pre-processor rules of operation, and INT_MAX
appears in the pre-processed output - to the confusion of all.
So, there are multiple reasons why it the superficially attractive idea turns out to be a bad idea.