Стандартная библиотека C не предоставляет функций, подобных printf
, которые работают с переменным количеством аргументов, предоставленных в виде массива. Чтобы делать то, что вы хотите, вам придется свернуть свою собственную.
Если вы хотите динамически построить такую строку, старый добрый for(...) realloc()
l oop - это путь к go. Вот простая реализация (ее, вероятно, можно было бы еще оптимизировать).
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *my_sprintf(const char *fmt, size_t n, char *const *strings) {
const char *fmt_start, *fmt_end;
size_t i, len, prev_len, fmt_len, spec_len;
char *res, *tmp;
fmt_start = fmt;
len = 0;
res = NULL;
for (i = 0; i < n; i++) {
// Find position of next %s format specifier.
fmt_end = strstr(fmt_start, "%s");
if (fmt_end == NULL) {
// Error out if not found.
free(res);
return NULL;
}
// Do some math...
fmt_len = fmt_end - fmt_start; // Length of current format specifier segment.
spec_len = strlen(strings[i]); // Length of current string.
prev_len = len; // Previous total length.
len += fmt_len + spec_len; // New total length.
// Increase the size of the final string.
tmp = realloc(res, len + 1);
if (tmp == NULL) {
// Error out if realloc() fails.
free(res);
return NULL;
}
res = tmp;
// Copy specifier segment and i-th string at the end of the final string.
memcpy(res + prev_len, fmt_start, fmt_len);
memcpy(res + prev_len + fmt_len, strings[i], spec_len);
// Skip current specifier.
fmt_start = fmt_end + 2;
}
// Copy last specifier segment (if needed).
fmt_len = strlen(fmt_start);
prev_len = len;
len += fmt_len;
tmp = realloc(res, len + 1);
if (tmp == NULL) {
free(res);
return NULL;
}
res = tmp;
memcpy(res + prev_len, fmt_start, fmt_len);
res[len] = '\0';
return res;
}
int main(int argc, char **argv) {
char *res = my_sprintf(argv[1], argc - 2, argv + 2);
if (res != NULL) {
puts(res);
free(res);
} else {
puts("ERR");
}
return 0;
}
Мне особенно нравится этот подход, потому что он имеет несколько преимуществ:
- Нет необходимости знать длину получившаяся строка заранее, последняя строка выделяется динамически.
- Нет необходимости изменять какую-либо из строк, предоставленных в качестве аргумента.
- Использует
memcpy()
, повторяя всю конечную строку дважды (один раз для проверки длины, один для копирования), в отличие от решений, включающих strlen()
+ strcpy()
, которые потенциально могут перебирать результат три раз (обычная реализация для strcpy(dst, src)
- memcpy(dst, src, strlen(src) + 1)
). - Простая проверка ошибок в случае, если количество спецификаторов недостаточно (вы можете решить, что делать внутри этих
if
операторов, если вы не хотите просто возвращать NULL
.