17

The <stdarg.h> header file is used to make functions accept undefined number of arguments, right?

So, the printf() funtion of <stdio.h> must be using <stdarg.h> to accept avariable number of arguments(please correct me if I'm mistaken).

I found the following lines in the stdio.h file of gcc:

#if defined __USE_XOPEN || defined __USE_XOPEN2K8
# ifdef __GNUC__
#  ifndef _VA_LIST_DEFINED
typedef _G_va_list va_list;
#   define _VA_LIST_DEFINED
#  endif
# else
#  include <stdarg.h>//////////////////////stdarg.h IS INCLUDED!///////////
# endif
#endif

I can't understand most of what's in it, but it appears to be including <stdarg.h>

So, if printf() uses <stdarg.h> for accepting variable number of arguments and stdio.h has printf(), a C program using printf() need not include <stdarg.h> does it?

I tried a program which had printf() and a user-defined function accepting variable number of arguments.

The program I tried is:

#include<stdio.h>
//#include<stdarg.h>///If this is included, the program works fine.

void fn(int num, ...)
{
    va_list vlist;
    va_start(vlist, num);//initialising va_start (predefined)

    int i;

    for(i=0; i<num; ++i)
    {
        printf("%d\n", va_arg(vlist, int));
    }

    va_end(vlist);//clean up memory
}

int main()
{
    fn(3, 18, 11, 12);
    printf("\n");
    fn(6, 18, 11, 32, 46, 01, 12);

    return 0;
}

It works fine if <stdarg.h> is included but otherwise generates the following error:

40484293.c:13:38: error: expected expression before ‘int’
         printf("%d\n", va_arg(vlist, int));//////error: expected expression before 'int'/////////
                                      ^~~

How is this?

Or is it that printf() doesn't use <stdarg.h> for accepting variable number of arguments? If so, how is it done?

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
J...S
  • 4,713
  • 1
  • 15
  • 34
  • 3
    `va_arg` is a macro defined into `stdarg.h`. So it is mandatory to include `stdarg.h` or to provide the macro for your project.... Same thing for `va_list` `va_start` and `va_end` – LPs Nov 08 '16 at 10:22

8 Answers8

13

Consider:

stdio.h:

int my_printf(const char *s, ...); 

Do you need <stdarg.h>? No, you don't. ... is part of the grammar of the language - it's "built-in". However, as soon as you want to do anything meaningful and portable with such list of arguments, you need the names defined in there: va_list, va_start and so on.

stdio.c:

#include "stdio.h"
#include "stdarg.h"

int my_printf(const char *s, ...)
{
   va_list va;
   va_start(va, s);

   /* and so on */
}

But this will be necessary, essentially, in the implementation of your libc which is something you don't see unless you compile the library on your own. What you instead get is the libc shared library, which has already been compiled to machine code.

So, if printf() uses for accepting variable number of arguments and stdio.h has printf(), a C program using printf() need not include does it?

Even if it were so, you cannot rely on that, otherwise your code is ill-formed: you must include all the headers anyway if a name belonging to them is used, regardless whether the implementation already does that or not.

edmz
  • 7,675
  • 2
  • 21
  • 43
  • 1
    Is there a way to see this libc shared library file on my computer? I mean I know I can't open it but just want to see where it resides. Am using Ubuntu 15.10 – J...S Nov 08 '16 at 10:55
  • 1
    @J...S Compile some dummy C code and `ldd prog`, you'll see `libc.so`. On my system it's placed under `/usr/lib`. – edmz Nov 08 '16 at 10:59
  • 1
    ldd prog ? What's that? Sorry I'm new in this! – J...S Nov 08 '16 at 11:22
  • 2
    @J...S Open a terminal and type `ldd` followed by the name of a C executable (I used prog to indicate that, but it can be anything :) – edmz Nov 08 '16 at 11:28
  • The comma is not optional. `func(char* arg, args...)` is GNU, while `func(char* arg, ...)` is C99+ (I think). – cat Nov 08 '16 at 18:43
  • 1
    @cat: You were right, but for another reason. My point is that `f(int arg...)` was valid C, but it only is in C++. That comment was useless anyways. – edmz Nov 08 '16 at 18:51
  • @J...S Could you please consider accepting zwol's answer? IMHO, it digs much deeper in the problem and should be worth the points. – edmz Nov 10 '16 at 16:05
10

stdarg header file is used to make functions accept undefined number of arguments, right?

No, <stdarg.h> just exposes an API that should be used to access extra arguments. There is no necessity to include that header if you want just declare function that accepts variable number of arguments, like this:

int foo(int a, ...);

This is a language feature and requires no extra declarations / definitions.

I found the following lines in the stdio.h file of gcc:

#if defined __USE_XOPEN || defined __USE_XOPEN2K8
# ifdef __GNUC__
#  ifndef _VA_LIST_DEFINED
typedef _G_va_list va_list;
#   define _VA_LIST_DEFINED
#  endif
# else
#  include <stdarg.h>//////////////////////stdarg.h IS INCLUDED!///////////
# endif
#endif

I guess this stuff is required only to declare things like vprintf() without internal including of <stdarg.h>:

int vprintf(const char *format, va_list ap);

To top it off:

  • Header that declares function with variable number of arguments shouldn't include <stdarg.h> internally.
  • Implementation of function with variable number of arguments must include <stdarg.h> and use va_list API to access extra arguments.
Sergio
  • 7,657
  • 2
  • 22
  • 45
  • `int vprintf(const char *format, va_list ap);` Does that mean `va_list` can be substituted for ... (ellipsis)? Does it have an advantage? – J...S Nov 08 '16 at 10:50
  • @J...S No, you can not. `va_list` is a type. This type was designed to hold info about extra arguments of `...` functions. You can threat it like a context that allows you to fetch extra arguments sequentially. Nevertheless, `vprintf()` takes exactly two arguments, while `printf()` - one or more. – Sergio Nov 08 '16 at 10:56
  • @J...S [This](http://man7.org/linux/man-pages/man3/stdarg.3.html) manpage may be helpful for you. – Sergio Nov 08 '16 at 10:58
  • @J...S Also it is difficult to say that `vprintf()` or `printf()` has some advantage. They simply were designed for different use cases. `printf()` is useful in most cases, but sometimes you may want to pass extra arguments of your function to another one and here `va_list` is helpful. – Sergio Nov 08 '16 at 11:01
  • The code the OP found appears to be implementing the POSIX.1-2008-only requirement for `stdio.h` to declare `va_list` - it has nothing to do with `vprintf`. – zwol Nov 08 '16 at 17:43
10

I'm first going to answer your question in terms of the C standard, because that is what tells you how you should write your code.

The C standard requires stdio.h to "behave as-if" it does not include stdarg.h. In other words, the macros va_start, va_arg, va_end, and va_copy, and the type va_list, are required not to be made available by including stdio.h. In other other words, this program is required not to compile:

#include <stdio.h>

unsigned sum(unsigned n, ...)
{
   unsigned total = 0;
   va_list ap;
   va_start(ap, n);
   while (n--) total += va_arg(ap, unsigned);
   va_end(ap);
   return total;
}

(This is a difference from C++. In C++, all standard library headers are allowed, but not required, to include each other.)

It is true that the implementation of printf (probably) uses the stdarg.h mechanism to access its arguments, but that just means that some files in the source code for the C library ("printf.c", perhaps) need to include stdarg.h as well as stdio.h; that doesn't affect your code.

It is also true that stdio.h declares functions that take va_list-typed arguments. If you look at those declarations, you will see that they actually use a typedef name that begins with either two underscores, or an underscore and a capital letter: for instance, with the same stdio.h you are looking at,

$ egrep '\<v(printf|scanf) *\(' /usr/include/stdio.h
extern int vprintf (const char *__restrict __format, _G_va_list __arg);
extern int vscanf (const char *__restrict __format, _G_va_list __arg);

All names that begin with two underscores, or an underscore and a capital letter, are reserved for the implementation - stdio.h is allowed to declare as many such names as it wants. Conversely, you, the application programmer, are not allowed to declare any such names, or use the ones that the implementation declares (except the subset that are documented, such as _POSIX_C_SOURCE and __GNUC__). The compiler will let you do it, but the effects are undefined.


Now I'm going to talk about the thing you quoted from stdio.h. Here it is again:

#if defined __USE_XOPEN || defined __USE_XOPEN2K8
# ifdef __GNUC__
#  ifndef _VA_LIST_DEFINED
typedef _G_va_list va_list;
#   define _VA_LIST_DEFINED
#  endif
# else
#  include <stdarg.h>
# endif
#endif

To understand what this is doing, you need to know three things:

  1. Recent "issues" of POSIX.1, the official specification of what it means to be a "Unix" operating system, add va_list to the set of things stdio.h is supposed to define. (Specifically, in Issue 6, va_list is defined by stdio.h as an "XSI" extension, and in Issue 7 it's mandatory.) This code defines va_list, but only if the program has requested Issue 6+XSI or Issue 7 features; that's what #if defined __USE_XOPEN || defined __USE_XOPEN2K8 means. Notice that it is using _G_va_list to define va_list, just as, elsewhere, it used _G_va_list to declare vprintf. _G_va_list is already available somehow.

  2. You cannot write the same typedef twice in the same translation unit. If stdio.h defined va_list without somehow notifying stdarg.h not to do it again,

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

    would not compile.

  3. GCC comes with a copy of stdarg.h, but it does not come with a copy of stdio.h. The stdio.h you are quoting comes from GNU libc, which is a separate project under the GNU umbrella, maintained by a separate (but overlapping) group of people. Crucially, GNU libc's headers cannot assume that they are being compiled by GCC.

So, the code you quoted defines va_list. If __GNUC__ is defined, which means the compiler is either GCC or a quirk-compatible clone, it assumes that it can communicate with stdarg.h using a macro named _VA_LIST_DEFINED, which is defined if and only if va_list is defined — but being a macro, you can check for it with #if. stdio.h can define va_list itself and then define _VA_LIST_DEFINED, and then stdarg.h won't do it, and

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

will compile fine. (If you look at GCC's stdarg.h, which is probably hiding in /usr/lib/gcc/something/something/include on your system, you will see the mirror image of this code, along with a hilariously long list of other macros that also mean "don't define va_list, I already did that" for other C libraries that GCC can, or could once, be used with.)

But if __GNUC__ is not defined, then stdio.h assumes it does not know how to communicate with stdarg.h. But it does know that it's safe to include stdarg.h twice in the same file, because the C standard requires that to work. So in order to get va_list defined, it just goes ahead and includes stdarg.h, and thus, the va_* macros that stdio.h isn't supposed to define will also be defined.

This is what the HTML5 people would call a "willful violation" of the C standard: it's wrong, on purpose, because being wrong in this way is less likely to break real-world code than any available alternative. In particular,

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

is overwhelmingly more likely to appear in real code than

#include <stdio.h>
#define va_start(x, y) /* something unrelated to variadic functions */

so it's much more important to make the first one work than the second, even though both are supposed to work.


Finally, you might still be wondering where the heck _G_va_list came from. It's not defined anywhere in stdio.h itself, so it must either be a compiler intrinsic, or be defined by one of the headers stdio.h includes. Here's how you find out everything that a system header includes:

$ echo '#include <stdio.h>' | gcc -H -xc -std=c11 -fsyntax-only - 2>&1 | grep '^\.'
. /usr/include/stdio.h
.. /usr/include/features.h
... /usr/include/x86_64-linux-gnu/sys/cdefs.h
.... /usr/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/include/x86_64-linux-gnu/gnu/stubs.h
.... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
.. /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h
.. /usr/include/x86_64-linux-gnu/bits/types.h
... /usr/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/include/x86_64-linux-gnu/bits/typesizes.h
.. /usr/include/libio.h
... /usr/include/_G_config.h
.... /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h
.... /usr/include/wchar.h
... /usr/lib/gcc/x86_64-linux-gnu/6/include/stdarg.h
.. /usr/include/x86_64-linux-gnu/bits/stdio_lim.h
.. /usr/include/x86_64-linux-gnu/bits/sys_errlist.h

I used -std=c11 to make sure I was not compiling in POSIX Issue 6+XSI nor Issue 7 modes, and yet we see stdarg.h in this list anyway — not included directly by stdio.h, but by libio.h, which is not a standard header. Let's have a look in there:

#include <_G_config.h>
/* ALL of these should be defined in _G_config.h */
/* ... */
#define _IO_va_list _G_va_list

/* This define avoids name pollution if we're using GNU stdarg.h */
#define __need___va_list
#include <stdarg.h>
#ifdef __GNUC_VA_LIST
# undef _IO_va_list
# define _IO_va_list __gnuc_va_list
#endif /* __GNUC_VA_LIST */

So libio.h includes stdarg.h in a special mode (here's another case where implementation macros are used to communicate between system headers), and expects it to define __gnuc_va_list, but it uses it to define _IO_va_list, not _G_va_list. _G_va_list is defined by _G_config.h...

/* These library features are always available in the GNU C library.  */
#define _G_va_list __gnuc_va_list

... in terms of __gnuc_va_list. That name is defined by stdarg.h:

/* Define __gnuc_va_list.  */
#ifndef __GNUC_VA_LIST
#define __GNUC_VA_LIST
typedef __builtin_va_list __gnuc_va_list;
#endif

And __builtin_va_list, finally, is an undocumented GCC intrinsic, meaning "whatever type is appropriate for va_list with the current ABI".

$ echo 'void foo(__builtin_va_list x) {}' |
    gcc -xc -std=c11 -fsyntax-only -; echo $?
0

(Yes, GNU libc's implementation of stdio is way more complicated than it has any excuse for being. The explanation is that back in elder days people tried to make its FILE object directly usable as a C++ filebuf. That hasn't worked in decades — in fact, I'm not sure if it ever worked; it had been abandoned before EGCS, which is as far back as I know the history — but there are many, many vestiges of the attempt hanging around still, either for binary backward compatibility or because nobody has gotten around to cleaning them up.)

(Yes, if I'm reading this correctly, GNU libc's stdio.h won't work right with a C compiler whose stdarg.h doesn't define __gnuc_va_list. This is abstractly wrong, but harmless; anyone wanting a shiny new non-GCC-compatible compiler to work with GNU libc is going to have a whole lot more things to worry about.)

zwol
  • 121,956
  • 33
  • 219
  • 328
  • If I understand your objection properly, _"it doesn't know how to tell stdarg.h "oi, I already defined va_list, don't do it again""_, well, it does: macros (include guards or something like `VA_LIST_DEFINED`) or still compiler magic. Might have missed your point, again. – edmz Nov 08 '16 at 20:01
  • @black Yes, that's what it's doing with `#define _VA_LIST_DEFINED` in the `__GNUC__` case. The thing is that `stdio.h` belongs to glibc but `stdarg.h` belongs to the compiler. If the compiler is not GCC (or a closely compatible compiler, e.g. clang, that also defines `__GNUC__`) glibc can't assume `#define _VA_LIST_DEFINED` will work. – zwol Nov 08 '16 at 20:20
  • Looking at `clang`'s `stdarg`, it does seem to adapt to GCC's word. It would be interesting a question covering this aspect. – edmz Nov 08 '16 at 20:37
  • @zwol You said **"All names that begin with two underscores, or an underscore and a capital letter, are reserved for the implementation - stdio.h is allowed to expose as many such names as it wants (and you're not supposed to use any of them)."** Does that mean I can't declare a macro which begins with 2 underscores or an underscore and a capital letter? – J...S Nov 09 '16 at 10:50
  • @J...S There are a small handful of exceptions, but yes, that is correct. Formally speaking, the compiler will let you do it, but the consequences are undefined. – zwol Nov 09 '16 at 13:39
  • @black I don't know exactly what you were thinking of with 'it would be interesting a question covering this aspect', but I just expanded the answer to go into more detail about `_VA_LIST_DEFINED` and `_G_va_list` and how `stdio.h` and `stdarg.h` cooperate. – zwol Nov 10 '16 at 15:20
  • @zwol Well, essentially, yes, the cooperation among different different standard header shippers. It's a rather serious problem (although easily solvable), there must have been some discussion in the past and thus worthy to dig in and bring it up in a article/QAs. Anyways, the answer I guess is simply GNU libc's spread, and so, relevance of its word. – edmz Nov 10 '16 at 16:01
  • I find your answer better and more thorough than mine, so, if you don't mind, I will ask the OP to mark yours as accepted. – edmz Nov 10 '16 at 16:02
  • @black *shrug* It's not important to me. – zwol Nov 11 '16 at 13:21
9

No, to use printf() all you need is #include <stdio.h>. There's no need for stdarg because printf is already compiled. The compiler only needs to see a prototype for printf to know that it is variadic (derived from the ellipsis ... in the prototype). If you look at the stdio library source code for printf you'll see the <stdarg.h> being included.

If you want to write your own variadic function, you must #include <stdarg.h> and use its macros accordingly. As you can see, if you forget to do that, the va_start/list/end symbols are unknown to the compiler.

If you want to see a real implementation of printf, look at the code in FreeBSD's standard I/O source, along with the source for vfprintf.

Jens
  • 61,963
  • 14
  • 104
  • 160
  • What do you mean printf() is already compiled? The part of code having the function definition of printf() is compiled before the rest of the code is compiled? – J...S Nov 08 '16 at 10:30
  • 3
    @J...S There is no code to *implement* `printf()` in ``. It's just a header, with *declarations* of functions and variables (and types etc). The code is already compiled, and part of the C runtime library (`glibc.so` on many Linux systems). – unwind Nov 08 '16 at 10:34
  • 1
    @J...S Already compiled means exactly that. The `` header only contains a prototype of the printf function; the printf code however is usually in a library of compiled code, termed the C-Library, often in a file named `libc.a` or `libc.so`, and used by the compiler when it finally links the program into an executable. Try using `gcc -v` to enable printing of the commands it runs. – Jens Nov 08 '16 at 10:37
  • @unwind Can I actually see the file of C runtime library on my computer? Am using Ubuntu 15.10. – J...S Nov 08 '16 at 10:52
  • @J...S What do you mean? Do you mean the source code of the runtime? If so, sure, [you browse their git repo](https://sourceware.org/git/?p=glibc.git;a=tree). Please note though that a C runtime is not a small project. – unwind Nov 08 '16 at 10:58
  • @unwind Not exactly I think. The part of the runtime library from where the pre-compiled code is taken when the program is run. The file which is used by my program for that. I know I can't be opening it. But still. – J...S Nov 08 '16 at 11:17
  • 1
    @J...S No code is "taken", unless you link statically. Normally you have an instance of the entire runtime library, and your code jumps into that when needed, i.e. when you call `printf()`. – unwind Nov 08 '16 at 11:55
  • @J...S Your precompiled C library is probably in the file `/lib/libc.so.6`. You may also have a `libc.a` file, which is sort of the same but intended for static linking (if you tell the compiler to use `libc.a`, it will copy the code for `printf` and other library functions into your program, whereas by default, with dynamic linking, your program will load `libc.so` at runtime and use the code from that). – Chortos-2 Nov 08 '16 at 20:59
  • @Jens The code for `printf()` you gave is really illuminating. – J...S Nov 09 '16 at 10:10
  • @Jens Is the `printf_l()` function defined after it in any way necessary to use that `printf()` ? And what is the use of `locale_t` which appears to be a data type? I googled it and saw something about **locale object**. Any idea what that is? – J...S Nov 09 '16 at 10:21
  • @J...S The locale determines things like the character used for the decimal point (which in a German locale is not a period, but a comma). The locale is set with `setlocale()`. This comment is too short to give an in-depth description of locales. – Jens Nov 09 '16 at 10:40
  • @Jens What about the `printf_l()` ? Is it indispensable for using this `printf()` ? – J...S Nov 09 '16 at 10:43
  • @J...S `printf`must support locales to be Standard C conformant. The `printf_l` function is an implementation detail of this particular FreeBSD libc. Other libraries use different names, or completely different code. Don't try to call it directly. It probably won't even link on Ubuntu with a GNU libc. – Jens Nov 09 '16 at 12:10
  • @Chortos-2 Can we too make a piece of code which is pre-compiled and stored somewhere to be used later by other programs? – J...S Nov 10 '16 at 06:28
  • @J...S Yes, that concept is called libraries, which are collections of object files, which in turn are collections of functions and objects. A good introduction is Peter van der Linden's book **Expert C Programming**: – Jens Nov 10 '16 at 07:39
2

Fundamentals of splitting a module into a header file and a source file:

  • In the header file, you put only the interface of your module
  • In the source file, you put the implementation of your module

So even if the implementation of printf makes use of va_arg as you speculate:

  • In stdio.h, the author only declared int printf(const char* format, ...);
  • In stdio.c, the author implemented printf using va_arg
barak manos
  • 27,530
  • 9
  • 49
  • 106
  • stdio.h also declares vprintf and other v-functions. In order to even declare such a function, user code would need stdarg.h. *But stdio.h does not*. – n. 'pronouns' m. Nov 08 '16 at 10:49
  • @n.m.: Well, true, but I was referring solely to the assumption made by the OP, according to which, if a function's implementation relies on some definition, then the function interface requires that definition too. – barak manos Nov 08 '16 at 10:53
2

This implementation of stdio.h does not include stdarg.h when compiled with gcc. It works by magic that compiler writers always have up their sleeves.

Your C source files must include every system header they reference anyway. It is a requirement of the C standard. That is, if your source code requires definitions present in stdarg.h, it must contain #include <stdarg.h> directive either directly, or in one of your header files that it includes. It cannot rely on stdarg.h being included in other standard headers, even if they do in fact include it.

n. 'pronouns' m.
  • 95,181
  • 13
  • 111
  • 206
  • Here's something I found [here](http://stackoverflow.com/a/2550799/5375464). It says `size_t` defined in `stddef.h` can be used by a program including `stdlib.h` as it includes `stddef.h` . _"It can be further imported by inclusion of stdlib.h as this file internally sub includes stddef.h."_ And `stddef.h` is a _**standard**_ header file and not a _**custom**_ one, right? – J...S Nov 10 '16 at 07:21
  • @J...S the wikipedia article doesn't say what the linker answer claims it to say. Perhaps that statement was found to be false, and removed. I could not find it in the history. Anyway, if **the standard** says header A includes header B, you can rely on it; I could not find any such statement in the standard though. – n. 'pronouns' m. Nov 10 '16 at 07:47
1

The <stdarg.h> file is required to be included only if you are going to implement a variable number of arguments function. It's not required to be able to use printf(3) and friends. Only if you are going to process arguments on a variable number of args function, you'll need the va_list type, and the va_start, va_arg and va_end macros. So, only then you'll need to forcibly include that file.

In general, you are not warranted that <stdarg.h> will be included with just including <stdio.h> Indeed, the code you cite only includes it, if __GNU_C__ is not defined (which I suspect, is the case, so it's not included in your case) and this macro is defined if you are using the gcc compiler.

If you are going to create variable argument passing functions in your code, the best approach is not to expect another included file to include it, but do it yourself (as a client for the requested functionality you are) everywhere you are using the va_list type, or va_start, va_arg or va_end macros.

In the past, there was some confusion about double inclusion, as some header files were not protected from double inclusion (including twice or more times the same include file produced errors about doubly defined macros or similar and you had to go with care) but today, this is not an issue and normally all standard header fields are protected from double inclusion.

Luis Colorado
  • 8,037
  • 1
  • 10
  • 27
-1

Okay, there is the "regular" printf family: printf, fprintf, dprintf, sprintf, and snprintf. And then there's the variable number of arguments printf family: vprintf, vfprintf, vdprintf, vsprintf, and vsnprintf.

To use a variable list of arguments with either, you need to declare stdarg.h. stdarg.h defines all the macros you're using: va_list, va_start, va_arg, va_end, and va_copy.

Terry Wendt
  • 28
  • 1
  • 7
  • This doesn't answer the question as asked, which was whether including `` is sufficient to allow the use of varargs macros defined by ``. – Toby Speight Nov 09 '16 at 10:39
  • @TobySpeight The "question as asked" has changed since I posted the answer. And the OP didn't seem to be aware that he was using macros at all. – Terry Wendt Nov 09 '16 at 18:54