Skip to the content of the web site.

Lesson 220: Must we first declare and then define functions?

Previous lesson Next lesson


Up to this point, we have always first declared all functions and then subsequently defined each function:

#include <iostream>

// Function declarations
int main();
unsigned int factorial( unsigned int n );

// Function definitions
int main() {
	std::cout << "10! = " << factorial( 10 ) << std::endl;

	return 0;
}

unsigned int factorial( unsigned int n ) {
	return (n <= 1) ? 1 : n*factorial( n - 1 );
}

Even with only one main() function, we have always declared it:

#include <iostream>

// Function declarations
int main();

// Function definitions
int main() {
	std::cout << "Hello world!" << std::endl;

	return 0;
}

The compiler, however, doesn't explicitly require this, only the compiler requires that a function is either declared or defined prior to the first time it is used.

In the Hello world! function, we could leave out the declaration of the function main(), as the definition of the function would implicitly declare it:

#include <iostream>

// Defining main(), which implicitly declares it
int main() {
	std::cout << "Hello world!" << std::endl;

	return 0;
}

In the first example above using the factorial function, the function main() calls factorial(...), so the following is acceptable:

#include <iostream>

// Simultaneously declare and define factorial(...)
unsigned int factorial( unsigned int n ) {
	return (n <= 1) ? 1 : n*factorial( n - 1 );
}

// Simultaneously declare and define main(), which uses factorial(...)
int main() {
	std::cout << "10! = " << factorial( 10 ) << std::endl;

	return 0;
}

So is the following:

#include <iostream>

// Declaring factorial(...)
unsigned int factorial( unsigned int n );

// Simultaneously declare and define main(), which uses factorial(...)
int main() {
	std::cout << "10! = " << factorial( 10 ) << std::endl;

	return 0;
}

// Define factorial(...)
unsigned int factorial( unsigned int n ) {
	return (n <= 1) ? 1 : n*factorial( n - 1 );
}

If you were, however, to try the following, the compiler would reach the statement factorial( 10 ) and not know what to do:

  • Is the 10 a short, int or long, or should it be cast to either a float or double?
  • What is factorial( 10 ) returning so that it can call the proper operator<< to print the returned value?
#include <iostream>

// Simultaneously declare and define main(), which uses factorial(...)
int main() {
	std::cout << "10! = " << factorial( 10 ) << std::endl;

	return 0;
}

// Simultaneously declare and define factorial(...)
//  - too late, though as it has already been called...
unsigned int factorial( unsigned int n ) {
	return (n <= 1) ? 1 : n*factorial( n - 1 );
}

When this author attempts to compile this with g++, the error message is

$ g++ declaration_example.cpp
declaration_example.cpp: In function ‘int main()’:
declaration_example.cpp:5:41: error: ‘factorial’ was not declared in this scope
  std::cout << "10! = " << factorial( 10 ) << std::endl;
                                         ^

Note that the compiler is pointing out exactly where on Line 5 it is confused about with the caret.

Critical warning:

While it is acceptable to mix declarations and definitions throughout source code not meant for production, it is always a good practice to declare all functions prior to defining and using them in any file. It is never acceptable to mix declarations and definitions (or omit declarations) in production code.

Just to remind you, the most expensive component of any software project is maintenance. We are giving you the tools to ensure that your maintenance costs are minimized. This requires more rigor and discipline, and while the benefits will not materialize for some time, it is best to automatically avoid poor software practices from the very start.

Functions calling each other

In some cases, it may actually happen that one function calls another and vice versa, in which case, it is absolutely necessary to declare the second defined functions first. For example, the following code minimizes the relative error on the interval $[0, \pi/4]$ and on the interval $(\pi/4, \pi/2]$, it uses the formulas that $\sin( \pi/2 - x ) = \cos(x)$ = $\cos( \pi/2 - x ) = \sin(x)$:

// If these two were not declared here, we could define neither function
double fast_sin( double x );   // Not necessary but useful for the reader
double fast_cos( double x );   // Absolutely necessary, as 'fast_sin(..)' calls this function

double fast_sin( double x ) {
	double const double PI = 3.1415926535897932;

	if ( x < 0 ) {
		return -fast_sin( -x );
	} else if ( x <= PI/4.0 ) {
		return ((
			-0.15624868420885309*x - 0.0045336113140803213
		)*x + 1.00025904999)*x;
	} else if ( x <= Pi/2.0 ) {
		return fast_cos( PI/2.0 - x );
	} else if ( x <= PI ) {
		return fast_sin( PI - x );
	} else if ( x <= 2.0*PI ) {
		return -fast_sin( x - PI );
	} else {
		return fast_sin( x - 2.0*PI );
	}
}

double fast_cos( double x ) {
	double const double PI = 3.1415926535897932;

	if ( x < 0 ) {
		return fast_cos( -x );
	} else if ( x <= PI/4.0 ) {

		return ((
			0.064056018117415379*x - 0.53045792029826003
		)*x + 0.00418447538833292115)*x + 1.0;
	} else if ( x <= Pi/2.0 ) {
		return fast_sin( PI/2.0 - x );
	} else if ( x <= PI ) {
		return -fast_cos( PI - x );
	} else if ( x <= 2.0*PI ) {
		return -fast_cos( x - PI );
	} else {
		return fast_cos( x - 2.0*PI );
	}
}

Previous lesson Next lesson