C and C++ Interview Questions and Answers

C and C++ interview questions show up together in embedded firmware, operating systems, game engines, trading infrastructure, and performance-critical backend teams. Interviewers rarely test only one language in isolation—they want you to explain C memory and pointers, then defend C++ ownership with RAII, choose the right STL container, and survive a live coding round without leaking or invoking undefined behavior.

Below are 45+ C and C++ interview questions with clear technical explanations for developers preparing in 2026. Each section builds from C foundations through modern C++ and coding scenarios, with strong answer samples on technical questions you can say aloud in the room. Pair this guide with operating system interview questions for processes, virtual memory, and Linux kernel context, OOP interview questions for cross-language object-oriented design, Java interview questions part 1 for managed-memory contrast, shell scripting interviews for gdb and sanitizer workflows on Linux, and Git interviews for review and CI habits.

NOTE
Prep tip: For each technical card, read What interviewers are testing aloud, walk through the body, then close with A strong answer is as your ~20-second spoken line. Master pointer diagrams and RAII first, then practice coding rounds that mix C utilities with C++ ownership.

Interview context and how to prepare

What combined C and C++ interviews test

Hiring loops that list C and C++ usually need engineers who can work on legacy C APIs, modern C++ services, or bare-metal plus host tooling.

Skill band C side C++ side
Memory malloc/free, pointers, layout RAII, smart pointers, move
Abstraction Structs, function pointers Classes, templates, STL
Correctness UB, bounds, const/volatile Exceptions, noexcept, casts
Performance Cache, no hidden alloc vector, algorithms, profiling
Coding String/array utilities STL + ownership in one problem
Role type Typical emphasis
Embedded / firmware C fundamentals, hardware interfaces, deterministic resource use; C++ varies by platform
Systems / infrastructure C interoperability plus modern C++ ownership, containers, concurrency
Games / low-latency systems Performance-oriented C++, memory layout, allocation, profiling

A common interview loop may look like:

Round Duration Focus
Phone screen 30 min Projects, domains, safety-critical experience
Language fundamentals 45–60 min Pointers, OOP, STL, UB
Coding 60–90 min C string ops, C++ container problem, debug snippet
Systems / design 45 min Module boundaries, threading, API design
Debugging story 30 min Segfault, leak, race you fixed

Follow-up questions often go deep on pointer arithmetic, virtual destructors, smart pointer choice, and iterator invalidation—plan to explain each with a small code example.

Prep plan for C and C++ interviews

Week Focus Deliverable
1–2 C pointers, arrays, malloc, strings Linked list + valgrind/ASan clean
3 C structs, padding, function pointers Draw struct layout; qsort comparator
4 C++ classes, RAII, Rule of Zero Wrap FILE* or socket handle
5 Smart pointers, move, four casts Refactor raw owner to unique_ptr
6 STL containers + complexities Explain when vector beats list
7 Threads, mutexes, atomics Fix a race; run TSan
8 Mock coding + STAR incidents Three timed problems out loud

Compile with -Wall -Wextra -Wpedantic and run AddressSanitizer and UndefinedBehaviorSanitizer on practice code.


C vs C++ — key differences interviewers expect you to list?

What interviewers are testing: Whether you understand how C and C++ differ on memory management, abstraction, and interoperability—and can choose the right language for platform constraints rather than treating C++ as only "C with classes."

Feature C C++
Paradigm Procedural Multi-paradigm (OOP, generic, procedural)
Memory Manual malloc/free Constructors, destructors, smart pointers
Abstraction Structs + function pointers Classes, templates, namespaces
Polymorphism Function pointers Virtual functions, templates
Standard library C standard library STL + C library
Compatibility Substantial C interoperability; not a strict superset of modern C (extern "C" for C linkage at ABI boundaries)

C and C++ share substantial syntax and interoperability, but C++ is not a strict superset of modern C. C APIs can be consumed from C++ using compatible headers and extern "C" linkage where required.

C++ adds type safety, RAII, and zero-cost abstractions when used well—not automatic safety if you still use raw owning pointers everywhere.

A strong answer is:

C is primarily procedural and gives explicit low-level control over memory and APIs. C++ adds classes, templates, RAII, the STL, and stronger abstraction tools while retaining low-level control. I choose based on platform constraints, interoperability, and how much abstraction the system can support.


C fundamentals: memory, pointers, and types

Explain stack vs heap storage in C and C++.

What interviewers are testing: Whether you understand object lifetime and storage duration, especially why returning a pointer to a local object is invalid while dynamically allocated storage can survive the function that allocated it.

Storage Lifetime Allocation
Automatic Block scope Compiler-managed; commonly implemented on the process/thread stack
Heap (dynamic) Until explicitly released malloc/free or new/delete
Static Program lifetime BSS/data segments
c
void demo(void) {
    int x = 10;              /* stack */
    static int counter;      /* static */
    int *p = malloc(sizeof *p); /* heap */
    free(p);
}

The important interview distinction is lifetime, not physical memory layout. A local automatic object is destroyed when its lifetime ends, while dynamically allocated storage remains allocated until it is explicitly released or managed by an owning RAII object. For how the OS maps virtual memory beyond language storage duration, see the Linux memory management overview.

Returning address of local x is undefined behavior. C++ adds RAII so stack objects run destructors automatically.

A strong answer is:

Automatic local objects normally live until their scope ends. Dynamically allocated storage remains valid until it is released, so it can outlive the allocating function. The important thing is to track lifetime and ownership and never return a pointer to an expired local object.

How do pointers, dereferencing, and pointer arithmetic work in C?

What interviewers are testing: Whether you diagram pointer arithmetic and UB traps like dangling pointers.

c
int arr[] = {10, 20, 30};
int *p = arr;
printf("%d %d\n", *p, *(p + 2));
Rule Detail
* Dereference — read/write through address
& Address-of
p + n Advances by n * sizeof(type)
p[i] Same as *(p + i)

Valid arithmetic only within same array object (or one past end for comparison, not dereference).

A strong answer is:

Pointers are typed addresses—I use arithmetic only inside known bounds and treat one-past-end as compare-only, not dereference.

What are common malloc and free mistakes?

What interviewers are testing: Whether you can reason about ownership—who allocated the memory, who frees it, whether every path releases it exactly once, and how you would detect leaks, double frees, or use-after-free bugs.

Mistake Result
Memory leak No free
Double free Undefined behavior; may abort or corrupt allocator state
Use-after-free UB / crash
Wrong size malloc(n * sizeof *p) not sizeof(p)
free non-heap pointer UB

Always check malloc for NULL. Setting a pointer to NULL after free can prevent accidental reuse through that variable, but it does not fix other aliases that still point to the freed object.

A strong answer is:

One allocation pairs with exactly one free on every path—I use sizeof on the pointed-to type, check NULL, and verify with AddressSanitizer.

What do const and volatile mean in C?

What interviewers are testing: Whether you understand that const controls modification through an interface while volatile affects observable memory accesses—and, importantly, whether you know volatile is not a replacement for atomics or mutexes.

const — prevents modification through that const-qualified access path. Casting away const does not make an originally const object safely writable.

volatile — tells the compiler that accesses to the object are observable side effects. It is used for cases such as memory-mapped hardware and some signal-related objects; it does not provide atomicity or thread synchronization.

c
const int limit = 100;
volatile uint32_t *reg = (volatile uint32_t *)0x40001000;

C++ adds constexpr for compile-time constants. C23 introduced constexpr objects, but C's constexpr capabilities are much narrower than modern C++ constexpr evaluation and functions.

A strong answer is:

const expresses that an interface will not modify an object through that access path; volatile is for observable special-memory accesses and is not thread synchronization.

What is undefined behavior and why does it matter in C/C++?

What interviewers are testing: Whether you understand that undefined behavior gives the language no required result, can identify common UB such as invalid lifetime/bounds access, and do not mistake "it worked in my test" for correctness.

UB means the standard imposes no requirements—anything may happen. Compilers assume UB never occurs and optimize accordingly.

Common sources:

  • Signed integer overflow
  • Out-of-bounds access
  • Uninitialized reads
  • Invalid pointer arithmetic / dereference
  • Data races (C++ memory model)

Code that "works in debug" may break in -O2 release after UB-based optimizations.

A strong answer is:

UB means the language gives no required outcome, so optimizers may assume it never occurs. I avoid it with correct lifetime and bounds reasoning, checked arithmetic where needed, compiler warnings, and ASan/UBSan.


C strings, structs, and procedural patterns

How are C strings stored and copied safely?

What interviewers are testing: Whether you remember that a C string is only a byte sequence terminated by \0, and whether you can copy into fixed buffers without overflow or accidentally losing null termination.

Null-terminated char sequences. Length is O(n) via strlen.

c
#include <stdio.h>
#include <string.h>

int main(void) {
    char dest[16];
    snprintf(dest, sizeof dest, "%s", "hello");
    printf("%zu %s\n", strlen(dest), dest);
    return 0;
}

Compiled and run, this prints 5 hello on one line.

Prefer APIs where truncation and termination semantics are explicit, such as snprintf, or perform explicit length checks/copies. Do not present strncpy as automatically safe—it may leave the destination without a terminating \0 and zero-fills remaining space.

A strong answer is:

C strings end at the first null—I always pass buffer sizes to bounded APIs and treat string length as explicit in APIs I design.

What is structure padding and alignment?

What interviewers are testing: Whether you understand why sizeof(struct) may be larger than the sum of its members, how alignment affects layout, and why changing member order can change size or ABI.

Alignment requirements can cause the implementation to insert padding between members and at the end of a structure.

c
struct S { char c; double d; }; /* often 16 bytes total */

Use offsetof to inspect layout. Group members with similar alignment requirements and inspect the actual layout before optimizing; reordering members can reduce padding but may affect ABI. #pragma pack changes layout and can create alignment/ABI issues. Use packed layouts only when an external binary ABI or hardware interface specifically requires them and the platform behavior is understood. For portable network formats, prefer explicit serialization instead.

A strong answer is:

Padding is inserted to satisfy member alignment requirements, so sizeof(struct) can exceed the sum of its fields. I verify layout with sizeof and offsetof, and I avoid depending on raw struct layout for portable file or network formats.

What are function pointers used for in C?

What interviewers are testing: Whether you understand functions can be passed as values in C and can recognize callback, comparator, dispatch-table, and driver-interface patterns.

Callbacks, dispatch tables, qsort/bsearch comparators, driver interfaces.

c
#include <stdlib.h>

int cmp_int(const void *a, const void *b) {
    int x = *(const int *)a, y = *(const int *)b;
    return (x > y) - (x < y);
}

C++ often replaces C-style function pointers with std::function, lambdas, or templates—but C APIs and embedded code still use raw function pointers daily.

A strong answer is:

Function pointers decouple call sites from implementations—I use them for comparators and plugin tables with clear, documented signatures.

How do C and C++ interoperate with extern "C"?

What interviewers are testing: Whether you understand that extern "C" changes C++ language linkage so C-compatible functions can be linked across C/C++ boundaries; it does not turn C++ code into C.

C++ name mangling encodes overloads into linker symbols. C expects unmangled names.

cpp
#ifdef __cplusplus
extern "C" {
#endif

void c_api_init(void);

#ifdef __cplusplus
}
#endif

Expose stable C ABI from C++ libraries for other languages and older C callers. Headers shared between .c and .cpp files need extern "C" guards.

A strong answer is:

extern "C" gives C linkage for shared APIs—I wrap C++ implementations behind C headers when the ABI must stay stable.

What is array-to-pointer decay?

What interviewers are testing: Whether you understand why array size information is usually lost when an array is passed to a function, and why sizeof(arr) behaves differently before and after that decay.

In most expressions, an array name decays to pointer to first element. Arrays do not decay in every context—for example, applying sizeof to an actual array gives the array's full size.

Context sizeof(arr)
In scope of declared array Total array size
Function parameter int arr[] Pointer size (decayed)

Always pass explicit length with array parameters in C.

A strong answer is:

Arrays decay to pointers in function calls—I never infer array length from a bare pointer parameter.


C++ object model and OOP

What is RAII and why is it central to C++?

What interviewers are testing: Whether you connect RAII and destructors to real ownership boundaries.

Resource Acquisition Is Initialization — acquire in constructor, release in destructor.

Destructors run when scope ends, including during exception unwind, so cleanup is tied to object lifetime—not scattered free calls.

cpp
class Guard {
    std::lock_guard<std::mutex> lock_;
public:
    Guard(std::mutex& m) : lock_(m) {}
}; // unlocks automatically

RAII applies to memory, files, locks, sockets, and GPU handles.

A strong answer is:

RAII makes cleanup automatic and exception-safe—I wrap every resource in a type whose destructor releases it.

Static vs dynamic polymorphism in C++?

What interviewers are testing: Whether you can distinguish compile-time polymorphism from runtime polymorphism and explain the trade-off: templates when types are known at compile time versus virtual dispatch when runtime substitution is required.

Kind Mechanism When resolved
Static Templates, overload resolution Compile time
Dynamic virtual functions, vtable Runtime
cpp
template<typename T>
T max_t(T a, T b) { return (a < b) ? b : a; }  // static

struct Base { virtual void f(); };
struct Derived : Base { void f() override; };

Templates enable zero-cost generics; virtual enables runtime substitution with vtable cost.

A strong answer is:

Templates give compile-time polymorphism without vtable cost; virtual gives runtime substitution when types are not known until run time.

Why do polymorphic base classes often need virtual destructors?

What interviewers are testing: Whether you know what happens when a derived object is destroyed through a base pointer and why a polymorphic ownership interface normally requires a virtual destructor.

Deleting a derived object through a base pointer when the base destructor is non-virtual results in undefined behavior—not merely a skipped derived destructor.

A polymorphic base does not require a virtual destructor if objects are never destroyed through the base interface; some designs instead make the non-virtual destructor protected to prevent such deletion.

cpp
struct Base {
    virtual ~Base() = default;
};
struct Derived : Base {
    std::vector<int> data;
};
std::unique_ptr<Base> p = std::make_unique<Derived>();

unique_ptr with virtual destructor is the modern default pattern.

A strong answer is:

If I delete derived objects through a base pointer, the base destructor must be virtual—otherwise the behavior is undefined. I use unique_ptr with a virtual destructor as the modern default.

Explain Rule of Zero, Three, and Five.

What interviewers are testing: Whether you understand how custom resource ownership affects copy, move, and destruction semantics—and whether your default design is Rule of Zero rather than manually implementing special members unnecessarily.

Rule Guidance
Rule of Zero Use RAII members (string, vector, smart ptr)—define no special members
Rule of Three If a class directly manages a resource and needs a custom destructor, it usually also needs an explicit policy for copying—copy constructor and copy assignment, either implemented correctly or deleted
Rule of Five In modern C++, also consider move constructor and move assignment

If you manage a raw char* manually, you own the full five or delete copying.

A strong answer is:

Rule of Zero is my default; if I hold raw resources I implement or delete the full copy/move/destructor set explicitly.

Shallow copy vs deep copy?

What interviewers are testing: Whether you understand that a shallow copy duplicates pointer values—not necessarily ownership—and when memberwise copying leaves two objects referring to the same underlying resource.

Shallow Deep
Pointers Copy pointer value Duplicate pointed-to data
Risk Double free, aliasing Extra cost, correct ownership

The compiler-generated copy performs memberwise copying. Whether that is effectively shallow or deep depends on the members—a raw pointer copies the pointer value, while std::string and std::vector copy their contents. Raw owning pointers need explicit deep copy or deleted copy.

A strong answer is:

A shallow copy copies pointer values, so two objects may refer to the same underlying resource. A deep copy duplicates the resource. In modern C++, I prefer value types and Rule of Zero so ownership semantics come from the member types.

Pointers vs references in C++?

What interviewers are testing: Whether you distinguish nullability, reseating, syntax, ownership, and API design—not whether references are simply "safer pointers."

Pointer Reference
Null Can be nullptr Must bind to a valid object
Reseating Can point to different objects Cannot rebind after initialization
Syntax *p, p-> r, member access directly
Typical use Optional access, reseating, ownership APIs Required non-null parameters

References are aliases, not owners. Use pointers when nullability, reseating, or optional access matters; use references for required parameters that must exist for the call's duration.

cpp
void process(const std::string& name);  // required input
void maybe_log(const Error* err);       // optional input

A strong answer is:

References must bind to a valid object and cannot be reseated; pointers can be null and can change target. I use references for required non-null parameters and pointers when nullability, reseating, or optional access matters—not because references are magically safer.


Smart pointers, new/delete, and move semantics

unique_ptr vs shared_ptr vs weak_ptr?

What interviewers are testing: Whether you choose a smart pointer from the ownership model: one owner → unique_ptr, genuinely shared lifetime → shared_ptr, and non-owning observation of shared state → weak_ptr.

Type Ownership Overhead
unique_ptr Exclusive Usually zero runtime ownership overhead with default stateless deleter; size can grow with stateful/custom deleters
shared_ptr Shared refcount Control block + atomics
weak_ptr Non-owning Breaks cycles
cpp
auto u = std::make_unique<int>(42);
auto s = std::make_shared<Resource>();
std::weak_ptr<Resource> w = s;

Prefer make_unique and make_shared; make_shared can typically allocate the object and control block together.

A strong answer is:

unique_ptr by default; shared_ptr when lifetime is genuinely shared; weak_ptr to observe without keeping objects alive or creating cycles.

new/delete vs malloc/free — when use each?

What interviewers are testing: Whether you know that malloc allocates raw storage while new combines allocation with object construction—and whether you avoid mixing the corresponding allocation/deallocation families.

malloc/free new/delete
Language C (+ C++ compatible) C++ only
Construction No constructors Calls ctor/dtor
Type Returns void* Typed pointer
Failure Returns NULL Throws bad_alloc (default)

In modern C++, direct new/delete is uncommon in application code because containers and RAII ownership types usually manage allocation for you. malloc/free still appears at C interfaces and in code deliberately managing raw storage. Never pair malloc with delete or new with free.

A strong answer is:

malloc is for C-style untyped bytes; new runs constructors—I avoid both for owning code and use make_unique and vectors instead.

What are move semantics and why mark moves noexcept?

What interviewers are testing: Whether you understand that moving transfers/repurposes resources instead of copying them, that std::move only enables move semantics by producing an rvalue expression, and why noexcept matters to containers.

Move transfers resources from expiring objects instead of copying.

cpp
std::vector<int> build() {
    std::vector<int> v{1, 2, 3};
    return v; // NRVO or move
}

std::move casts to rvalue. Move ctor should leave source valid but unspecified, often empty.

noexcept on move — marking a move operation noexcept when it truly cannot throw allows standard containers such as vector to prefer moving elements during reallocation while preserving their exception guarantees.

A strong answer is:

Move semantics transfer resource ownership from an object that is no longer needed instead of performing an expensive copy. I mark move operations noexcept where valid so standard containers can use them efficiently during reallocation.

Explain static_cast, dynamic_cast, const_cast, and reinterpret_cast.

What interviewers are testing: Whether you choose casts intentionally rather than using C-style casts, especially whether you understand checked polymorphic downcasts and the risks around const_cast and reinterpret_cast.

Cast Safe use
static_cast Numeric conversions, up/down casts with programmer guarantee
dynamic_cast Polymorphic downcast; failed pointer downcast returns nullptr; failed reference downcast throws std::bad_cast
const_cast Add/remove const only (not immutability hack on truly read-only memory)
reinterpret_cast Low-level conversions with very limited portability guarantees; aliasing, alignment, and object-lifetime rules still apply

Prefer C++-style casts over C (T)x—they are searchable and categorized.

A strong answer is:

I use static_cast for compile-time conversions, dynamic_cast for safe runtime downcasts, and treat reinterpret_cast as last-resort code where I understand aliasing and lifetime constraints.

What is operator overloading in C++?

What interviewers are testing: Whether you know that operators are functions with language-defined syntax and whether you preserve the meaning users naturally expect from that operator.

Define operator+, operator[], operator<<, etc. on user types for natural syntax.

cpp
struct Vec2 {
    double x, y;
    Vec2 operator+(const Vec2& o) const { return {x + o.x, y + o.y}; }
};

Rules:

  • Cannot overload ::, .*, ?:, sizeof
  • At least one user-defined type in overload
  • Maintain intuitive semantics—don't surprise readers

A strong answer is:

Operator overloading is syntactic sugar for functions—I overload only when it reads clearly and matches mathematical or domain expectations.


STL, templates, and algorithms

Compare vector, list, deque, map, and unordered_map.

What interviewers are testing: Whether you choose containers from access pattern, ordering, iterator stability, memory layout, and complexity—not simply memorize that one container has O(1) operations.

Container Random access Insert/erase characteristic Lookup
vector O(1) Back amortized O(1); middle O(n)
list O(n) traversal O(1) at known iterator
deque O(1) O(1) ends; O(n) middle
map O(log n) keyed insert O(log n)
unordered_map O(1) average keyed insert O(1) average

Default: vector until profiling proves otherwise—cache locality dominates many workloads. vector is often the default because contiguous storage gives good locality; an O(1) linked-list insertion does not help if you first need O(n) traversal to find the position.

A strong answer is:

vector for contiguous data; unordered_map for average O(1) hash lookup; map when I need sorted iteration.

When do vector iterators invalidate?

What interviewers are testing: Whether you know the documented invalidation rules for vector reallocation, insertion, and erasure—not just that reallocation is dangerous.

Operation Iterator/reference validity
push_back causing reallocation All invalidated
push_back without reallocation Existing element references/iterators remain valid; old end() does not
insert causing reallocation All invalidated
insert without reallocation At/after insertion point invalidated
erase At/after erased position invalidated

Classic bug: for (auto it = v.begin(); it != v.end(); ++it) { if (*it == x) v.erase(it); } — use erase-remove idiom or careful loop.

A strong answer is:

A vector reallocation invalidates every iterator and reference. Without reallocation, insertion or erasure can still invalidate positions at or after the modification point. I design loops around the operation's documented invalidation rules rather than keeping stale iterators.

What are C++ templates at interview level?

What interviewers are testing: Whether you understand templates as compile-time generic code, why their definitions are normally visible where they are instantiated, and how concepts can constrain valid template arguments.

Templates generate type-safe generic code at compile time.

cpp
template<typename T>
const T& min_ref(const T& a, const T& b) {
    return (a < b) ? a : b;
}

C++20 concepts constrain templates: template<std::integral T>.

Definitions usually live in headers—each translation unit instantiates used specializations.

A strong answer is:

Templates are compile-time generics—I constrain them with concepts in C++20 and keep definitions visible to the compiler.

Name important STL algorithms and complexities.

What interviewers are testing: Whether you know common standard algorithms, their preconditions and complexity, and prefer expressing an operation with an existing algorithm rather than immediately writing a manual loop.

Algorithm Typical use Complexity
std::sort General sort O(n log n)
std::lower_bound Binary search on sorted range O(log n) comparisons; best paired with random-access iterators when you expect binary-search-style traversal cost
std::find Linear search O(n)
std::accumulate Sum / fold O(n)
std::transform Map O(n)

Prefer algorithms over hand-rolled loops—they document intent and enable optimizer patterns.

A strong answer is:

I reach for sort, lower_bound, and find before writing custom loops—knowing complexities matches interviewer expectations.


Concurrency, exceptions, and modern C++

How do you prevent data races with std::mutex?

What interviewers are testing: Whether you understand a mutex prevents races only when every conflicting access to shared mutable state follows the same synchronization discipline.

cpp
std::mutex m;
int counter = 0;

void inc() {
    std::lock_guard<std::mutex> lock(m);
    ++counter;
}
Primitive Role
lock_guard RAII lock
unique_lock Deferred, try_lock, condition_variable
scoped_lock RAII locking of one or more mutexes; uses deadlock-avoidance when locking multiple mutexes

Run ThreadSanitizer (-fsanitize=thread) on concurrent tests.

A strong answer is:

A mutex does not magically protect a variable; every conflicting access must use the synchronization protocol. I protect shared mutable state with mutexes or atomics and verify with thread sanitizer—not ad hoc volatile flags.

What are std::atomic and memory orders?

What interviewers are testing: Whether you know that atomics solve data races only for accesses performed atomically and whether you understand that memory ordering controls visibility/ordering between threads—not just whether an increment itself is atomic.

Atomics give defined concurrent read/write without mutex for simple counters/flags.

Order Typical idea
memory_order_relaxed Atomicity without inter-thread ordering
memory_order_acquire / release Publish data in one thread and synchronize its observation in another
memory_order_seq_cst Strongest commonly used ordering; default

C11 _Atomic mirrors for C-only subsystems.

A strong answer is:

atomics define legal concurrent access—I start with seq_cst and relax only with measurement and a written memory-order rationale.

How do C++ exceptions interact with RAII?

What interviewers are testing: Whether you understand stack unwinding and can separate automatic resource cleanup through RAII from the basic, strong, and no-throw guarantees of an operation.

When an exception throws, stack unwinds and destructors run for automatic objects—RAII releases locks and memory.

Guarantee Meaning
Basic Invariants preserved and no resource leaks
Strong Operation either succeeds or leaves observable state unchanged
No-throw Operation guarantees not to throw

Embedded projects may compile with -fno-exceptions—know team policy.

A strong answer is:

When an exception propagates, automatic objects are destroyed during stack unwinding, so RAII releases resources such as locks and memory automatically. The overall exception guarantee still depends on how the operation updates program state.

What are C++ lambdas and captures?

What interviewers are testing: Whether you understand what is stored in the closure for value versus reference capture and can reason about lifetime when a lambda escapes the current scope.

cpp
int factor = 10;
auto f = [factor](int x) { return factor * x; };       // by value
auto g = [&factor](int x) { return factor * x; };      // by reference

mutable allows modifying value-captured copies. Generic lambdas (auto params) act like templates.

Reference captures can dangle if the lambda outlives the referenced object, so capture lifetime matters whenever the lambda is stored or executed asynchronously.

Used with std::sort, std::thread, and STL algorithms.

A strong answer is:

Lambdas are local function objects—I capture by value unless I need to mutate external state safely with explicit reference discipline.


Coding interview problems (C and C++)

Coding: Reverse a C string in place.

What interviewers are testing: Whether you can manipulate a writable C string using pointers without allocating another buffer, while handling the null terminator, empty strings, and invalid input safely.

c
#include <stdio.h>

void reverse(char *s) {
    if (!s) return;
    char *a = s, *b = s;
    while (*b) ++b;
    if (b > s) --b;
    while (a < b) {
        char t = *a;
        *a++ = *b;
        *b-- = t;
    }
}

int main(void) {
    char s[] = "hello";
    reverse(s);
    printf("%s\n", s);
    return 0;
}

Running prints olleh. Must use writable array—not a string literal.

A strong answer is:

I first find the end of the writable C string, then swap characters from both ends until the pointers meet. It runs in O(n) time and O(1) extra space, and I handle null and empty input before dereferencing.

Coding: Two-sum with a hash map (C++ STL).

What interviewers are testing: Whether you can improve a brute-force O(n²) search to expected O(n) time using a hash table, explain the O(n) space trade-off, and return indices without accidentally matching an element with itself.

Find two indices whose values sum to target—classic STL + complexity question.

This example assumes target - nums[i] fits in int and the number of elements fits an int index.

cpp
#include <iostream>
#include <unordered_map>
#include <vector>

std::pair<int, int> two_sum(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> seen;
    for (int i = 0; i < (int)nums.size(); ++i) {
        int need = target - nums[i];
        auto it = seen.find(need);
        if (it != seen.end()) return {it->second, i};
        seen[nums[i]] = i;
    }
    return {-1, -1};
}

int main() {
    std::vector<int> v{2, 7, 11, 15};
    auto [a, b] = two_sum(v, 9);
    std::cout << a << " " << b << "\n";
}

Compile with g++ -std=c++17 -Wall -Wextra — prints 0 1.

A strong answer is:

A brute-force solution is O(n²). I can reduce that to expected O(n) time with O(n) extra space by storing values I've already seen in an unordered_map and checking whether the required complement already exists.

Coding: Detect a cycle in a linked list (C).

What interviewers are testing: Whether you recognize Floyd's slow/fast pointer technique, can explain why a cycle eventually makes the pointers meet, and safely advance pointers without dereferencing null.

Floyd's tortoise-and-hare — O(n) time, O(1) space.

c
#include <stdbool.h>
#include <stddef.h>

struct Node { int val; struct Node *next; };

bool has_cycle(struct Node *head) {
    struct Node *slow = head, *fast = head;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) return true;
    }
    return false;
}

Check fast and fast->next before advancing to avoid null dereference.

A strong answer is:

I use Floyd's slow-and-fast pointer algorithm: slow advances one node and fast advances two, so they eventually meet if a cycle exists. It runs in O(n) time and O(1) space, with null checks before advancing fast.

Coding: Wrap a C FILE* in minimal RAII (C++).

What interviewers are testing: Whether you can turn a raw C resource into an ownership type: acquire once, release once, prevent accidental copying, and decide explicitly whether ownership may move.

cpp
class File {
    FILE* f_{nullptr};
public:
    explicit File(const char* path, const char* mode) : f_(fopen(path, mode)) {}
    ~File() { if (f_) fclose(f_); }
    File(const File&) = delete;
    File& operator=(const File&) = delete;
    FILE* get() const { return f_; }
};

This minimal wrapper is intentionally non-copyable. A production owner would also define move semantics if the handle must be transferable, and should expose or handle fopen() failure explicitly.

Interview checks destructor cleanup, deleted copy, and explicit constructor.

A strong answer is:

I wrap the FILE* in an owning RAII type whose destructor closes it, disable copying so two objects cannot close the same handle, and add move semantics if ownership needs to be transferred.


Debugging, design, and senior scenarios

How do you debug segfaults and memory corruption on Linux?

What interviewers are testing: Whether you debug from evidence in an ordered way—reproduce, obtain a stack trace or sanitizer report, identify the invalid access or lifetime event, fix the root cause, and rerun the failing case.

Step Tool
Reproduce minimally
gdb backtrace bt, core files
AddressSanitizer -fsanitize=address
UBSan -fsanitize=undefined
Valgrind/Memcheck Invalid memory accesses and leaks; useful when sanitizer instrumentation is unavailable

For hands-on examples with GDB, Valgrind, sanitizers, and core dumps, see how to find memory leaks in Linux.

A strong answer is:

I reproduce first, inspect the crash/core with gdb, then use ASan/UBSan or TSan depending on symptoms. I use evidence to distinguish bounds errors, lifetime bugs, races, library issues, and environment failures.

What is the PIMPL idiom?

What interviewers are testing: Whether you understand why moving private implementation details out of a public header reduces dependencies and can help library ABI stability, and whether you recognize the indirection/allocation trade-off.

Pointer to implementation — hide details behind unique_ptr<Impl> in header, define Impl in .cpp.

With std::unique_ptr<Impl>, the owning class's destructor is commonly declared in the header and defined in the .cpp file after Impl is complete.

Benefits: reduced compile-time coupling, implementation hiding, and the ability to preserve the public class layout across some implementation changes.

Trade-off: extra indirection and allocation.

A strong answer is:

PIMPL hides implementation details and reduces compile coupling—I use it for stable library APIs, not every small class.

Embedded C vs application C++ — interview focus shift?

What interviewers are testing: Whether you adapt language choices to platform constraints instead of assuming every C or C++ environment has the same runtime, allocation, exception, and debugging capabilities.

Embedded / constrained systems General application C++
Heap May be restricted, avoided, or replaced by pools Dynamic allocation commonly available
RTTI / exceptions May be disabled by project/platform policy Commonly available
Libraries Often platform-constrained Broader standard/library ecosystem
Tools JTAG, scope profilers, sanitizers

Many engineers face both in one product—RTOS in C, configuration UI in C++.

A strong answer is:

Embedded interviews usually emphasize deterministic resource use, memory limits, MMIO, interrupts, and platform constraints. Application C++ interviews put more emphasis on RAII, STL design, concurrency, exceptions, and profiling. I adapt the language features I use to the platform rather than assuming one environment.

Scenario: Hot loop in C++ — what do you optimize first?

What interviewers are testing: Whether you investigate in an ordered way instead of guessing—measure first, then address algorithm, allocation, and locality before micro-optimizations.

When CPU is the suspect, confirm the loop is actually hot on the host before rewriting code—for example with the diagnostic path in Linux high CPU usage.

  1. Measure and profile — prove this loop is actually the bottleneck.
  2. Algorithm/data structure — fix unnecessary complexity first.
  3. Allocation — remove avoidable allocations or reserve where appropriate.
  4. Memory locality — inspect data layout/cache behavior.
  5. Branches/vectorization/micro-optimization — only after measurement identifies them.

A strong answer is:

I measure first and confirm the loop is actually hot. Then I fix algorithmic complexity and allocation or locality problems before attempting instruction-level or SIMD optimizations, and I benchmark again after each change.


Final preparation

Coding: Implement strcmp-style logic (C).

What interviewers are testing: Whether you can walk two null-terminated strings safely, stop at the first difference, and reproduce strcmp's negative/zero/positive result semantics without relying on signed char.

c
#include <stdio.h>

int my_cmp(const char *a, const char *b) {
    while (*a && (*a == *b)) { ++a; ++b; }
    return (unsigned char)*a - (unsigned char)*b;
}

int main(void) {
    printf("%d %d\n", my_cmp("abc", "abd"), my_cmp("abc", "abc"));
    return 0;
}

Prints negative integer and 0. Cast to unsigned char for correct signed char platforms.

A strong answer is:

I compare as unsigned char, matching the semantics expected from strcmp and avoiding signed-char surprises.

What is enum class in C++ vs C enum?

What interviewers are testing: Whether you understand why scoped enums improve type safety: enumerator names stay in the enum's scope and values do not implicitly convert to integers.

C enum — names leak into enclosing scope; underlying type implementation-defined.

enum class (C++11) — scoped enumerators, optional fixed underlying type, no implicit conversion to int.

cpp
enum class Color : uint8_t { Red, Green, Blue };

Safer for new C++ code than unscoped enums.

A strong answer is:

enum class prevents implicit int conversions and name pollution—I use it for type-safe constants in modern C++.

What is copy elision / RVO?

What interviewers are testing: Whether you distinguish guaranteed C++17 copy elision from optional NRVO and know why return std::move(local) can inhibit NRVO.

C++17 guarantees elision in certain prvalue constructions. NRVO for a named local remains an optimization, though widely implemented.

cpp
std::vector<int> make() {
    return std::vector<int>{1, 2, 3}; // C++17 guaranteed copy elision
}

std::vector<int> make_named() {
    std::vector<int> v{1, 2, 3};
    return v; // NRVO is permitted but not guaranteed
}

Do not write return std::move(local); just to force a move; it can inhibit NRVO.

A strong answer is:

C++17 guarantees elision in certain prvalue constructions; NRVO for named locals is still an optimization. I return by value and let the compiler elide, not std::move locals blindly.

What are friend functions and friend classes?

What interviewers are testing: Whether you know friendship grants selected non-members or classes access to private/protected state, and whether you use it as a deliberate design relationship rather than a shortcut around encapsulation.

Friends grant private access to non-members—use sparingly for operators and tightly coupled collaborators.

cpp
class Matrix {
    friend std::ostream& operator<<(std::ostream& os, const Matrix& m);
};

Breaks encapsulation boundary deliberately—prefer public interface when possible.

A strong answer is:

Friends expose private access for operators or paired classes—I use them narrowly, not to bypass design.


Modern C and C++ (2026)

What are C++20/23 concepts and ranges at interview level?

What interviewers are testing: Whether you understand what modern C++ concepts solve—expressing template requirements clearly—and what ranges improve—working with algorithms and views without manually passing iterator pairs everywhere.

Concepts constrain templates with readable requirements:

cpp
template<std::integral T>
T add(T a, T b) { return a + b; }

Ranges (std::ranges) provide composable algorithms and views with less iterator boilerplate than raw begin/end loops.

A strong answer is:

Concepts make generic code self-documenting; ranges replace many manual iterator loops—I reach for them in new C++20+ code when the team standard allows.

What are std::span and std::string_view?

What interviewers are testing: Whether you understand that views avoid ownership and copying but therefore depend entirely on the lifetime of the referenced storage.

Non-owning views over contiguous data or strings:

Type Use
std::string_view Read-only string slice without copying
std::span<T> Non-owning view over array/vector buffer

They reduce copies at API boundaries but do not extend lifetime—do not return a view to expired storage. A string_view referring to a temporary std::string can dangle just like a span referring to a destroyed vector.

A strong answer is:

span and string_view are non-owning views, so they can make APIs accept existing contiguous data without copying. Their main risk is lifetime—the referenced storage must remain valid for the entire use of the view.

What are std::jthread and stop_token?

What interviewers are testing: Whether you understand cooperative thread shutdown through jthread and stop_token rather than assuming destruction forcibly terminates a worker.

std::jthread (C++20) joins automatically at scope end and supports cooperative cancellation via std::stop_token.

If a std::jthread is still joinable when destroyed, its destructor requests stop and then joins. The worker must cooperate by observing its stop token; a stop request does not forcibly terminate the thread.

A strong answer is:

jthread manages thread lifetime with RAII: destruction requests cooperative stop and joins the thread. The worker must check the stop_token; the request itself does not forcibly kill execution.

Name a few useful C23 additions.

What interviewers are testing: Whether you are aware of important modern C additions and, more importantly, whether you avoid assuming that similarly named C and C++ features have identical semantics or compiler support.

Feature Note
nullptr Standard null pointer constant
constexpr objects Much narrower than C++ constexpr functions
typeof / typeof_unqual Type inference from expressions
auto in object definitions Type inference for variables
#embed Embed binary data at compile time
Fixed underlying enum types Explicit enum storage
true / false keywords Standard boolean literals
Portability Embedded targets may lag C23 support

C23 does not make C a subset of C++—interoperability still needs extern "C" and careful ABI boundaries.

A strong answer is:

C23 adds features such as nullptr, constexpr objects, typeof, type inference with auto, and #embed. I still treat C and C++ as distinct languages and check compiler/platform support before depending on newer features.


C and C++ interview rehearsal checklist

Before your interview, make sure you can:

  • Draw stack vs heap; explain pointer size on 64-bit
  • malloc/free rules + sanitizer workflow
  • virtual destructor and vtable sketch
  • Pointers vs references — nullability, reseating, API choice
  • Rule of Zero and smart pointer defaults
  • Four casts and when each is valid
  • vector invalidation and reserve
  • unordered_map vs map complexity
  • Code: reverse string, two-sum, cycle detect
  • extern "C" for ABI boundaries
  • One debugging STAR story (ASan, gdb, race)
  • Java OOP contrast if full-stack loop

Pattern cheat sheet (quick reference)

Need C / C++ approach
Dynamic buffer (C) malloc + free, size param
Dynamic buffer (C++) std::vector
Exclusive ownership std::unique_ptr
Shared ownership std::shared_ptr + weak_ptr
Polymorphic delete virtual ~Base()
C API from C++ extern "C"
Fast lookup unordered_map
Sorted keys map
Sort custom type std::sort + comparator
Thread-safe increment mutex or atomic
Find bugs ASan, UBSan, gdb, Valgrind

References

Official language references


Summary

C and C++ interviews connect pointer discipline, modern ownership, STL complexity, and clean coding under time pressure. Compile the examples locally and compare your answers to each section. Pair with shell scripting and Java interviews when interviewers compare memory models.

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)