Chapter 7: Functions
The chapter introduction contrasts conditional and unconditional control transfer and explains why functions are useful for organizing C programs.
After this chapter you will be able to
- Unconditional control transfer. Distinguish a run-time choice between branches from an unconditional transfer of control to another part of the code.
- Reusable factored functionality. Explain how putting functionality in a function supports reuse, avoids repeated code, and reduces the work of changing or compiling that code.
- Function prototype information. Explain what a function prototype specifies, how parentheses are used in declarations and calls, and what void means in a parameter list or return type.
- Prototype guided call conversions. Describe how knowing a prototype lets calling code convert an argument to the expected parameter type and use the declared return type in an expression.
- Return statements and function types. Explain how returns must match a function’s return type, including multiple returns, convertible expressions, and what happens on reaching the end of the body.
- Portable forms of main. Explain why main has special rules and why portable programs use one of its two official forms rather than a platform-specific variation.
- Main return status. Describe how main reports success or failure to the runtime system and what happens if execution reaches its end without an explicit return.
- Exit and main returns. Explain how calling exit relates to returning from main and whether exit returns to its caller.
- Command-line argument strings. Describe what argv contains, how the program obtains values from its strings, and the special meanings of argv[0] and argv[argc].
- Independent recursive call state. Explain that a function is recursive when it calls itself directly or indirectly, and that every call gets its own newly initialized parameters and local variables, even while an earlier call remains active.
- Gcd termination and reduction. Trace how gcd2 returns b when a is zero; otherwise it takes the remainder of b modulo a and returns the result of calling itself with that remainder and a. Explain why the zero check must precede the recursive call.
- Branching Fibonacci recursion cost. Describe how fib returns 1 for n less than 3 and otherwise sums calls with n-1 and n-2; explain why repeated calls in this branching recursion make its execution time exponential.
- Cached Fibonacci performance improvement. Explain that fibCacheRec uses an array of previously computed values so recursive calls compute a value only when it is not cached, reducing execution time from exponential to linear by using storage.
What it builds on
- Conditional execution (chapter 3) · Assumes familiarity with conditional execution.
- Function calls (chapter 2) · Assumes familiarity with calling functions.
- Strings are special (chapter 6) · Earlier function declarations
- Implicit conversions (chapter 5) · Implicit conversions between types
- Function return (chapter 2) · The function return statement
- Division and remainder (chapter 4) · The remainder operation supplies the reduced argument.
- Arrays (chapter 6) · An array can hold previously computed values.
Section by section
Introduction
The chapter introduction contrasts conditional and unconditional control transfer and explains why functions are useful for organizing C programs.
- Unconditional control transfer: Distinguish a run-time choice between branches from an unconditional transfer of control to another part of the code.
Why it matters: This distinction explains how functions fit into the chapter’s discussion of control flow. - Reusable factored functionality: Explain how putting functionality in a function supports reuse, avoids repeated code, and reduces the work of changing or compiling that code.
Why it matters: Factoring repeated work into a function makes programs easier to change and extend. - Function interfaces and computation: Describe what function arguments and return types communicate about a computation, and state the other organizational uses mentioned here.
Why it matters: A clear function interface helps readers see what data a computation takes and produces.
Key terms: conditional execution, unconditional transfer of control, run-time data, functions, code factorization, code reuse, modularity, function arguments, return types, interfaces
7.1 Simple functions
This section explains how function prototypes guide calls and how a function’s returns must match its declared return type.
- Function prototype information: Explain what a function prototype specifies, how parentheses are used in declarations and calls, and what void means in a parameter list or return type.
Why it matters: A prototype tells both the compiler and the caller what a function expects and returns. - Prototype guided call conversions: Describe how knowing a prototype lets calling code convert an argument to the expected parameter type and use the declared return type in an expression.
Why it matters: The declared types affect what happens at a call, not just how the function is documented. - Return statements and function types: Explain how returns must match a function’s return type, including multiple returns, convertible expressions, and what happens on reaching the end of the body.
Why it matters: Consistent returns let callers rely on the value promised by a function’s type. - Variable argument list caution: Describe why functions such as printf need special care when called with varying numbers of arguments.
Why it matters: With variable arguments, the caller bears extra responsibility for keeping the call consistent.
Key terms: prototype, parameter type list, return type, void, arguments, signed int, double, return statement, non-void return type, return value
7.2 main is special
This section explains main’s portable forms, how it reports termination status, and how it receives command-line arguments.
- Portable forms of main: Explain why main has special rules and why portable programs use one of its two official forms rather than a platform-specific variation.
Why it matters: Choosing an official form avoids relying on an interface that another C platform may not provide. - Main return status: Describe how main reports success or failure to the runtime system and what happens if execution reaches its end without an explicit return.
Why it matters: A program’s termination status tells the runtime system whether execution succeeded or failed. - Exit and main returns: Explain how calling exit relates to returning from main and whether exit returns to its caller.
Why it matters: It distinguishes terminating the program from an ordinary function call that resumes afterward. - Command-line argument strings: Describe what argv contains, how the program obtains values from its strings, and the special meanings of argv[0] and argv[argc].
Why it matters: A command-line value must be interpreted from text before the program can use it as, for example, a number.
Key terms: main, entry point, prototype, runtime system, official forms, return value, EXIT_SUCCESS, EXIT_FAILURE, exit, status parameter
7.3 Recursion
The section uses recursive gcd and Fibonacci functions to explain how calls keep separate local state, why recursion needs termination, and how algorithm choice affects performance.
- Independent recursive call state: Explain that a function is recursive when it calls itself directly or indirectly, and that every call gets its own newly initialized parameters and local variables, even while an earlier call remains active.
Why it matters: Separate state lets each recursive call work on different arguments without overwriting its caller's variables. - Gcd termination and reduction: Trace how gcd2 returns b when a is zero; otherwise it takes the remainder of b modulo a and returns the result of calling itself with that remainder and a. Explain why the zero check must precede the recursive call.
Why it matters: A recursive formula becomes a working function only when its calls can reach a case that returns directly. - Gcd precondition wrapper: Explain that gcd2 requires its first argument to be no greater than its second, checks this with assert, and can use a wrapper to ensure the initial call meets that precondition.
Why it matters: A wrapper separates checking the caller's inputs from the invariant maintained by recursive calls. - Branching Fibonacci recursion cost: Describe how fib returns 1 for n less than 3 and otherwise sums calls with n-1 and n-2; explain why repeated calls in this branching recursion make its execution time exponential.
Why it matters: A short recursive definition can still perform many redundant calls. - Cached Fibonacci performance improvement: Explain that fibCacheRec uses an array of previously computed values so recursive calls compute a value only when it is not cached, reducing execution time from exponential to linear by using storage.
Why it matters: Changing the algorithm can improve performance far more than tuning the same repeated computation.
Key terms: recursion, recursive, local variables, function parameters, gcd2, remainder, termination condition, infinite recursion, precondition, assert
7.4 Summary
Where this is used later
- Interface documentation (chapter 10) · Provides context for documenting function interfaces.
- Interfaces (chapter 8) · Library function interfaces rely on declared parameter and return types.
- Pure functions (chapter 10) · Calling functions with parameters and receiving return values.
- (chapter intro) (chapter 11) · The distinction between a function's arguments and variables in its caller.
- A detour: Variadic functions (chapter 17) · That section explains variadic functions in detail.
- Program termination and assertions (chapter 8) · The special role of main and its return.
- String processing and conversion (chapter 8) · string processing builds on recognizing arguments as strings
- Unbounded recursion (chapter 15) · Understanding active recursive calls helps explain unbounded recursion.
Concepts explained in this chapter
- preconditionA platform precondition is a required property or capability of the execution environment that must hold for code to be valid or executable.
- prototypeA function prototype is a declaration that specifies a function's name, return type, and parameter types without providing its body.
- recursionRecursion is a technique in which a computation is defined or performed by calls to the same computation on smaller or simpler inputs.
- Euclid's algorithmEuclid's algorithm is an algorithm for computing the greatest common divisor by repeatedly replacing a pair of integers with a smaller equivalent pair.
- Fibonacci sequenceThe Fibonacci sequence is an integer sequence in which each term after the initial terms is the sum of the preceding two terms.
- cachingA cache is storage that retains previously computed values so they can be reused instead of recomputed.
- encapsulationEncapsulation is the practice of containing implementation details and associated state within a program unit while exposing a callable interface.
- exponential time complexityExponential computation time is runtime whose growth as input size increases is bounded by an exponential function.
- greatest common divisorThe greatest common divisor of two positive integers is the greatest positive integer that divides both of them.
- multiple recursionMultiple recursion is recursion in which one invocation makes more than one recursive call.
- stopping criterionA condition is an expression whose evaluated value controls conditional or iterative execution in C.
- return typeA return type is the type of value that a C function supplies to its caller, or void when it supplies no value.
- exitexit is a C standard-library function that terminates normal program execution and performs implementation-defined cleanup for the process.
- variadic argumentsA variadic parameter is the ellipsis-based parameter portion of a C function declaration that accepts additional arguments.
- functionA function is a named C program unit that can be called to perform an operation and optionally produce a value.
- JavaScript runtimeA runtime environment is the software and system context that provides resources and services while a program executes.
- command-line argumentA command-line argument is a string value supplied to a program when it is invoked from a command-line environment.
- scopeA scope is a region of a C program within which an identifier is visible and its declaration applies.
- assertionassert is a C standard-library assertion macro that evaluates a runtime condition and calls abort when the condition is false.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
