Und was ist damit?
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);
}
Es liegt in der Verantwortung des Aufrufers, den zurückgegebenen Zeiger freizugeben - und zu prüfen, dass er nicht null ist, bevor er ihn verwendet. Die Funktion 'realloc()' gibt zusätzlichen Speicherplatz frei, wenn die zugewiesene Menge zu groß war, als dass es sich gelohnt hätte. Dieser Code geht munter davon aus, dass die Werte tatsächlich 32-Bit-Ganzzahlen ohne Vorzeichen sind; wenn sie größer sein können, muss die Aufzählung entsprechend angepasst werden.
Geprüfter 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);
}
Überprüft mit valgrind - scheint OK zu sein.
Diskussion über die Umwandlung INT_MAX
o UINT_MAX
zur Zeichenkette:
Sie können sizeof("," STRINGIZE(INT_MAX)) verwenden, anstatt es hart zu kodieren. Das Makro stringize ist ein gängiges cpp-Werkzeug, das als #define STRINGIZE_(v) #v und #define STRINGIZE(v) STRINGIZE_(v) definiert werden kann. - R. Kumpel
@R Pate: gute Idee - ja, das könnte man recht effektiv machen. Tatsächlich gibt es zwei interessante Ideen: die Verwendung von String-Verkettung mit sizeof() (Klammern sind für die Klarheit erforderlich - aber die String-Verkettung geschieht früh genug, so dass der Compiler nicht besorgt ist) und die Verwendung einer stringize-Operation auf INT_MAX
. - Jonathan Leffler
Die Verwendung einer Stringize-Operation auf INT_MAX
ist keine gute Idee - es muss nur ein "konstanter Ausdruck" sein, nicht unbedingt eine Folge von Ziffern. Er könnte als ((1<<32)-1) oder sogar als etwas Verrücktes wie __int_max definiert werden, solange der Compiler die Verwendung überall dort zulässt, wo man einen konstanten Ausdruck verwenden kann. - caf
Und @caf hat Recht. Betrachten Sie diesen 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);
}
Das lässt sich nicht einmal unter MacOS X 10.5.8 mit GCC 4.0.1 kompilieren - weil der Bezeichner INT_MAX
ist nicht definiert. Eine vorläufige Version des Codes, die nicht gedruckt wurde INT_MAX
o UINT_MAX
funktionierte; sie zeigte, dass der Wert von SIZEOF_INT_AS_STR_1
war 31 - also hatte @caf recht. Durch das Hinzufügen der doppelten Überprüfung der Werte von INT_MAX
y UINT_MAX
dann nicht kompilieren, was mich überrascht hat. Ein Blick auf die Ausgabe von gcc -E
zeigt, warum:
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);
}
Wie vorhergesagt, ist die Zeichenfolge für SIZEOF_IN_AS_STR_1
ist überhaupt keine Ziffernfolge. Der Präprozessor kann den Ausdruck auswerten (soweit er das muss), muss aber keine Zeichenkette erzeugen.
Die Ausweitung der INT_MAX
stellt sich heraus als in Bezug auf INT_MIN
y INT_MIN
ist seinerseits definiert in Form von INT_MAX
Wenn also die umgeschriebene INT_MAX
ausgewertet wird, wird die "rekursive Expansion" durch die Arbeitsregeln des C-Präprozessors verhindert, und INT_MAX
erscheint in der vorverarbeiteten Ausgabe - zur Verwirrung aller.
Es gibt also mehrere Gründe, warum sich die oberflächlich betrachtet attraktive Idee als schlecht herausstellt.
2 Stimmen
Können Sie die Art und Weise posten, wie Sie es in C gemacht haben?
2 Stimmen
Moment, join() ist eine Funktion der Klasse string!?
0 Stimmen
@BibedalShark: es ist C und nicht C++; keine Klasse
0 Stimmen
Ich spreche von seinem Python-Beispiel.
0 Stimmen
Ja, das ist sie.
', '.join([1, 2, 3])
wird emittieren1, 2, 3
.3 Stimmen
Bipedal: Auf diese Weise müssen wir join nur einmal implementieren (okay, einmal pro Stringklasse, also zweimal), anstatt hunderte Male für jeden möglichen Typ von iterable-Objekten. Lesen
','.join(sequence)
als "kommagetrennte Folge" und''.join(sequence)
als "leer-getrennte Folge"1 Stimmen
@BipedalShark - docs.python.org/library/stdtypes.html#str.join
0 Stimmen
Ich vermute, es war zu viel, um eine "iterables" Elternklasse haben?
1 Stimmen
Zweibeiner: Ja, das ist es tatsächlich. Python verwendet Duck-Typing und jedes Objekt kann ein Iterator sein, indem es nur next und
__iter__
Methoden. Python erfordert keine Vererbung, um eine Schnittstelle zu spezifizieren.1 Stimmen
Warum nicht einfach eine Funktion
join(sequence, delim)
? Was ist so besonders an Methoden, dass man sie anwenden muss, auch wenn die Lösung so unelegant ist?0 Stimmen
"".join()
ist eine Warze von Python. Betrachten Sie jedoch etwas Ähnliches:a = ","; b = a.join(seq)
-- es sind nur die Literale, die Sie verwirren.