C++ Functions, Recursion & Storage Classes Interview Questions

C++ Functions, Recursion, and Storage Classes

Basic Level (15 Questions)

What is a function in C++ and what are its key components?
A function in C++ is a named block of code that performs a specific task and can be called multiple times from different parts of a program. Key components of a function:
  • Function declaration/prototype: Specifies function name, return type, and parameters
  • Function definition: Contains the actual implementation of the function
  • Function name: Identifier used to call the function
  • Return type: Data type of value returned (void if no return)
  • Parameters/arguments: Input values passed to the function
  • Function body: Code statements that perform the task
  • Return statement: Returns value to calling code (optional for void functions)
Functions promote code reusability, modularity, and maintainability.
What are the different types of functions in C++?
Classification based on various criteria:
ClassificationTypesDescription
By return typeValue-returning, VoidReturns a value or performs action only
By parametersParameterized, ParameterlessAccepts arguments or no arguments
By definitionBuilt-in, User-definedLibrary functions or programmer-defined
By scopeMember, Non-memberClass member or standalone function
By call mechanismCall by value, Call by reference, Call by pointerHow arguments are passed
Special typesInline, Virtual, Pure virtual, Friend, Lambda (C++11)Special function categories
By storage classStatic, Extern, Auto, RegisterStorage duration and linkage
What is the difference between function declaration and function definition?
AspectFunction Declaration (Prototype)Function Definition
PurposeInforms compiler about function existenceProvides actual implementation
SyntaxReturn type, name, parameters (types only)Return type, name, parameters, body
MemoryNo memory allocationAllocates memory for code
LocationHeader files or top of source filesSource files (.cpp files)
Multiple declarationsAllowed (must be consistent)Only one definition allowed
Exampleint add(int a, int b);int add(int a, int b) { return a + b; }
RequiredWhen calling before definitionRequired for function to exist
Key points:
  • Declaration can appear multiple times, definition only once
  • Declaration ends with semicolon, definition has body in braces
  • Parameter names are optional in declaration but required in definition
  • Default arguments can be specified in either declaration or definition, but not both
What are the different parameter passing mechanisms in C++?
MechanismSyntaxEffectUse Case
Pass by valuefunc(int x)Creates copy, original unchangedWhen input shouldn't be modified
Pass by referencefunc(int &x)Works on original variableWhen modifying input or avoiding copy
Pass by const referencefunc(const int &x)Read-only access, no copyLarge objects, read-only access
Pass by pointerfunc(int *x)Works on original via addressC compatibility, optional parameters
Pass by const pointerfunc(const int *x)Read-only via pointerC compatibility, read-only
Pass by rvalue reference (C++11)func(int &&x)For temporary objectsMove semantics, optimization
Best practices:
  • Use pass by value for primitive types and small objects
  • Use pass by const reference for large objects and read-only access
  • Use pass by reference when modification is needed
  • Use pass by pointer for optional parameters or C compatibility
  • Use rvalue references for move semantics optimization
What are default arguments in functions and what are the rules?
Default arguments allow parameters to have default values if not provided by caller. Rules for default arguments:
  • Default arguments must be specified only in function declaration, not definition
  • Default values must be constant expressions or literals
  • Default arguments must be at the end of parameter list (trailing arguments)
  • Once a parameter has default value, all subsequent parameters must also have defaults
  • Default arguments can be overridden by providing explicit values
  • Default arguments are evaluated at call time, not declaration time
  • In class member functions, default arguments can be specified in declaration or definition, but not both
Advantages:
  • Provides flexibility in function calls
  • Reduces number of overloaded functions
  • Makes APIs more user-friendly
Disadvantages:
  • Can make function signatures harder to read
  • Default values become part of interface - changing them breaks binary compatibility
  • Can cause confusion if overused
What is function overloading and what are its requirements?
Function overloading allows multiple functions with the same name but different parameters. Requirements for overloading:
  • Functions must have the same name
  • Functions must differ in:
    • Number of parameters, OR
    • Type of parameters, OR
    • Sequence/order of parameters
  • Return type alone is NOT sufficient for overloading
  • Constness can differentiate overloads for member functions
How overloading works:
  • Compiler performs name mangling to create unique names
  • Overload resolution happens at compile time
  • Compiler chooses best match based on argument types
Benefits:
  • Provides intuitive function names for similar operations
  • Reduces need for different function names
  • Makes APIs cleaner and more readable
Limitations:
  • Can cause ambiguity if overloads are too similar
  • Return type cannot be used for differentiation
  • Automatic type conversions can cause unexpected matches
What is recursion and what are its characteristics?
Recursion is a programming technique where a function calls itself directly or indirectly. Characteristics of recursion:
  • Base case: Condition that stops recursion (prevents infinite recursion)
  • Recursive case: Part where function calls itself with modified parameters
  • Progress toward base case: Each recursive call should move closer to base case
  • Stack usage: Each call creates new stack frame (can cause stack overflow)
  • Termination: Must eventually reach base case
Types of recursion:
  • Direct recursion: Function calls itself directly
  • Indirect recursion: Function calls another function which calls the original
  • Tail recursion: Recursive call is last operation (can be optimized)
  • Non-tail recursion: Operations after recursive call
  • Mutual recursion: Two or more functions call each other
Common recursive algorithms:
  • Factorial calculation
  • Fibonacci sequence
  • Tower of Hanoi
  • Binary tree traversals
  • Divide and conquer algorithms (merge sort, quick sort)
  • Backtracking algorithms
What is tail recursion and why is it important?
Tail recursion occurs when the recursive call is the last operation in the function. Characteristics of tail recursion:
  • No computation is performed after the recursive call
  • Recursive call is in "tail position" (last operation before return)
  • Function returns the value of the recursive call directly
Importance and optimization:
  • Tail Call Optimization (TCO): Compiler can optimize tail recursion to iteration
  • No stack growth: Optimized version uses constant stack space
  • Prevents stack overflow: For deep recursion
  • Better performance: Eliminates function call overhead
Converting to tail recursion:
  • Add accumulator parameter to carry result
  • Perform computation before recursive call
  • Pass accumulated value to next call
  • Base case returns accumulator
Compiler support:
  • Not all compilers perform TCO
  • GCC and Clang have good TCO support with optimization flags
  • C++ standard doesn't require TCO, but allows it
  • For guaranteed optimization, use iteration instead
What are the advantages and disadvantages of recursion?
AspectAdvantagesDisadvantages
ReadabilityElegant for recursive problems (trees, graphs)Can be confusing for simple problems
Code simplicityOften simpler than iterative solutionsHidden complexity in function calls
Problem solvingNatural fit for divide-and-conquerNot intuitive for all problems
Memory usage-High stack usage, risk of overflow
Performance-Function call overhead, slower
Debugging-Harder to debug (multiple stack frames)
MaintenanceCan be elegant and maintainableCan become spaghetti if overused
When to use recursion:
  • Problems with recursive structure (trees, graphs, nested data)
  • Divide and conquer algorithms
  • Backtracking problems
  • When recursive solution is significantly simpler
  • When depth is limited and predictable
When to avoid recursion:
  • Simple iterative solutions exist
  • Deep recursion expected (risk of stack overflow)
  • Performance-critical code
  • Real-time systems with limited stack
  • When tail recursion optimization not guaranteed
What are storage classes in C++ and what do they control?
Storage classes determine the scope, lifetime, and visibility of variables and functions. Aspects controlled by storage classes:
  • Scope: Where variable/function is accessible
  • Lifetime: How long variable exists in memory
  • Linkage: Whether variable/function can be accessed from other files
  • Memory location: Where variable is stored (stack, heap, data segment)
  • Initial value: Default initialization
C++ storage classes:
  • auto (C++11 meaning differs from C)
  • register (deprecated in C++17)
  • static
  • extern
  • mutable (for class members)
  • thread_local (C++11)
Default storage classes:
  • Local variables: auto (automatic storage)
  • Global variables and functions: extern (external linkage)
What is the auto storage class in C++ (pre and post C++11)?
Pre C++11 (traditional meaning):
  • auto indicated automatic storage duration
  • Variables created on stack when block entered, destroyed when block exited
  • Default for local variables (rarely explicitly used)
  • Example: auto int x = 5; (redundant, as int x = 5; means the same)
C++11 and later (new meaning):
  • auto is a type specifier for type deduction
  • Compiler deduces type from initializer
  • Must have initializer (can't declare auto variable without initialization)
  • Used to simplify complex type declarations
  • Works with references (auto&), pointers (auto*), const (const auto)
  • Common uses:
    • Iterators: auto it = vec.begin();
    • Lambda expressions: auto lambda = [](){ };
    • Templates: auto result = function<T>(args);
    • Range-based for loops: for(auto& element : container)
Key differences:
AspectPre C++11C++11+
PurposeStorage duration specifierType deduction specifier
Usageauto int x;auto x = expression;
InitializationOptionalRequired
CommonalityRarely used explicitlyCommonly used
CompatibilityC compatibilityC++ specific feature
What is the static storage class and its different uses?
The static storage class has different meanings based on context. Static local variables:
  • Initialized only once (first time function is called)
  • Retains value between function calls
  • Stored in data segment (not stack)
  • Lifetime: entire program execution
  • Scope: local to function
  • Default initialization to zero
  • Use case: Function that needs to remember state between calls
Static global variables/functions:
  • Internal linkage (not accessible from other files)
  • File scope only
  • Avoids naming conflicts across files
  • Use case: Helper functions/variables used only in one file
Static class members:
  • Shared by all objects of the class
  • Exists independently of any object
  • Must be defined outside class (except const integral types)
  • Can be accessed using class name (ClassName::staticMember)
  • Use case: Class-wide counters, shared resources
Static member functions:
  • Can be called without object
  • Can only access static members
  • No this pointer
  • Use case: Utility functions related to class
What is the extern storage class and when is it used?
The extern storage class is used to declare variables/functions that are defined elsewhere. Purpose of extern:
  • External linkage (accessible from other files)
  • Declaration without definition
  • Tells compiler "this exists somewhere else"
  • Used for sharing variables/functions across multiple files
Usage scenarios:
  • Sharing global variables:
    • File1.cpp: int globalVar = 42; (definition)
    • File2.cpp: extern int globalVar; (declaration)
  • Sharing functions:
    • Default for functions (usually omitted)
    • Explicit: extern void function();
  • C linkage:
    • extern "C" void c_function();
    • Prevents C++ name mangling
    • Used for C library compatibility
Rules and characteristics:
  • extern declaration doesn't allocate memory
  • Can have multiple extern declarations but only one definition
  • Definition must occur somewhere in program
  • Default for global variables and functions (but explicit extern clarifies intent)
  • extern variables can be initialized only at definition, not declaration
Best practices:
  • Use header files for extern declarations
  • Minimize use of global variables (use alternatives when possible)
  • Use namespaces to avoid naming conflicts
  • Prefer static or anonymous namespaces for file-local variables
What are register and mutable storage classes?
Register storage class:
  • register suggests compiler to store variable in CPU register
  • Hint only - compiler may ignore
  • Cannot take address of register variable (& operator not allowed)
  • Deprecated in C++17 (register keyword removed)
  • Modern compilers better at register allocation than programmers
  • Historical use: Performance optimization for frequently accessed variables
Mutable storage class:
  • mutable applies to class data members only
  • Allows modification even in const objects
  • Useful for:
    • Caching/memoization (storing computed results)
    • Reference counting
    • Debugging/tracing information
    • Thread synchronization primitives (mutexes)
  • Doesn't affect external const-ness (logical const-ness vs physical const-ness)
  • Should be used judiciously (breaks const correctness)
Comparison:
Aspectregistermutable
Applicable toLocal variablesClass data members
PurposePerformance hintAllow modification in const objects
Current statusDeprecated (C++17)Active feature
Common useRarely used, compiler optimizes betterCaching, mutexes in const methods
Can take addressNoYes
What is thread_local storage class (C++11)?
thread_local (C++11) specifies that a variable has thread storage duration. Characteristics:
  • Each thread has its own copy of the variable
  • Created when thread starts, destroyed when thread ends
  • Can be combined with static or extern
  • Useful for thread-specific data (no need for thread-local storage APIs)
Usage examples:
  • Thread-local random number generators
  • Thread-specific caches
  • Error numbers (errno-like variables)
  • Thread identification or context
  • Performance counters per thread
Combinations with other storage classes:
  • thread_local static: Thread-local with static initialization
  • thread_local extern: Declaration of thread-local defined elsewhere
Implementation considerations:
  • Compiler/runtime manages thread-local storage
  • May have performance overhead compared to automatic variables
  • Portable alternative to platform-specific TLS APIs
  • Dynamic initialization happens on first use per thread
Comparison with alternatives:
StorageLifetimePer threadUse case
autoBlockNoLocal variables
staticProgramNo (shared)Global/function state
thread_localThreadYesThread-specific data

Tricky Level (10 Questions)

What is inline functions and how do they differ from macros?
Inline functions:
  • inline is a request to compiler to insert function code at call site
  • Compiler may ignore inline request (especially for large functions)
  • Reduces function call overhead
  • Definitions must be available in every translation unit (usually in headers)
  • Follows all C++ rules (type checking, scope, etc.)
Comparison with macros:
AspectInline FunctionsMacros (#define)
Type safetyYes (full type checking)No (text substitution)
ScopeFollows C++ scope rulesNo scope (global text replacement)
DebuggingEasy (appears as function)Hard (disappears after preprocessing)
EvaluationArguments evaluated onceArguments may be evaluated multiple times
SyntaxFull C++ syntaxPreprocessor syntax
Error messagesClear, at point of callObscure, at point of expansion
When expandedCompile timePreprocessing time
Modern C++ alternatives:
  • Compiler auto-inlining (modern compilers inline small functions automatically)
  • constexpr functions (C++11): Evaluated at compile time when possible
  • Template metaprogramming
  • Lambda expressions for small, local operations
Best practices:
  • Use inline for small, frequently called functions
  • Place inline function definitions in header files
  • Let compiler decide - modern compilers are good at inlining decisions
  • Avoid macros for function-like behavior
  • Use constexpr when compile-time evaluation is needed
What is function template and how does it relate to function overloading?
Function templates:
  • Blueprint for generating functions for different types
  • Uses template parameters (typename T or class T)
  • Compiler generates concrete functions (instantiations) as needed
  • Provides generic programming
Comparison with function overloading:
AspectFunction OverloadingFunction Templates
PurposeDifferent implementations for different typesSame algorithm for different types
CodeMultiple function definitionsSingle template definition
Type handlingExplicit types for each overloadGeneric type T
CompilationAll overloads compiledOnly used instantiations compiled
FlexibilityLimited to predefined typesWorks with any compatible type
SpecializationCan have completely different implementationsCan have template specializations
Template specialization:
  • Special implementation for specific types
  • Can optimize or handle special cases
  • Example: template<> void swap<MyClass>(MyClass&, MyClass&)
Combining both:
  • Templates and overloading can work together
  • Non-template functions are preferred over template instantiations
  • More specialized templates are preferred over more general ones
  • Overload resolution considers both templates and non-template functions
What are lambda expressions (C++11) and their characteristics?
Lambda expressions (C++11) are anonymous function objects. Basic syntax:
  • [capture](parameters) -> return_type { body }
  • Can be stored in auto variables or std::function
  • Can be invoked immediately or stored for later use
Capture clauses:
  • []: Capture nothing
  • [&]: Capture all by reference
  • [=]: Capture all by value (deprecated in C++20)
  • [var]: Capture specific variable by value
  • [&var]: Capture specific variable by reference
  • [this]: Capture class this pointer
  • [=, &var]: Capture all by value, but var by reference
Characteristics:
  • Can have mutable specifier (allows modifying captured by-value variables)
  • Can have noexcept specifier
  • Can have constexpr (C++17)
  • Can have trailing return type (auto or explicit)
  • Can be generic with auto parameters (C++14)
  • Can be templated (C++20)
Storage and lifetime:
  • Lambdas are objects with overloaded operator()
  • Can be stored, copied, passed as arguments
  • Capture determines what variables are available
  • By-value captures create copies (be careful with large objects)
  • By-reference captures require captured variables to outlive lambda
Use cases:
  • STL algorithms (sort, find_if, transform, etc.)
  • Callback functions
  • One-time simple operations
  • Creating function objects on the fly
  • Multithreading tasks
What are the memory locations for different storage classes?
Storage ClassMemory LocationLifetimeInitialization
auto (local)StackBlockGarbage (unless initialized)
registerCPU Register (hint)BlockGarbage (unless initialized)
static localData segmentProgramZero (if not explicitly initialized)
static globalData segmentProgramZero (if not explicitly initialized)
externData segmentProgramZero (if not explicitly initialized)
thread_localThread-local storageThreadZero (if static) or dynamic
dynamic (new)HeapUntil deleteGarbage (unless initialized)
Memory segments explained:
  • Stack: Automatic variables, function parameters, return addresses
  • Heap: Dynamically allocated memory (new/delete, malloc/free)
  • Data segment:
    • .data: Initialized global/static variables
    • .bss: Uninitialized or zero-initialized global/static variables
    • Read-only data: String literals, const variables
  • Text/Code segment: Executable code
  • Thread-local storage: Per-thread data
Performance implications:
  • Stack: Fast allocation/deallocation, limited size
  • Heap: Slower, flexible size, fragmentation possible
  • Data segment: Fast access, fixed size at compile/link time
  • Registers: Fastest, very limited
What are the best practices for functions, recursion, and storage classes in C++?
Function best practices:
  • Keep functions small and focused (Single Responsibility Principle)
  • Use descriptive function names that indicate purpose
  • Limit number of parameters (consider struct for many parameters)
  • Prefer pass by const reference for large objects
  • Use noexcept for functions that don't throw
  • Document preconditions, postconditions, and side effects
  • Avoid functions with too many responsibilities
Recursion best practices:
  • Always define a clear base case
  • Ensure progress toward base case
  • Consider stack depth and potential overflow
  • Use tail recursion when possible
  • Consider iterative alternatives for performance-critical code
  • Use memoization to optimize repeated calculations
  • Document recursion depth expectations
Storage class best practices:
  • Minimize use of global variables (prefer local scope)
  • Use static for file-local functions/variables
  • Use const for variables that shouldn't change
  • Use thread_local for thread-specific data in multithreaded programs
  • Avoid register (deprecated, compiler optimizes better)
  • Use mutable only when necessary and document why
  • Prefer automatic variables (stack) for short-lived data
  • Use dynamic allocation (heap) only when necessary
  • Follow RAII (Resource Acquisition Is Initialization) for resource management
Modern C++ recommendations:
  • Use auto for type deduction when type is obvious or verbose
  • Use lambda expressions for small, local operations
  • Use constexpr for compile-time evaluation when possible
  • Use noexcept for exception specifications
  • Use move semantics (&&) for efficient parameter passing
  • Consider std::function for type-erased function objects
  • Use templates for generic programming
  • Prefer algorithms and lambdas over manual loops
What is tail recursion?
Recursive call is the final operation in the function. Compilers may optimize to iteration (tail call optimization), but not guaranteed in C++.
Why specify `noexcept` on move operations?
STL containers use move only when moves are noexcept; otherwise they copy for strong exception safety.
What is RVO (return value optimization)?
Compiler elides copy/move when returning a local object by value—mandatory in some C++17 cases (guaranteed RVO).
Difference between function pointer and `std::function`?
Function pointers are lightweight but fixed signature; std::function type-erases any callable with matching signature (heap may be used).
What is ADL (argument-dependent lookup)?
Unqualified calls also search namespaces associated with argument types—how swap and stream operators are found for user types.
Note: These questions cover core interview topics. Pair with the tutorial and MCQ quiz for this section. This page lists 15 basic and 10 tricky questions—use the tutorial and MCQ links above and below.