The main issue with this is that variadic functions have non-existent type safety. It is better to avoid them - it's one of those old things in C that's simply broken.
I would suggest to instead write a function like:
char* strcat_alloc (size_t n, const char* list[n])
Okay, so that takes an array as parameter which may be cumbersome. To make it easier to use, we can combine it with a variadic wrapper macro and compound literals:
#define cat(...) strcat_alloc( sizeof (const char*[]){ __VA_ARGS__ } / sizeof(const char*), \ (const char*[]){ __VA_ARGS__ } )
Now isn't that as bad as a variadic function? Not at all, because in this case we force every parameter passed to be a pointer part of an initializer list. C has somewhat strict rules then, particularly if you use a conforming compiler. Then it's neither allowed to pass pointers of wrong type, not integers etc.
Complete example together with a naive implementation of the function follows. It can be optimized a lot, but the main point I wished to make is the function interface.
#include <stdio.h>#include <string.h>#include <stdlib.h>#define cat(...) strcat_alloc(sizeof (const char*[]){ __VA_ARGS__ } / sizeof(const char*), \ (const char*[]){ __VA_ARGS__ } )char* strcat_alloc (size_t n, const char* list[n]){ size_t size = 0; for(size_t i=0; i<n; i++) { size += strlen(list[i]); } size++; // null term char* result = malloc(size); if(result == NULL) { return result; } *result = '\0'; for(size_t i=0; i<n; i++) { strcat(result, list[i]); } return result;}int main (void){ char STR1[] = "hello"; char STR2[] = "world"; char* str = cat(STR1, " ", "meow", " ", STR2, " ", "meow"); puts(str); free(str);}