Module 0065: Subroutines (functions) control flow

About this module

Why?

In programming, copy-and-paste is not a good approach to “reusing” code. This is because of several reasons:

The main function

Up to this point, we have been writing C++ code without any functions. In C/C++, code cannot exist outside of a function! A function of the name main is the entry point of code execution of an entire program. The following is the shell of a minimalist main function:

int main() {  // line 1
  return 0;   // line 2
}             // line 3

Here is an explanation of this minimalist shell:

An example of function invocation

Consider the following code:

void f() {    // line 1
}             // line 2

int main() {  // line 3
  f();        // line 4
  f();        // line 5
  return 0;   // line 6
}

There are two functions defined in this program: f and main. Function f has a return type of void, meaning it does not have a return value. Also, note the empty block statement spanning from line 1 to line 2.

The function main is no longer just a shell. Lines 3 and 4 are calls to the function f. The verb “call” is also interchangeable with “invoke”. It is best to take a look at the trace of this code to understand what “calling” or “invoking” means.

line # Comments
3 main is the entry point of an entire program
4 Invoke the function f
1 Execution continues at the entry point of the invoked function
2 The invoked function completes
5 When an invoked function completes, execution continues immediately after the invocation (“returning to the caller”). In this case, there is a second invocation of function f.
1 Execution continues at the entry point of the invoked function
2 The invoked function f completes
6 When an invoked function completes, execution continues immediately after the invocation.
post When main returns, the program execution completes

But how does the computer know where to return to from the function? In other words, in the first invocation, line 2 leads to line 5. However, in the second invocation, line 2 leads to line 6.

Dynamically allocated columns in a trace

We need to modify how code is traced to explain how a called function knows where to return. First, we move the “comments” column to the left. This allows the right side to expand and contract as necessary. Second, we introduce a way to document how columns can be dynamically allocated and deallocated.

The following is a trace that corresponds to the C++ code introduced earlier:

comments line # ret_line_#
  pre  
  3  
invoke f, remember where to return to 4 ret line #
remember to return to line 5 when function f completes   5
continue execute in function f 1  
function f completes, use the right-most column to indicate where to return to, and deallocate it 2 ret line #
now execution returns to the caller, but line 5 is another invocation, so the previously deallocated column is reallocated 5 ret line #
after the second invocation of f, remember to return to line 6   6
continue execution in function f 1  
function f completes, use the right-most column to indicate where to return to, and deallocate it 2 ret line $
now execution returns to the caller 6  
all done post  

To invoke a function

  1. Dynamically allocate the next available column on the right-hand side and label it “ret line #” (return line number).
  2. On the following row, remember the line number to return to on the newly allocated column
  3. Continue execution on the first line of the invoked function

To return when a function completes

  1. Locate the right-most column that is labeled “ret line #”.
  2. Copy-and-paste the return line number to the “line #” column of the next row
  3. Use strike-out style to indicate the “ret line #” column is now deallocated, like “ret line #
  4. Once a column is deallocated, it becomes available again.

How are functions useful?

As illustrated in the example, functions provide a mechanism to execute the same block of code (in the example, the code contained in function f) at multiple points of invocation. This ability reduces, and in many cases eliminates, the copy-and-paste method.

At the same time, the ability to name a block of code and separate it from the context in which this block of code is used helps to reduce the apparent complexity of algorithms. This is similar to the outlining approach when writing an essay. A function serves as a “main point to be expanded” so that the writer (developer) can focus on a few items and how they relate to each other at any time.

What about the variables?

The introduction of functions also adds a new dimension to the concept of variables that will be discussed in a different module.