I highly recommend that you avoid variadic functions and use pointer arrays and variadic macros instead (with a terminator object).
Your function would have looked like this when using this approach:
void printline(const char *str) { printf("%s\n", str); }
int printlines(char **lines) {
  if (!lines)
    return -1;
  while (*lines)
    printline(*(lines++));
  return 0;
}
#define printlines(...) printlines((char *[]){__VA_ARGS__, NULL})
Not only are variadic functions sometimes difficult to code, but the ABI for variadic functions is problematic to the point that different languages might treat it differently and C bindings between different languages might break your code.
Besides, when using this approach, things can become much more fun and interesting as well, allowing for easy type detection and multi-type arguments... this code from the facil.io CSTL library provides a good example for what I mean.
The function accepts an array of structs:
/** An information type for reporting the string's state. */
typedef struct fio_str_info_s {
  /** The string's length, if any. */
  size_t len;
  /** The string's buffer (pointer to first byte) or NULL on error. */
  char *buf;
  /** The buffer's capacity. Zero (0) indicates the buffer is read-only. */
  size_t capa;
} fio_str_info_s;
/** memory reallocation callback. */
typedef int (*fio_string_realloc_fn)(fio_str_info_s *dest, size_t len);
/** !!!Argument type used by fio_string_write2!!! */
typedef struct {
  size_t klass; /* type detection */
  union {.      /* supported types */
    struct {
      size_t len;
      const char *buf;
    } str;
    double f;
    int64_t i;
    uint64_t u;
  } info;
} fio_string_write_s;
int fio_string_write2(fio_str_info_s *restrict dest,
                      fio_string_realloc_fn reallocate, /* nullable */
                      const fio_string_write_s srcs[]);
Then a macro makes sure the array's last element is a terminator element:
/* Helper macro for fio_string_write2 */
#define fio_string_write2(dest, reallocate, ...)            \
  fio_string_write2((dest),                                 \
                    (reallocate),                           \
                    (fio_string_write_s[]){__VA_ARGS__, {0}})
Additional helper macros were provided to make the fio_string_write_s structs easier to construct. i.e.:
/** A macro to add a String with known length to `fio_string_write2`. */
#define FIO_STRING_WRITE_STR2(str_, len_)                    \
  ((fio_string_write_s){.klass = 1, .info.str = {.len = (len_), .buf = (str_)}})
/** A macro to add a signed number to `fio_string_write2`. */
#define FIO_STRING_WRITE_NUM(num)                            \
  ((fio_string_write_s){.klass = 2, .info.i = (int64_t)(num)})
And the function used the terminator element to detect the number of arguments received by the macro:
int fio_string_write2 (fio_str_info_s *restrict dest,
                               fio_string_realloc_fn reallocate, /* nullable */
                               const fio_string_write_s srcs[]) {
  int r = 0;
  const fio_string_write_s *pos = srcs;
  size_t len = 0;
  while (pos->klass) {
    switch (pos->klass) { /* ... */ }
    /* ... counts total length */
    ++pos;
  }
  /* ... allocates memory, if required and possible ... */
  pos = srcs;
  while (pos->klass) {
    switch (pos->klass) { /* ... */ }
    /* ... prints data to string ... */
    ++pos;
  }
    /* ... house-keeping + return error value ... */
}
Example use (from the source code comments):
 fio_str_info_s str = {0};
 fio_string_write2(&str, my_reallocate,
                     FIO_STRING_WRITE_STR1("The answer is: "),
                     FIO_STRING_WRITE_NUM(42),
                     FIO_STRING_WRITE_STR2("(0x", 3),
                     FIO_STRING_WRITE_HEX(42),
                     FIO_STRING_WRITE_STR2(")", 1));
This both simplifies the code and circumvents a lot of the issues with variadic functions. This also allows C bindings from other languages to work better and the struct array to be constructed in a way that is more idiomatic for the specific target.