Topic : An introduction to C
Author : Tom Torfs
Page : << Previous 19  Next >>
Go to page :


ellipsis). That's also a reason why the ellipsis can never be on its own, and at least one fixed parameter is required.

   sum = 0;
   for (i=0; i<numbers; i++)
      sum += va_arg(vl,int);


This for() loop simply adds up a number of integers. The fixed numbers parameter is used to pass the number of integers that will follow in the variable argument list. The va_arg() function returns the next variable argument. The first parameter to va_arg() should again be the va_list variable and the second parameter should be the type of the argument (in our case int).
We use the fixed numbers parameter here to determine how many variable arguments there are. Another commonly used method is to use some sort of "terminator" value, e.g. the value 0 would end the list. However, keep in mind that at least 1 fixed parameter is required.

   va_end(vl);

The variable argument list processing is closed with the va_end() function, again with the va_list variable as parameter. This is required.

   return sum;

And the calculated sum is returned to the caller.

   int a,b;

   printf("Sum #1 = %d\n",
          sumintegers(10,1,2,3,5,7,9,11,13,17,19));

   a = 100;
   b = 200;

   printf("Sum #2 = %d\n",
          sumintegers(2,a,b));


This code uses the sumintegers() function twice: once to calculate the sum of 10 constant numbers, and once to calculate the sum of 2 variables. The disadvantage to this approach is that we must manually count the number of arguments, a disadvantage that would not be present if we had used the terminator-value approach mentioned above.14.3. Example: a printf() encapsulation

/* prog14-2.c: myprintf */

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

void myprintf(const char *text, const char *fmt, ...)
{
   va_list vl;

   printf("myprintf(): %s: ", text);

   va_start(vl,fmt);
   vprintf(fmt,vl);
   va_end(vl);

   printf("\n");
}

int main(void)
{
   int a = 5;

   myprintf("Test #1", "a = %d", a);

   myprintf("Test #2", "File name %s, line number %ld",
                       __FILE__, (long)__LINE__);

   return 0;
}


The output should be something like:

myprintf(): Test #1: a = 5
myprintf(): Test #2: File name prog14-2.c, line number 26

One of the most commonly used variable argument functions is printf(). To determine the number and type of its arguments it uses a format string with format specifiers (see 3.3. printf).
Sometimes it may be useful to encapsulate the printf() function in a function of your own, so that you can add some text of your own, or write a copy of the text to a logfile, etc. That's what the above program does.

#include <stdarg.h>

Of course, the stdarg.h header is needed here as well.

void myprintf(const char *text, const char *fmt, ...)
{
}


Our myprintf() function will accept two fixed parameters of type pointer to constant char (i.o.w. strings that will not be modified, so for example a string literal would be a suitable value for these) followed by a variable argument list. The first parameter accepts some text which will be printed before the normal printf() output and the second parameter is the format string for the printf() function.

   va_list vl;

The vl variable is familiar.

   printf("myprintf(): %s: ", text);

Some text is printed, to show that we have encapsulated the printf() function.

   va_start(vl,fmt);

The variable argument list is initialized. The last fixed parameter before the variable argument list is the format string.

   vprintf(fmt,vl);

The vprintf() function is similar to the printf() function. However, the variable values corresponding to the format specifiers in the format string are no longer passed as separate parameters after the format string, but instead a va_list variable is accepted, so that our variable argument list can be used instead.

   va_end(vl);

The variable argument list processing is closed. This is the only thing we can do with the va_list after the vprintf() call (or a call to any function that takes a va_list parameter). If we wanted to use the va_list variable after the call, we would have to close processing with va_end(), restart it with va_start(), do what has to be done, and close it again with va_end().

   printf("\n");

A newline is appended to the previous output.

   int a = 5;

   myprintf("Test #1", "a = %d", a);

   myprintf("Test #2", "File name %s, line number %ld",
                       __FILE__, (long)__LINE__);


A small test of our myprintf() function, by printing the value of a variable, and the value of the __FILE__ and __LINE__ macros (see 13. Preprocessor macros/conditionals).

15. Modular approach
15.1. The module program
This program consists of three files:

/* prog15-1.c: module 1 */

#include <stdio.h>

#include "prog15-2.h"

int main(void)
{
   myfunction(25);

   printf("This is %s. The value of myvariable is %d\n",
          __FILE__, myvariable);

   return 0;
}


/* prog15-2.c: module 2 */

#include <stdio.h>

int myvariable = 42;

void myfunction(int number)
{
   printf("This is %s. The sum of number and myvariable is %d.\n",
          __FILE__, number+myvariable);
}


/* prog15-2.h: module 2 header file */

extern int myvariable;

void myfunction(int number);


The output of this program should be something like this:

This is prog15-2.c. The sum of number and myvariable is 67.
This is prog15-1.c. The value of myvariable is 42

To compile this program you must compile and link both modules. Depending on your system, this may be done by putting both prog15-1.c and prog15-2.c on your command line or by inserting them in your make- or projectfile. If during linking you get "unresolved external myvariable" or similar error messages, you are not linking in the correct modules. See your compiler manual for more information.15.2. Using different modules
When you start writing larger programs, you'll soon notice that if you keep all your functions in one module (one .c file), this file soon becomes very difficult to overview. Not to mention the fact that if one thing changes in this file the entire program needs to be recompiled.
By splitting up your program in separate modules, each combining the functions that are related to one specific task, the program will become much easier to maintain and only those modules where something has changed need to be recompiled.
Let's start by analyzing the prog15-1.c module:

#include "prog15-2.h"

As mentioned in 2.3. #include, this form with the double quotes instead of the angle brackets is used to include header files that are part of your program. In this case the prog15-2.h header declares the variables and functions in the prog15-2.c module that we'll be using in our prog15-1.c module. The contents of this header file are discussed below.

int main(void)
{
   myfunction(25);

   printf("This is %s. The value of myvariable is %d\n",
          __FILE__, myvariable);

   return 0;
}


The prog15-1.c module contains the main() function (only 1 module in a program may contain a main() function). This code uses a function (myfunction) and a variable (myvariable) that are not defined in this module. They are declared in the prog15-2.h header file and defined in the prog15-2.c module, which we'll analyze now:

int myvariable = 42;

void myfunction(int number)
{
   printf("This is %s. The sum of number and myvariable is %d.\n",
          __FILE__, number+myvariable);
}


As we've seen in 3.2. Defining variables, since myvariable is not inside a function and it isn't defined using the static keyword, this is a global variable. That means it can be accessed from all modules, in our case this means the prog15-1.c module can access it.
Similarly, since the myfunction() is not defined as static, it can also be accessed from all modules. If you don't want this, you should define it like this:

static void myfunction(int number)
{
}


And now the header file that allows the prog15-1.c module to access the global variables and functions from the prog15-2.c module:

extern int myvariable;

void myfunction(int number);


The first line looks like the definition of an integer variable called myvariable. The extern makes it a declaration of a variable that is defined in another module (in this case prog15-2.c). The extern is like saying "you can assume the variable is there and that it is of this type". If it turns out afterwards that the variable isn't really there, your linker will most likely object violently.
The second line looks like the definition of the myfunction() function. The only difference is that where normally the function definition would be between { and }, there's now a semicolon. This is called a prototype. Similar to the above case, it is like saying "you

Page : << Previous 19  Next >>