Как правило, не стоит делать байты самостоятельно. Ваш код является свидетельством этого. Вот наиболее явные проблемы, которые я вижу:
join_strings()
записывает строку результата в буфер, выделенный его вызывающей стороной. Как звонящий узнает, сколько места потребуется? Это не знает. Он просто сделает предположение, и если это предположение слишком мало, все чертовы рвутся.
Вызовы strlen()
должны повторять всю строку аргумента, чтобы найти завершающий нольбайт. Затем ваш код повторяет ту же последовательность байтов снова, перезагружая данные в CPU. Один из этих проходов по входной строке можно было бы оптимизировать, но только вы, я не думаю, что есть достаточно умных компиляторов, которые могли бы выполнить эту оптимизацию.
Ваш *Реализация 1015 * излишне сложна. Сложность делает код трудным для размышления, а сложный для восприятия - источником ошибок.
Копирование входных строк в main()
не имеет смысла. Это только увеличивает сложность, делает код трудным для рассуждения и привлекает ошибки. Тебе лучше без него.
Хорошо, так как ты можешь добиться большего успеха? Ну, используя соответствующие стандартные библиотечные функции. В этом случае лучшее решение - использовать поток:
void write_strings(FILE* stream, int count, char** src, char* separator) {
for(int i = 0; i < count; i++) {
fprintf(stream, "%s%s", src[i], separator);
}
}
Три строки, один цикл, один простой вызов fprintf()
.
Эта функция может использоваться для записи некоторых вещейв stderr
, например: write_strings(stderr, 2, (char*){"Streams", "Rock"}, ". ")
выведет сообщение «Streams. Rock.» в поток ошибок.
Но как получить результат в виде строки? Просто. Используйте open_memstream()
:
char* join_strings(int count, char** src, char* separator) {
char* result = NULL;
size_t length = 0;
FILE* stream = open_memstream(&result, &length);
write_strings(stream, count, src, separator);
fclose(stream);
return result;
}
В этой функции даже нет цикла, в основном это просто вызов write_strings()
.
Возвращаемая строка автоматически выделяется open_memstream()
, , что означает нулевую опасность переполнения буфера . Это само по себе должно быть достаточной причиной для использования. Это также упрощает использование этой версии join_strings()
: вызывающей программе не нужно решать, сколько места выделить, она просто помещает строки и получает присоединенную строку. Именно то, что ему нужно.
Вы можете заметить, что я изменил сигнатуру функции, добавив в нее аргумент count. Это изменение не является обязательным, но мой опыт подсказывает, что это гораздо лучший выбор, чем завершенный список NULL
: вы можете легко забыть добавить терминатор NULL
, но вы не можете забыть предоставить обязательный параметр функции. А предварительный подсчет позволяет реализовать многие алгоритмы гораздо более простым способом. Поэтому я по умолчанию всегда ассоциирую массивы с соответствующим аргументом размера, даже если бы я мог избежать этого, если бы захотел.
В любом случае, как все это меняет способ использования функции? Не совсем так много. Наиболее важным изменением является то, что нам не нужно вычислять размер результирующего буфера в main()
. Поскольку мы можем также вырезать копию входных строк, обновленный main()
сводится к этому короткому простому коду:
int main(void) {
char * strings[] = {"A", "B"}; //no terminator necessary
size_t num_array_elements = sizeof strings / sizeof * strings;
char * separator = "XXX";
printf("Separator: %s | Len Array: %lu\n", separator, num_array_elements);
char * joined_string_buffer = join_strings(num_array_elements, strings, separator);
//Added by me: Print the result to stdout
printf("Resulting string: \"%s\" (%d characters)\n", joined_string_buffer, strlen(joined_string_buffer));
//Cleanup: Free the buffer allocated by `open_memstream()`
free(joined_string_buffer);
}
Обратите внимание, что во всем коде нет единого вычисления размера(все три функции), и при этом нет единственного явного вызова malloc()
. Существует один free()
, но это все о необходимом управлении памятью, тяжелая работа выполняется стандартными библиотечными функциями.