Chapter 10: Organization and documentation
This introduction explains why useful documentation distinguishes an interface’s purpose from its implementation details and why code and comments should each contribute meaningful information.
After this chapter you will be able to
- Four levels of explanation. Distinguish what an interface does, what a function is for, how its code is organized, and the manner in which implementation details work.
- Interface and implementation separation. Explain why interface documentation and implementation explanation should be kept distinct, and identify the C file kinds the section associates with that separation.
- Doxygen interface documentation. Explain why C interfaces should be documented thoroughly and describe how Doxygen-style comments document a function.
- Cohesive interface headers. Explain how grouping related interfaces in a header helps users find the code they need, and describe what the example header contains.
- Readable code and comments. Explain why readable C code may need few comments: names and code structure should make what it does clear, while comments explain ideas and prerequisites that are not obvious.
- Obvious control flow. Describe how clear grouping and control statements make control flow apparent, and identify buried jumps and hard-to-read controlling expressions as ways to obscure it.
- Visible macro control flow. Explain why a macro should not hide a return or other surprising control flow, and how an exposed if in its replacement text can cause a dangling else.
- Function call shaped macros. Describe the section's rule that function-like macros should behave syntactically like function calls, including how the do-while(false) wrapper can keep statements together without disrupting surrounding control structures.
- Value Parameters and Shared Objects. Explain that C function parameters receive values computed from arguments, so a function does not directly change an argument object through such a parameter. Describe why using a global variable to let functions share an object is discouraged.
- Pure Function Properties. Describe the two requirements for a pure function and explain why those requirements give an optimizer flexibility in scheduling its execution.
- Returned Results and Copying. Explain why callers must use the returned value of these pure rational functions, and describe the section's qualified performance concern about copying structures.
- Unsequenced interface annotations. Explain what [[__unsequenced__]] tells a compiler that sees only a header, and distinguish functions without pointer parameters or returns from those with them.
- Standard attribute meanings. Describe the purposes of the standard attributes explained here, including how they affect diagnostics or describe a function's behavior.
- Macro-safe attribute spelling. Justify the use of double-underscore attribute forms in header files.
What it builds on
- Naming (chapter 9) · The importance of meaningful names.
- Headers (chapter 8) · Prior discussion of headers.
- Statements (chapter 2) · The use of statements to structure a program.
- Macros (chapter 5) · Introduces macros as textual replacements.
- Simple functions (chapter 7) · Calling functions with parameters and receiving return values.
- The abstract state machine (chapter 5) · The abstract state machine and program state.
- Structures (chapter 6) · Structure types and their members.
- Recursion (chapter 7) · The previously described gcd function.
Section by section
Introduction
This introduction explains why useful documentation distinguishes an interface’s purpose from its implementation details and why code and comments should each contribute meaningful information.
- Useful code explanations: Explain why names and comments should help readers understand code rather than obscure it or repeat what is obvious.
Why it matters: A future reader—including you—needs clues about the code’s meaning, not commentary that adds no information. - Four levels of explanation: Distinguish what an interface does, what a function is for, how its code is organized, and the manner in which implementation details work.
Why it matters: The distinction helps you put the most important information where the most readers will find it. - Interface and implementation separation: Explain why interface documentation and implementation explanation should be kept distinct, and identify the C file kinds the section associates with that separation.
Why it matters: Separating these explanations lets users find the interface information they need without reading implementation details.
Key terms: magic constants, comments, function name, variable name, what, what for, how, in which manner, function interfaces, interface comments
10.1 Interface documentation
The section explains how to document C interfaces with Doxygen-style comments and organize related interfaces in cohesive header files.
- Doxygen interface documentation: Explain why C interfaces should be documented thoroughly and describe how Doxygen-style comments document a function.
Why it matters: Consistent interface comments make code easier for its users to understand and can be turned into documentation. - Cohesive interface headers: Explain how grouping related interfaces in a header helps users find the code they need, and describe what the example header contains.
Why it matters: A cohesive header gives users one place to find the interfaces for a data type.
Key terms: interface, Doxygen, @param, @a, @brief, strong semantic connections, header file, interfaces, include guards
10.2 Implementation
The section explains how clear implementation makes code readable, control flow obvious, and non-obvious ideas worth documenting.
- Readable code and comments: Explain why readable C code may need few comments: names and code structure should make what it does clear, while comments explain ideas and prerequisites that are not obvious.
Why it matters: Clear code lets readers understand the ordinary steps without relying on comments to narrate them. - Obvious control flow: Describe how clear grouping and control statements make control flow apparent, and identify buried jumps and hard-to-read controlling expressions as ways to obscure it.
Why it matters: A reader should be able to follow where execution goes without untangling dense expressions or hidden exits. - Pure function readability: Explain the property of a pure function stated here and why it helps humans and compilers understand it.
Why it matters: This explains why a function's limited interaction with the rest of the program can improve readability and implementation efficiency.
Key terms: Implement literally, descriptive text, naming entities, control flow, compound statements, buried jumps, flyspeck expressions, pure functions, interface
10.2.1 Macros
This section explains how to use macros without hiding control flow or making function-like invocations behave unexpectedly.
- Visible macro control flow: Explain why a macro should not hide a return or other surprising control flow, and how an exposed if in its replacement text can cause a dangling else.
Why it matters: Code that looks straightforward at the call site can behave differently after macro replacement. - Function call shaped macros: Describe the section's rule that function-like macros should behave syntactically like function calls, including how the do-while(false) wrapper can keep statements together without disrupting surrounding control structures.
Why it matters: A macro that fits the surrounding syntax is less likely to break otherwise ordinary-looking code. - Textual replacement pitfalls: Explain the listed syntactic and evaluation hazards of function-like macros: trailing semicolons, commas, expressions that bind unexpectedly, and parameters used more than once.
Why it matters: Checking the replacement text helps catch behavior that is not apparent from a macro invocation.
Key terms: macros, control flow, dangling else, ERRORCHECK, function-like macros, do-while(false), compound statement, trailing semicolons, comma operators, continuable expressions
10.2.2 Pure functions
This section explains pure, value-based functions and uses rational-number arithmetic to show both their usefulness and their limits.
- Value Parameters and Shared Objects: Explain that C function parameters receive values computed from arguments, so a function does not directly change an argument object through such a parameter. Describe why using a global variable to let functions share an object is discouraged.
Why it matters: This distinction explains why the section's functions return new values rather than changing their callers' objects. - Pure Function Properties: Describe the two requirements for a pure function and explain why those requirements give an optimizer flexibility in scheduling its execution.
Why it matters: Purity makes a small function's effect easy to describe and gives optimizers more freedom. - Rational Representation and Normalization: Explain why the example's rational numbers need normalization and describe how the product and sum functions use rational representations.
Why it matters: The example shows how small value-returning functions can work together while maintaining a useful representation. - Returned Results and Copying: Explain why callers must use the returned value of these pure rational functions, and describe the section's qualified performance concern about copying structures.
Why it matters: Using the return value is necessary for the example's computations to affect a caller's variable.
Key terms: passed by value, parameters, global variables, pure function, return value, abstract state machine, rat, numerator, denominator, sign
10.2.3 Attributes
This section explains how C23 attributes annotate code for compilers and tools, how to check attribute support, and how to avoid macro interference in headers.
- Unsequenced interface annotations: Explain what [[__unsequenced__]] tells a compiler that sees only a header, and distinguish functions without pointer parameters or returns from those with them.
Why it matters: An interface annotation can give the compiler useful information even when the function body is unavailable. - Standard attribute meanings: Describe the purposes of the standard attributes explained here, including how they affect diagnostics or describe a function's behavior.
Why it matters: These annotations help compilers distinguish intended code from uses worth diagnosing. - Prefixed implementation attributes: Describe the form and purpose of prefixed attributes, using the format attribute as the section's example.
Why it matters: Prefixed attributes let implementations supply targeted checks beyond the standard attributes. - Attribute support testing: Explain how code can conditionally use an attribute when support varies between implementations.
Why it matters: Checking support avoids unconditionally using an attribute on a platform that may not implement it. - Macro-safe attribute spelling: Justify the use of double-underscore attribute forms in header files.
Why it matters: Macro-safe spelling protects a header's annotations from names defined by code that includes it.
Key terms: [[__unsequenced__]], pure, pointer parameters, pointer returns, deprecated, fallthrough, maybe_unused, nodiscard, noreturn, prefixed attributes
10.3 Summary
Where this is used later
- Pointers and structures (chapter 11) · The distinction between an interface and its implementation.
- Inline functions (chapter 16) · The organization of implementation code.
- How function-like macros work (chapter 17) · Provides a control-flow hazard to keep in mind when examining how function-like macros work.
- (chapter intro) (chapter 17) · Introduced function-like macros and their potential to obscure code.
- Unsequenced and reproducible attributes (chapter 16) · Develops the properties of unsequenced and reproducible attributes in more detail.
- A complete example with varying array size (chapter 13) · Provides examples of deprecated features and nodiscard on storage allocation.
Concepts explained in this chapter
- pure functionA pure function is a function whose interactions with the rest of a program occur only through its interface and whose behavior has no other observable effects.
- attributeAn attribute is supplemental implementation or semantic information attached to a C program construct.
- global variableA global variable is a C object with program-wide or file-wide lifetime and visibility determined by its linkage and scope.
- normalizationNormalization is the process of choosing a canonical representation of a rational number by reducing common factors from its numerator and denominator.
- rational numberA rational number is a number that can be expressed as the quotient of two integers with a nonzero denominator.
- rational typeThe rational type is a book-specific C structure type representing a rational number with a numerator, denominator, and sign.
- __has_c_attribute__has_c_attribute is a C preprocessor feature-test operator that reports whether an attribute is supported.
- double-underscore attribute formA double-underscore attribute form is an attribute spelling whose identifier is prefixed and postfixed with double underscores to avoid collisions with application-defined macros.
- format attributeA format attribute supplies a compiler with the positions of a formatted-output specification and its variable arguments in a function call.
- prefixed attributeA prefixed attribute is an implementation-defined attribute whose name combines an implementation prefix, the :: token, and a feature suffix.
- standard attributeA standard attribute is an attribute defined by the C standard with a specified meaning and syntax.
- do-while(false) trickA do-while macro wrapper is a macro pattern that encloses multiple statements in a single statement-like construct using a do loop whose condition is false.
- function-like macroA function-like macro is a C preprocessor macro invoked with parentheses and arguments that expands its invocation into replacement source text.
- macroA macro definition is a preprocessing directive that associates a macro name and optional parameters with replacement text.
- commentA comment is source text that the C compiler ignores and that can document a program.
- contractA library interface is the specified set of declarations and calling conventions through which software accesses library functionality.
- header fileA header file is a source file containing declarations, macro definitions, and related information intended to be included by other source files.
- translation unitA translation unit is the source-code unit produced after preprocessing a C source file and its included contents for compilation.
- control flowControl flow is the order in which execution proceeds among a program's operations and functions.
- maybe_unused attributeThe maybe_unused attribute suppresses diagnostics for declarations or definitions whose identifiers might not be used.
- nodiscard attributeThe nodiscard attribute is a C23 attribute that recommends issuing a diagnostic when a function return value is ignored.
- reproducible attributeThe reproducible attribute is a C23 function attribute declaring that a call produces consistent results and effects for given arguments while permitting limited temporary state changes.
- unsequenced attributeThe unsequenced attribute is a C function attribute indicating that calls to the function can be treated as unsequenced with respect to relevant evaluation and side-effect rules.
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.
