Hier ist eine lineare Lösung, die einen exponentiell wachsenden Puffer zuweist (weniger Aufrufe von realloc
(falls das eine Rolle spielt) für den Anrufer zu befreien. Testskript enthalten.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void ensure(bool pred, char *msg, char *file, int line) {
if (!pred) {
fprintf(stderr, "%s:%d: %s", file, line, msg);
exit(1);
}
}
char *arr_to_s(int len, int *arr, char *sep) {
size_t sep_len = strlen(sep);
int result_capacity = 16 + sep_len;
int result_len = 0;
char *result = malloc(result_capacity);
ensure(result, "malloc", __FILE__, __LINE__);
result[0] = '\0';
for (int i = 0; i < len; i++) {
char num[16+sep_len];
int previous_len = result_len;
result_len += sprintf(num, i < len - 1 ? "%d%s" : "%d", arr[i], sep);
if (result_len >= result_capacity) {
result_capacity <<= 1;
result = realloc(result, result_capacity);
ensure(result, "realloc", __FILE__, __LINE__);
}
strcat(result + previous_len, num);
}
return result;
}
void run_basic_tests(void) {
int tests[][4] = {
{0},
{0, 1},
{0, 1, 2},
{0, 42, 2147483647, -2147483648},
};
for (int i = 0; i < 4; i++) {
char *s = arr_to_s(i + 1, tests[i], ", ");
printf("[%s]\n", s);
free(s);
}
}
void run_intensive_tests(int n) {
srand(42);
for (int i = 0; i < n; i++) {
int len = rand() % 2000;
int test[len];
printf("[");
for (int j = 0; j < len; j++) {
test[j] = rand() % 2000000000 - 1000000000;
printf(j < len - 1 ? "%d," : "%d", test[j]);
}
puts("]");
char *s = arr_to_s(len, test, ",");
printf("[%s]\n", s);
free(s);
}
}
int main(void) {
//run_basic_tests();
run_intensive_tests(10000);
return 0;
}
Testläufer:
#!/usr/bin/env bash
gcc -std=c99 -pedantic -Wall \
-Wno-missing-braces -Wextra -Wno-missing-field-initializers -Wformat=2 \
-Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith \
-Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline \
-Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code \
-Wlogical-op -Wfloat-equal -Wstrict-aliasing=2 -Wredundant-decls \
-Wold-style-definition -Werror \
-ggdb3 \
-O0 \
-fno-omit-frame-pointer -ffloat-store -fno-common -fstrict-aliasing \
-lm \
-o arr_to_s.out \
arr_to_s.c
./arr_to_s.out > arr_to_s_test_out.txt
cat arr_to_s_test_out.txt | awk 'NR % 2 == 1' > arr_to_s_test_expected.txt
cat arr_to_s_test_out.txt | awk 'NR % 2 == 0' > arr_to_s_test_actual.txt
diff arr_to_s_test_expected.txt arr_to_s_test_actual.txt
Valgrind:
==573== HEAP SUMMARY:
==573== in use at exit: 0 bytes in 0 blocks
==573== total heap usage: 103,340 allocs, 103,340 frees, 308,215,716 bytes allocated
==573==
==573== All heap blocks were freed -- no leaks are possible
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.