Subprograms

A subprogram is a self-contained, but not standalone, program unit. It performs a specific task, usually by accepting parameters and returning a result to the unit that invokes (calls) it. Subprograms are essential to good coding practice. Among other benefits, they are

  • Reusable. They can be called anywhere the task is to be performed.
  • Easier to test and debug than a large, catch-all unit.
  • Effective at reducing errors such as cut-and-paste mistakes.

Other general names for subprograms are routines, procedures, and methods. The word “method” is generally reserved for procedures defined within an object, but it is not conceptually different from any other subprogram.

Subprograms must be invoked or called in order for any of their code to be executed.

Functions and Subroutines

Functions take any number (up to compiler limits) of arguments and return one item. This item can be a compound type. Functions must be declared to a type like variables. The return statement returns the result to the caller.

Subroutines take any number of arguments (up to the compiler limit) and return any number of arguments. Bidirectional communication can take place through the argument list. Strictly speaking, all subprograms in C++ are functions, but the ability to declare a void return “type” and to pass by reference means some are effectively subroutines.

Variables in the argument list are often called dummy arguments since they stand for actual arguments that are defined in the calling unit. Like any variable, they must be declared explicitly. In C++ these declarations are included in the argument list. In C++ either the function or its prototype must appear before any invocation. The prototype consists only of the declaration of the function along with its argument list. Only the types are required in the argument list of a prototype; dummy variables are optional. The prototype provides the compiler with information about the number and type of arguments to the function, which enables it to check each invocation to ensure that the argument lists match.

C++ does not define or expect a keyword for a function declaration. The parentheses following the name are required, however.

//Prototype
float myfunc(float, float, float);

//Definition
float myfunc(float v1, float v2, float v3) {
    return v1*v2-v3;
}

The prototype is frequently declared in a header file ending in .h or .hpp. The function definition is generally then in a corresponding .cpp or .cxx file.

Functions are invoked by name. They can enter into an expression anywhere on the right-hand side of the assignment operator (=).

z=4.*myfunc(var1,var2,var3)

The names of the actual arguments when the function is invoked need not be the same as those of the dummies, but the number and type must match.

A “subroutine” would be declared void (so it has no return value). If it is a utility routine, such as to print a message, it does not require an argument list. In C++ (but not C) an empty argument list in the declaration is equivalent to a single void argument.

#include <iostream>

void printme();

int main() {

    printme();

    return 0;
}

void printme() {

    std::cout<<"Mod 11,3 is "<<11%3<<"\n";
    std::cout<<"Mod -11,3 is "<<-11%3<<"\n";
    std::cout<<"Mod 11,-3 is "<<11%-3<<"\n";
}

Default (Optional) Arguments

The programmer can provide default values for dummy arguments in a function. If not passed, these take their default values.

int myfunc(int i, int j=0, int k=1) {
//code
}

This function could be called as

myfunc(i,j,k);
myfunc(i,j);
myfunc(i);

Since they are not required, default arguments are also said to be optional. Values passed explicitly through the argument list override the defaults.

Default arguments may be set in either the declaration (prototype) or the definition of the function, but not both. The values are set at compile time.

#include <iostream>

int myfunc(int, int=0, int=1 );

int main() {

    int i=7;
    std::cout<<myfunc(i)<<"\n";
    int j=5;
    std::cout<<myfunc(i,j)<<"\n";
    int k=3;
    std::cout<<myfunc(i,j,k)<<"\n";

    return 0;
}

int myfunc(int i, int j, int k) {
    return i+j+k;
}

C++ does not support what other languages call “keyword” arguments. Optional arguments must be kept in position and may not be rearranged or skipped.

Exercises

  1. Write a program that evaluates the function $$f(x)=\frac{1}{\pi (1+x^2)}$$ for 401 values of x equally spaced between -4.0 and 4.0 inclusive.
    Put the values into an array or vector variable x. Use variables for the starting and ending values of x and the number of values.
    Write a function to evaluate f(x) for any given real (scalar) value of x and call it each time through your loop.
Example Solution

#include <iostream>
#include <fstream>
#include <cmath>

using namespace std;

const double pi=4.0*atan(1.0);

double func(float x) {
	return 1./(pi*(1.+pow(x,2)));
}

int main() {

    const int nx=401;
    double x[nx];
    double y[nx];

    double xstart=-4.0, xend=4.0;
    double incr=(xend-xstart)/(nx-1);

    ofstream fout;
    fout.open("feval.csv");

    x[0]=xstart;
    y[0]=func(x[0]);
    fout<<x[0]<<","<<y[0]<<"\n";

    for (int i=1; i<nx; ++i) {
	x[i]=x[i-1]+incr;
	y[i]=func(x[i]);
        fout<<x[i]<<","<<y[i]<<"\n";
    }

    return 0;
}

Print the values and the corresponding function evaluation to a comma-separated-values (CSV) file. Use software such as Excel, Python, Matlab, or anything else you know to plot the result.

  1. Modify your program from Exercise 1 to use a prototype in a header file ending in .h, with the implementation in a .cpp or .cxx file. Refer to the chapter on linking if you need a refresher on building with multiple files. This simple example probably does not require a Makefile.
Example Solution

#include <cmath>

const double pi=4.0*atan(1.0);

double func(float x) {
        return 1./(pi*(1.+pow(x,2)));
}


#include <iostream>
#include <fstream>
#include "function2.h"

using namespace std;

int main() {

    const int nx=401;
    double x[nx];
    double y[nx];

    double xstart=-4.0, xend=4.0;
    double incr=(xend-xstart)/(nx-1);

    ofstream fout;
    fout.open("feval.csv");

    x[0]=xstart;
    y[0]=func(x[0]);
    fout<<x[0]<<","<<y[0]<<"\n";

    for (int i=1; i<nx; ++i) {
	x[i]=x[i-1]+incr;
	y[i]=func(x[i]);
        fout<<x[i]<<","<<y[i]<<"\n";
    }

    return 0;
}

Previous
Next