Formatted I/O






Formatted I/O

Variable-Length Argument Lists

Most programmers were introduced to variable-length argument lists when they learned the wonders of printf. The prototype for printf looks like this:

int printf(const char *fmt, ...);

The ellipsis at the end of the prototype says that the function takes an unspecified number of arguments of unspecified types in addition to the required argument fmt. As we all know, printf copies the contents of fmt to stdout, replacing each printf conversion specifier in fmt with text representing the value of the corresponding additional argument.[15]

[15] You get extra credit if you know what the value returned by printf means.

Many programmers fall down, however, if they have to write their own function that takes a variable-length argument list and pass that list to another function that takes a variable-length argument list. For example, suppose that you need to write a function that takes the name of a log file, a format specifier, and a variable-length argument list holding values to be logged. The function should use fopen to open the file, pass the resulting FILE* and the format specifier and the additional arguments to fprintf to write the information to the log file, and, when fprintf returns, close the log file. For most programmers, the first attempt at writing this function looks something like this:

   int logdata(const char *fname, const char *fmt , ...)
{
FILE *fp = fopen(fname,"w");
 // how the hell do I call fprintf?
fclose(fp);
return ?;
}

The answer to the question in the comment is that you can't call fprintf with the additional arguments. Instead, you need to use vfprintf, which is just like fprintf but takes a final argument of type va_list instead of an ellipsis. The argument of type va_list, in turn, points at the additional arguments in the call to log.

Figure. Using va_list (compat/valist.c)

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

int logdata(const char *fname,const char *fmt,...)
{   // log formatted data to file fname
int res = -1;
FILE *fp = fopen(fname,"a");
if (fp)
  { // set up argument list, call vfprintf
  va_list ap;
  va_start(ap,fmt);
  res = vfprintf(fp,fmt,ap);
  fclose(fp);
  va_end(ap);

  }
return res;
}

int main()
  {   // demonstrate use of variable-length argument lists
  FILE  *fp;
  char buf[128];
  logdata("test.txt","%d\n",3);
  logdata("test.txt","%d %d %d\n",3,4,5);
  fp = fopen("test.txt","r");
  while(fgets(buf,sizeof(buf),fp))
    printf(buf);
  fclose(fp);
  return 0;
  }

Of course, in order to do that with the rest of the printf and scanf family, you need versions of those functions that take a final argument of type va_list. Several of these in the C90 standard, but there were quite a few that were missing. The C99 standard fills in these gaps, as we see in Section 22.6.3.

Copying Variable-Length Argument Lists

If you write code that uses variable-length argument lists, you might occasionally need to copy the object that holds the context information for the variable-length argument list. Prior to C99, that was a problem because there are no constraints on the type of that object. On some implementations, it's an array, and arrays can't be copied directly. The solution to this problem in C99 was to add a macro, va_copy, in the header <stdarg.h>. tr1 does the same, adding the macro to both the header <cstdarg> and the header <stdarg.h>.

#define va_copy(dst,src) <void expression>

The arguments dst and src must be objects of type va_list. The macro copies the context information in src to the object designated by dst.

22.6.3. The printf and scanf Functions

Figure shows the names of all of the printf and scanf variants defined in C99. The ones that are new in C99 are marked with an asterisk. The functions in the second and fourth columns take a final argument of type va_list; the ones in the first and third columns take an arbitrary number of arguments of more or less arbitrary types. Functions in the first and second columns take string arguments of type char_t*; those in the third and fourth columns take wchar_t*.

Figure printf and scanf Functions

<stdio.h>

<wchar.h>

printf

vprintf

wprintf

vwprintf

fprintf

vfprintf

fwprintf

vfwprintf

sprintf

vsprintf

swprintf

vswprintf

snprintf*

vsnprintf*

  

scanf

vscanf*

wscanf

vwscanf*

fscanf

vfscanf*

fwscanf

vfwscanf*

sscanf

vsscanf*

swscanf

vswscanf*


The functions in the first row write formatted text to standard output. The functions in the second row write to a file, identified by an initial argument of type FILE*. The functions in the third row write to a character array, identified by an initial argument of type char* or wchar_t*. The functions in the fourth row also write to a character array but take an additional argument that gives the maximum number of charactersincluding the terminating null characterto write. Similarly, the functions in the fifth row read from standard input, those in the sixth row read from a file, and those in the last row read from a character array.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows