First | Next | Previous | Last | Glossary | About

# Functions - Default arguments, overloading and variable argument lists

- Introduction
- Default arguments
- Tutorial 1 - default arguments
- Overloading functions
- Tutorial 2 - Overloading functions
- Variable argument lists
- Tutorial 3 - Variable argument lists

## Introduction

We now finish most of the work with functions by looking at some topics which tend to set C and C++ apart from many other languages.

## Default arguments

    //Example 1 - default arguments
    #include <iostream>
    #include <time.h>
    #include <stdlib.h>

    float read_floats( float [100],
                       int start = 10,
                       int stop = 20);

    int main()
    {
     float a[100];

     srand (time (0));

     for (int i = 0; i < 100; i++)
      a[i] = rand();

     cout << read_floats(a,19) << endl;
     cout << read_floats(a) << endl;
     cout << read_floats(a,83,94) << endl;

     return 1;
    }

    float read_floats(float a[], int start, int stop)
    { int i;
      float total = 0;

      cout << "Summing "
           << start
    	   << " to "
    	   << stop
    	   << " ";
      for (i = start;
           i <= stop;
           total = total + a[i], i++);
      return total;
    }

Assume you have created a function to add a number of values in an array, for instance the array might contain 100 floats. Your function takes a starting index (start) and a finishing index (stop) and sums the floats over the range start to stop.

We can declare a function and initialise its arguments:

    float read_floats( float [],
                       int start = 10,
                       int stop = 20);

Now if we call the function like this:

     cout << read_floats(a) << endl;

that is, supplying only the first argument, the start and stop arguments will take the default values.

We can also supply just one of the default arguments:

     cout << read_floats(a,19) << endl;

But we can't do this:

     cout << read_floats(a,,19) << endl;

Arguments can't be "defaulted" and then followed by a non-default argument.

## Tutorial 1 - default arguments

Exercise 1.1

Try the example program first and make sure that it works.

Exercise 1.2

Example 1 is quite a dangerous little program. What is wrong with the following calls to read_floats():

1. read_floats(a, 21, 11);
2. read_floats(a, 21, 110);
3. read_floats(a, -21, 0);

Modify Example 1 so that the start and stop arguments are never out of range and start is always less than or equal to stop.

What simple modification could be made to read_floats() so that it could manage arrays of an undefined size? There is a sample answer here if you get stuck.

Return to top of page

## Overloading functions

    //Bad Identifier
    #include <iostream>

    int main()
    {
      int i = 0;   //i1
      {int i = 5;} //i2
      int i = 10;  //i3
      return 0;
    }

    bad_id.cc: In function `int main()':
    bad_id.cc:9: redeclaration of `int i'
    bad_id.cc:5: `int i' previously declared here

C and C++ don't normally allow you to declare the same identifier more than once in the same scope.

The example shows that we can declare an identifier with the same name but in different scopes. Identifiers i1 and i2 are quite OK. However i3 will create the error message bad_id.cc:9: redeclaration of `int i'.

    // Function overloading

    #include <iostream>
    #include <iomanip>

    int add_values(int a, int b)
    { return (a + b); }

    int add_values(int a, int b, int c)
    { return(a + b + c); }

    float add_values(double a, double b)
    { return a + b; }

    float add_values(double a, int b)
    { return a + b; }

    void main(void)
    {
     cout << "100 + 801       = " << setw(4)
          << add_values(100, 801) << endl;
     cout << "100 + 201 + 700 = " << setw(4)
          << add_values(100,201,700)  << endl;
     cout << "11.74 + 22.37 = " << add_values(11.74, 22.37)
          << endl;
     cout << "11.74 + 22 = " << add_values(11.74, 22)
          << endl;
    }

The situation is different with functions. Functions can have the same identifier in the same scope but be quite different functions. For instance you might have several functions all called add_values() but you can ensure each function is different by ensuring each function has a different argument list.

Here we have four functions called add_values() but each has different arguments. When this program is compiled the compiler will generate different signatures for each of the functions based on the function name and the argument list.

If similarly named functions have different signatures then they are different functions.

Is function overloading a good thing? It's certainly useful to be able to call functions which do the same things with different data types and once the program is running everything works fine. The major problem is a coding time problem - making sure that you call the correct function with the correct arguments.

## Tutorial 2 - Overloading functions

Exercise 2.1

Try the example program first and make sure that it works.

Return to top of page

## Variable argument lists

    #include <iostream>
    #include <stdarg.h>

    void varints(int , ... );
    void varstrings(int , ... );

    void main(void)
    {
      varints(3,1,2,3);
      varints(7,1,2,3,4,5,6,7);
      varstrings(3, "David",
                    "Robert",
                    "Beech");
    }

    void varints(int a, ... )
    {
      va_list argument;

      va_start(argument, a);
      cout << "Function has "
           << a
           << " arguments\n";
      for (int count = 0;
               count < a;
               count++)
       cout << va_arg(argument, int)
            << " ";
      cout << endl;
      va_end(argument);
    }

    void varstrings(int a, ... )
    { va_list argument;

      va_start(argument, a);
      cout << "Function has "
           << a
           << " arguments\n";
      for (int count = 0;
               count < a;
               count++)
       cout << va_arg(argument, char *)
            << " ";
      cout << endl;
      va_end(argument);
    }

You are used to fastidiously ensuring that when you call a function you match each of the actual parameters with each of the function arguments but ... a function can have a variable number of arguments!

To create and use functions with an arbitrary number of arguments we:

1. Include the header file stdargs.h. This makes available the data type va_list and three macros: va_start(), va_arg() and va_end().
2. Prototype the function or functions so that the argument list contains as the first argument an int followed by .... When the function is called the int argument contains the number of actual arguments that follow the int. The ... stands in place of the actual arguments, excluding the first of course.
3. The function definition now declares a variable of type va_list. This variable is a pointer to a char, ie a C style string. Next we use the macro va_start() to get the count of arguments and the pointer to the string of arguments represented by .... We are almost there.
4. The next step is to extract the arguments and use them and we need the va_arg() macro to do this. Each time we use var_arg() we extract the next available argument from the list. We also need to tell var_arg() what the data type is that we are extracting, eg va_arg(argument, int) states that an int will be extracted whereas va_arg(argument, char *) means a C style string will be extracted.
5. Last of all we release the va_list variable from our clutches by using the va_end() macro.

    void naughty_float(int argc, ...)
    { va_list arguments;

      va_start(arguments, argc);
      for (int i = 0; i < argc; i++)
       cout << va_arg(arguments, float) << endl;
      va_end(arguments);
    }

    void nice_float(int argc, ...)
    { va_list arguments;

      va_start(arguments, argc);
      for (int i = 0; i < argc; i++)
       cout << va_arg(arguments, double) << endl;
      va_end(arguments);
    }

There is a small catch with using va_arg() which this example shows. The second argument to va_arg() should not be a "promotable" data type, ie use double rather than float and integer rather than short. If you try the example functions out you will see what I mean.

The va_arg() macro must make some decisions about the size of the datatype of the argument that will be extracted from the argument list. This means that floats are treated like doubles and shorts like integers. If the compiler treats float and double as the same size and short and int as the same size then there probably won't be a problem. It's not an issue I understand clearly myself but I have told you about it. My duty is done.

Return to top of page

## Tutorial 3 - Variable argument lists

Exercise 3.1

Try the first example program and make sure it works.

Exercise 3.2

If you haven't already done so use the two functions naughty_float() and nice_float() in a program and compare the results. Why do they differ?

Exercise 3.3

Write a program which takes a variable number of integers from the keyboard and which sums the integers.

Return to top of page

First | Next | Previous | Last | Glossary | About

Copyright © 1999 - 2001 David Beech