Learncpp: 01 Functions and Files

Function Return Values

  1. The main() function must return an int.
  2. Explicit function calls to main() should be avoided.
  3. Most compilers detect missing return values, but in complex cases, they might fail to do so.
  4. Nested functions are not allowed in C++.

Function Parameters and Arguments

  1. A parameter is a variable in a function’s header. It behaves like a regular variable but is initialized with a value provided by the caller.
  2. An argument is the actual value passed to a function when calling it.
  3. When a function is called:
    • Parameters are created as variables.
    • Argument values are copied into parameters (pass by value).
    • These parameters are called value parameters.
  4. Unreferenced parameters exist but are not used in the function body.
  5. Unnamed parameters are parameters without a name.

Local Scope

  1. Local variables are declared inside a function and only accessible within that function.
  2. Creation & Destruction:
    • Created when execution reaches their definition.
    • Destroyed when they go out of scope (end of {} block).
  3. Scope & Lifetime:
    • Scope defines where a variable is accessible.
    • Lifetime lasts from creation to destruction.
    • Temporary objects (e.g., function return values) exist briefly and are often optimized away in modern compilers.

The One Definition Rule (ODR)

C++ enforces one definition per entity across a program. ODR consists of three key rules:

  1. Within a file:
    • Each function, variable, type, or template must have only one definition in a given scope.
    • Definitions in separate scopes (e.g., different namespaces) are fine.
  2. Within a program:
    • Each function or variable must have only one definition across files.
    • If multiple files define the same function/variable, it causes linker errors.
    • Local variables and functions marked static or inline are exceptions.
  3. Multiple identical definitions are allowed for:
    • Types, templates, inline functions, and inline variables.
    • As long as each definition is identical across translation units.

Namespaces

  1. A scope region ensures identifiers remain distinct from names declared elsewhere.
  2. A namespace introduces a new scope for identifiers, preventing conflicts.
  3. If an identifier isn’t inside a class, function, or namespace, it belongs to the global namespace.
  4. The :: symbol is the scope resolution operator.
  5. A using directive allows access to namespace members without a prefix:

    using namespace std; // Avoid in large projects
    

The Preprocessor

  1. After preprocessing, a file becomes a translation unit.
  2. Preprocessor directives:
    • Start with # and end with a newline (and not with a semicolon).
    • Example: #include, #define, #if.
  3. Macros (#define):
    • Define object-like macros:

      #define IDENTIFIER
      #define IDENTIFIER substitution_text
      
    • Macros replace occurrences of IDENTIFIER with substitution_text.
    • Function-like macros are discouraged.
  4. Conditional Compilation:
      #include <iostream>
      #define PRINT_JOE
    
      int main()
      {
      #ifdef PRINT_JOE
          std::cout << "Joe\n"; // Compiled
      #endif
    
      #ifdef PRINT_BOB
          std::cout << "Bob\n"; // Skipped
      #endif
    
          return 0;
      }
    
  5. #if 0 for commenting out blocks:

    #include <iostream>
    
    int main()
    {
        std::cout << "Joe\n";
    
    #if 0 // Disable this section
        std::cout << "Bob\n";
        std::cout << "Steve\n";
    #endif
    
        return 0;
    }
    
    • Useful for disabling code blocks as nesting multiline (/* */) comments is not allowed.
  6. Scope of #define: Preprocessor directives execute before compilation, top to bottom.

Header Files

  1. A source file should include its paired header:
    • Helps catch errors at compile time instead of link time.
    • If a function’s return type differs in declaration and definition, the compiler throws an error.
    • Overloaded functions (same name, different parameters) are not caught at this stage.
    • Some necessary definitions may be in the header, making it mandatory to include.
  2. Angled Brackets (<>) vs. Double Quotes (""):
    • #include <file> : Searches system directories (used for standard headers).
    • #include "file" : Searches current directory first, then system directories.
  3. Why doesn’t <iostream> have .h?
    • Old C++ standard used <iostream.h>, which defined everything in the global namespace.
    • The ANSI committee moved all standard library identifiers into the std namespace.
    • New-style headers omit .h to differentiate from old headers.
    Header Type Naming Convention Example Namespace
    C++ Specific (New) <xxx> <iostream> std
    C Compatibility (New) <cxxx> <cstddef> std (required), global (optional)
    C++ Specific (Old) <xxx.h> <iostream.h> Global
    C Compatibility (Old) <xxx.h> <stddef.h> Global (required), std (optional)
  4. Including headers from other directories:

    g++ -o main -I./source/includes main.cpp
    



    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Learncpp: 02 Scope, Duration, and Linkage
  • Linux Device Drivers: 01 Introduction