Concatenate variable number of strings
Calling strlen() twice on each string is wasteful in time.

Current code make 3 trips down each string, once for length, once for copying, once to determine new length.

    l += strlen(va_arg(ap, char*));
    ...
    memcpy(joined + total_length, s, sizeof_char * (length = strlen(s)));
    total_length += length;


The following runs down each string twice, once for length determination and a second for copying.  It "parses" the variable arguments only once, but then does need to save those results somewhere.

char* concat_n(size_t n, ...) {
  va_list ap;
  size_t size = 1;

  // Allocate memory or for small `n`, use VLA or fixed array.
  struct {
    size_t len;
    const char *ptr;
  } str[n];

  va_start(ap, n);
  for (size_t i = 0; i &lt; n; i++) {
    str[i].ptr = va_arg(ap, const char *);
    str[i].len = strlen(str[i].ptr);
    if (str[i].len &gt; SIZE_MAX - size) {  // Detect overflow in length
      va_end(ap);
      return NULL;
    }
    size += str[i].len;
  }
  va_end(ap);

  char *dest = malloc(size);
  if (dest) {
    char *p = dest;
    for (size_t i = 0; i &lt; n; i++) {
      memcpy(p, str[i].ptr, str[i].len);
      p += str[i].len;
    }
    *p = '\0';
  }
  return dest;
}




An alternative to saving the lengths and recomputing strlen() is to use some non-standard function like char *strpcpy() which copies and returns the pointer to the null character.  As if this is faster than other approaches, YMMV.  IMO, memcpy() is the best to speed code along and since the length was  compute prior, take advantage of that even if managing the saving of those prior results is a minor extra burden.



int is not the best type for array index nor size math.  size_t is the Goldilocks type, neither too wide, nor too narrow.



sizeof_char appears artificial.  Alternatives:

// char *joined = malloc(sizeof_char * (l + 1));
char *joined = malloc(l + 1);
// or
char *joined = malloc(sizeof *joined * (l + 1));




Defensive programming.  What happens if user passed a negative value for argc?   (Answer: a very long loop).  

// Problem code when `argc &lt; 0`
char *concat_strings(int argc, ...) {
  ...
  int n = argc;
  while(n--) { 


Suggest either size_t or &gt; 0

char *concat_strings(size_t argc, ...) {
  ...
  size_t n = argc;
  while(n--) { 


or 

char *concat_strings(int argc, ...) {
  ...
  int n = argc;
  while(n-- &gt; 0) { 




Check allocation

char *joined = malloc(l + 1);
if (joined == NULL) return NULL;




argc, though not a reserved name, has special meaning.  Suggest a different variable name like string_count