1

I've been reviewing stuff from multiple sources including my old favorite K&R second ed.

I've been looking at variadic functions, and almost all the tutorials I've seen use and int before the ellipses to determine the total number variable arguments passed.

But K&R example in the book does not use an int, they uses a char *

I searched stackoverflow and found stuff like:

Any variadic function must have a way for the caller to specify the number and types of arguments. The *printf functions, for example, do so via the (non-variadic) format string. link

and

No -- C doesn't define end as having any special meaning with varargs. When you write a function that takes a variable argument list, it's up to you to decide how to tell it how long of a list has been passed. link

K&R minprintf is meant to show how to use variadic function.

---- to show how to write a function that processes a variable-length argument list in a portable way. Since we are mainly interested in the argument processing, minprintf will process the format string and arguments but will call the real printf to do the format conversions

K&R only show the function, I added the main at the bottom, to see if it would work. I compiled it with gcc -Wall test.c. It compiled with no warnings, and worked as expected. The code is:

#include <stdarg.h>
#include <stdio.h>

/* minprintf: minimal printf with variable argument list */
void minprintf(char *fmt, ...)
{
   va_list ap; /* points to each unnamed arg in turn */
   char *p, *sval;
   int ival;
   double dval;

   va_start(ap, fmt); /* make ap point to 1st unnamed arg */
   for (p = fmt; *p; p++) {
      if (*p != '%') {
         putchar(*p);
         continue;
      }
      switch (*++p) {
      case 'd':
         ival = va_arg(ap, int);
         printf("%d", ival);
         break;
      case 'f':
         dval = va_arg(ap, double);
         printf("%f", dval);
         break;
      case 's':
         for (sval = va_arg(ap, char *); *sval; sval++)
            putchar(*sval);
         break;
      default:
         putchar(*p);
         break;
      }
   }
   va_end(ap); /* clean up when done */
}
// I added this main below to test K&R minprintf function. It compiled no errors and gave right answer.

int main()
{
   int i = 25;
   int j = 21;
   char str[] = "This is a test";
   minprintf("%d, %d, %s\n", i, j, str);
}   

How does minprintf know when to end? Is there a NULL in there? K&R do not explain it. From the stuff I read on-line, and some of the quotes above, a variadic functions does not know where to end, unless you tell it, such as with an int before the ellipses.

Manny_Mar
  • 398
  • 2
  • 13
  • Since you said the problem was you not knowing that strings end with `\0`, then this had nothing to do with variadic functions to begin with. You confused the amount of arguments passed to a variadic function with the amount of characters in a string. – HolyBlackCat Feb 03 '19 at 23:29
  • @ HolyBlackCat I know that if I do `a[] = " text"` add null, but the `(char *, ...)` confused me. K&R `getline` does not add NULL, so you don't always get a NULL. You can add it at the end or not. Also every other example online, and in a book i have use and `int` to determine size. – Manny_Mar Feb 03 '19 at 23:38

1 Answers1

3

Yes, there is a \0 in there. Look at the terminating condition of the loop:

for (p = fmt; *p; p++) { // look here
    // ...
}

When p does not point to a \0 value, *p != '\0', i.e. (bool)*p is true. In this case, *p is processed, be it a %, or something else.

When p points to a \0 value, (bool)*p is false. The loop ends, and the va_end is called.

Therefore, after it has scanned over the string, processed all % specifiers, it terminates by checking the end of the string.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • How did the NULL get put into the fcn? He has `(char *, ...)` Many times if i write my own string, I have to manually add `'\0'` to the end. – Manny_Mar Feb 03 '19 at 23:25
  • Or does `char *` add it for you? – Manny_Mar Feb 03 '19 at 23:26
  • @Manny_Mar When you use a string literal, like `"abc"`, it automatically gets `\0` at the end. Compare with `char` arrays, like `char arr[3] = {'a', 'b', 'c'};`, which don't get `\0` automatically. – HolyBlackCat Feb 03 '19 at 23:26
  • @Manny_Mar C-strings automatically end with `\0`. `"%d, %d, %s\n"` automatically adds a `\0` at the end. – L. F. Feb 03 '19 at 23:27
  • @Manny_Mar You can read the first paragraph on [this](https://en.cppreference.com/w/c/string/byte) page to get more information. – L. F. Feb 03 '19 at 23:28
  • @ L. F OK, did not realize it added the NULL. Then i see how it ends. – Manny_Mar Feb 03 '19 at 23:29
  • @Gerhardh That makes sense. I will tweak my wording. – L. F. Feb 04 '19 at 08:34