The C standard library does not provide functions similar to printf that work on a variable number of arguments provided as an array. In order to do what you want, you would have to roll your own.
If you want to dynamically construct such a string, a good old for(...) realloc() loop is the way to go. Here's a simple implementation (it could probably be optimized more).
#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;
}
I particularly like this approach because it has a few advantages:
- No need to know the length of the resulting string upfront, final string is dynamically allocated.
- No need to modify any of the strings provided as argument.
- Uses
memcpy(), iterating over the whole final string twice (one time to check length, one to copy), unlike solutions involving strlen() + strcpy(), which could potentially iterate over the result three times (a common implementation for strcpy(dst, src) is memcpy(dst, src, strlen(src) + 1)).
- Simple error checking in case the number of specifiers is not enough (you can decide what to do inside those
if statements if you don't want to just return NULL.
with any format string & any number of specifiers & respective arguments?I do not understand. What does it mean "any"? No,%sexpects achar *, it can't take another type. Could you provide more examples? Could you provide what have you tried? Even if it failed it will help as a boilerplate code for others. Could you provide a function declaration of how would you see the interface? What should the input be?how could this be printed?isprintf(format_str, specs[0], specs[1])not enough?%sin the format string alway equal tocountcountinformation is redundant - the count of elements is already available via parsing the formatting string? Honestly, this looks like XY problem.