User-defined functions in C++ let engineers encapsulate a set of instructions into a reusable block. This promotes modularity, reduces code repetition, and improve readability. Engineers can break down complex problems into smaller, manageable units with functions.

#include <iostream>
using namespace std;

// Function prototype
int fibonacci(int);

// Main function
int main(void){
    int n = 20;
    int nth_fib = fibonacci(n);
    cout << "Fibonacci number #" << n << ": ";
    cout << nth_fib << endl;
    return 0;
}

// Function definition
int fibonacci(int n){
    int result;

    if {n==1}{
        result = 1;
    } else if {n==2}{
        result = 1;
    } else {
        int prev[2] = {1, 1};
        for (int i=3; i<=n; i++){
            result = prev[0] + prev[1];
            
            prev[0] = prev[1];
            prev[1] = result;
        }
    }
    return result;
}

Function Anatomy and Prototypes

A function prototype states the return type, the function name, and the types of its parameters, ending with a semicolon. For example, int fibonacci(int); tells the compiler that calls to fibonacci will supply one int and will return an int. The prototype appears near the top of the file, before int main(void), so any calls inside main are type-checked. The function definition repeats the same signature but adds the body between braces. The parameter names in the definition become local variables bound to the argument values at the call site. Order then becomes flexible: as long as the prototype is visible, the definition itself can come later in the file.

Example: Normal Stress

Question

The normal stress in a member under axial load is given by:

\[\sigma = \frac{F}{A}\]

where $F$ is the applied load and $A$ is the cross-sectional area. Write a function named normal_stress that computes $\sigma$, then call that function within main to calculate the normal stress (in psi) on a member with a cross-sectional area of 20 in2 under a load of 500 lbf.

Solution

The following cpp file includes a normal_stress function that is called by main to find the stress.

#include <iostream>
using namespace std;

float normal_stress(float, float);

int main(void){
    float f = 500; // lbf
    float a = 20;  // in^2

    float s = normal_stress(f, a);
    cout << s << " psf" << endl;
}

float normal_stress(float force, float area){
    float sigma = force / area;
    return sigma;
}

This is the result from compiling and running it:

25 psf

Calling, Arguments, and Return Values

Function calls must match the prototype in number and type of arguments. If a function returns a value, the return statement both ends the function and provides the value to the caller. Its expression must be compatible with the function’s declared return type. For functions that perform an action but produce no value, the return type is void, and the function may simply reach the closing brace or execute a bare return;.

Style Considerations

Clear names make it easier to understand what the function does. Keep functions focused on a single task. If a function starts to grow long, or handle unrelated tasks, split it into separate functions. Place prototypes together near the top of the file to give the compiler and the reader quick access to available operations. Finally, compile and test functions incrementally. Verify the function’s behavior with simple, known inputs before rely on it in a larger computation.

Reading Questions

  1. What are two benefits of user-defined functions in C++?
  2. How does a function prototype help the compiler?
  3. What are the key components of a function prototype?
  4. Why is it important to place the prototype before int main(void)?
  5. How does the return statement work in a function?
  6. What are two style guidelines to follow when writing functions?