Functions

A C function consists of a block of program code that can be called from any point in a program. A function can accept parameters (or arguments), and can return a value to the routine that calls it. The general format of a C function is shown below.

type name (parameter1, parameter2, ...) {statements}

The type component specifies what data type (if any) will be returned to the calling routine by the function, while the name of the function must be used when the function is called. Each parameter consists of a data type specifier followed by a parameter name, which will be used by the function's code to reference the parameter, in the same way it would reference a local variable.

If there are two or more parameters, they are separated by commas. If no parameter list is provided, the function does not accept any parameters and is defined either with empty parenthesis or with the keyword void. If the parameter list is terminated with an ellipsis (,...), the number of parameters may vary. The function's statements form the body of the function, and are enclosed within curly braces. The short program below demonstrates how a function may be created and used.

// Example program 1
#include <stdio.h>
#include <conio.h>

/* the function "circ" accepts a floating point value
as a parameter, and returns a floating point value */

float circ(float rad)
{
  return 2 * rad * 3.14;
}

void main ()
{
  float radius = 3;
  float circumference;

  circumference = circ(radius);
  printf("Circumference is: %8.3f.", circumference);
  printf("\n\nPress any key to continue.");
  getch();
}

Note that, although we have declared and defined the circ() function ahead of the main() function in the code, program execution always starts with main(). When the function is called by main(), program execution moves to the circ() function, which executes the code within the function body. Once the function has completed its assigned task, which is to calculate the circumference of a circle based on the radius of the circle (which is passed to it as a parameter), it returns the calculated value to the calling procedure (main()). Program execution then continues form the program statement that follows the function call in main(). The output from example program 1 is shown below.


The output from example program 1

The output from example program 1


Note also that variables declared within a function body have local scope only. In other words, they are not visible outside the function in which they are declared. In the example program above, therefore, it would not be possible for the circ() function to refer directly to the variable radius, since it is local to the function main(). We can, however, pass a copy of the variable radius to the function as a parameter. If we want variables to be accessible from anywhere in the program, we must declare them as global variables, i.e. outside of any function. Global variables normally appear before any function declarations in the code.

A function call can be used within an expression in the same way as a variable, in the sense that its return value can be used directly. The following short program illustrates the use of a function in this way.

// Example program 2
#include <stdio.h>
#include <conio.h>

/* This program calculates and adds together the area of two circles for which the radius is provided by the user. The "area" function accepts a floating point value as a parameter, and returns a floating point value */

float area(float rad)
{
  return rad * rad * 3.14;
}

void main()
{
  float radius1, radius2, total_area;
  printf("Please enter radius (in metres) for circle 1: ");
  scanf("%f", &radius1);
  printf("\n\nPlease enter radius (in metres) for circle 2: ");
  scanf("%f", &radius2);
  total_area = area(radius1) + area(radius2);
  printf("\n\nThe total area covered by both circles is: ");
  printf("%8.3f square metres.", total_area);
  printf("\n\nPress any key to continue.");
  fflush(stdin);
  getch();
}

In the above program, we have made two calls to the area() function in the same statement, the return values of which are added together and assigned to the variable total_area.


The output from example program 2

The output from example program 2



Functions with no return value

It is sometimes the case that we need to create a function that performs some task, but does not return a value. Because the function declaration requires that we specify a type, the void keyword is used to specify that there is no return value. The general format of such a function is shown below.

void name (parameter1, parameter2, ...) {statements}

It is equally possible that we could declare a function that takes no parameters. We must still provide the parentheses following the name of the function, but in this case they will be empty. The format of a function that returns no value, and accepts no parameters, is shown below.

void name () {statements}

In some programming languages, this type of function might be called a procedure rather than a function, since these languages draw a distinction between a program routine that returns a value to its calling process, and one that doesn't. The following short example program uses a function that simply displays a message on the screen.

//Example program 3
#include <stdio.h>
#include <conio.h>

void display_message()
{
  printf("Hello World!");
}

void main()
{
  display_message();
  printf("\n\nPress any key to continue.");
  getch();
}


The output from example program 3

The output from example program 3



Passing variables by reference

In the functions we have created thus far, any variables passed to those functions as arguments have been passed by value. In other words, a copy of the variable has been passed to the function. Whatever actions are carried out by the function cannot, therefore, change the value of the variable itself. There may be occasions, however, when we want the function to change the value of one or more external variables.

One method would be to assign the function's return value to the variable. This would work if only one variable needs to be changed, since a function can only directly return one value. Another way would be to declare the affected variables as global, so that the function could access them directly. Unfortunately, this method would make them accessible by other parts of the program, and is not considered good programming practice. The short program below demonstrates how variables may be passed to a function by reference.

//Example program 4
#include <stdio.h>
#include <conio.h>

void circle(float *radius, float *circumference, float *area)
{
  *radius = 5;
  *circumference = 2 * *radius * 3.14;
  *area = *radius * *radius * 3.14;
}

void main()
{
  float radius, circumference, area;

  circle(&radius, &circumference, &area);
  printf("Radius is: %6.1f\n\n", radius);
  printf("Circumference is: %6.1f\n\n", circumference);
  printf("Area is: %6.1f", area);
  printf("\n\nPress any key to continue.");
  getch();
}

Note that, in the declaration of the function circle(), the name of each parameter is prefixed by an asterisk (*). This tells us that, rather than passing a copy of the variable to the function (i.e. passing by value), what we are passing to the function here is the address of the variable. In other words, we are providing the function with a pointer to the variable, so that it can manipulate the variable itself.

Any changes made to the value of these variables within the function remain in effect after the function has returned control of program execution to the calling process, and any further reference to these variables by the program's code will reflect this. One of the main advantages of passing variables by reference in this manner is that it allows us to create functions that can, effectively, return multiple values.


The output from example program 4

The output from example program 4



Recursive functions

Recursive functions are functions that call themselves, and are often used to carry out repetitive tasks such as sorting lists or calculating the factorial of a number. In order to calculate the factorial of the number six (6!), for example, we could use the following expression:

6! = 6 * 5 * 4 * 3 * 2 * 1 = 720

The short program below uses a recursive function to calculate the factorial of a number entered by the user.

//Example program 5
#include <stdio.h>
#include <conio.h>

long factorial (long number)
{
  if (number > 1)
  {
    return number * factorial (number - 1);
  }
  else
  {
    return 1;
  }
}

void main()
{
  long x;

  printf("Please enter a number: ");
  scanf("%ld", &x);
  printf("\n\nThe factorial of %ld is: %ld", x, factorial(x));
  fflush(stdin);
  printf("\n\nPress any key to continue.");
  getch();
}

The recursive function factorial() calls itself repeatedly until the exit condition (that the number passed to the function as a parameter is not greater than 1) is met. Since each successive call to factorial() passes the input parameter of the previous call minus one as an argument, the exit condition is certain to be satisfied at some point. It is essential, when using recursive functions, to ensure that there is an exit condition that will be satisfied within a finite number of iterations, since failure to do so will result in an infinite loop that will eventually result in a stack overflow.


The output from example program 5

The output from example program 5



Function prototypes

So far, in all of the example programs in this section, we have defined functions before they are called from within the main() function. This is because a function cannot be used by a program until it has been declared. We can, however defer the writing of the function body until later in the program, providing we provide a function prototype. The prototype simply determines the function's name, together with its return type and parameter types. The general form of a function prototype is shown below.

type name (parameter_type1, parameter_type2, ...);

The main difference between this and the examples we have seen hitherto is the complete absence of the function body (i.e. the program statements, enclosed in curly braces, that determine what the function actually does). Note that the inclusion of parameter names is optional for the function prototype; only the parameter types need to be declared. The example program below uses two functions, which are declared as function prototypes ahead of the main() function, and defined later in the code.

//Example program 6
#include <stdio.h>
#include <conio.h>

// function prototypes
float calc_area(float);
float calc_circ(float);

void main ()
{
  float rad, circ, area;

  printf("Enter the radius (in metres) for the circle: ");
  scanf("%f", &rad);
  area = calc_area(rad);
  circ = calc_circ(rad);
  printf("\n\nThe circle's area is: %7.2f", area);
  printf(" square metres.");
  printf("\n\nThe circle's circumference is: %7.2f", circ);
  printf(" metres.");
  printf("\n\nPress any key to continue.");
  getch();
}

// function definitions
float calc_area(float rad) {return rad * rad * 3.14;}
float calc_circ(float rad) {return 2 * rad * 3.14;}


The output from example program 6

The output from example program 6



The main() function

A program begins by calling the function main(), which requires no prototype. It can be defined without parameters, e.g.

void main(void) { body... }

Or with the following parameters:

void main(int argc, char *argv[]) { body... }

Note that the parameters do not have to be called argc or argv, this is simply a commonly used convention. The argc parameter is a non-negative integer that indicates the number of arguments passed to the program. The argv parameter is an array of pointers to the strings comprising each of the arguments (the first of which is the name of the program), which are passed to the program by the command-line interpreter.

In the above declarations the main() function returns an integer value which, in hosted environments such as DOS or UNIX, is used to pass the exit status back to the command interpreter. In UNIX, for example, a zero value indicates success, while a non-zero value indicates that an error has occurred.

//Example program 7
#include <stdio.h>
#include <conio.h>

void main(int argc, char *argv[])
{
  int loop;
  if(argc>0) printf("My program name is %s.\n", argv[0]);
  if(argc>1)
  {
    for(loop=1; loop<argc; loop++)
      printf("Parameter #%i is %s.\n", loop, argv[loop]);
  }
  printf("\n\nPress any key to continue.");
  getch();
}


The output from example program 7

The output from example program 7