Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++ Module 2

Types, values and expressions interview questions

Interview questions on types, values and expressions: short warm-ups, then what each harder question tests, the first step of a good answer and its follow-ups.

36 questions: 12 warm-ups and 24 with model answers · Levels: Fresher to staff

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher to mid

    Why do C++ programmers use unsigned types for bit manipulation?

    Answered in the lesson C++ bitwise operators and the <bit> header

  2. Fresher to mid

    What is the difference between const and constexpr?

    Answered in the lesson C++ const, constexpr, consteval and constinit

  3. Fresher to mid

    What does auto deduce for auto x = r; when r is a const int&?

    Answered in the lesson C++ auto, decltype and type aliases

  4. Fresher to mid

    When should a function take std::string_view instead of const std::string&?

    Answered in the lesson std::string and std::string_view

  5. Fresher to mid

    Why does std::string::size() not return the number of characters in a Hindi word?

    Answered in the lesson UTF-8, Unicode and text encodings in C++

  6. Fresher

    What is the difference between enum and enum class?

    Answered in the lesson C++ enums: enum class and std::to_underlying

  7. Fresher

    What is the difference between int x(2.9); and int x{2.9};?

    Answered in the lesson Variables and initialisation: =, () and {}

  8. Fresher

    Is an int always 32 bits in C++?

    Answered in the lesson C++ fundamental types: integers, floats, char, bool

  9. Fresher

    What happens when an unsigned int overflows, and when an int does?

    Answered in the lesson Integer overflow and signed/unsigned conversions

  10. Fresher

    Why is 0.1 + 0.2 == 0.3 false in C++?

    Answered in the lesson Floating-point precision and comparison in C++

  11. Fresher

    What are -7 / 2 and -7 % 2 in C++, and why?

    Answered in the lesson C++ operators, precedence and integer division

  12. Fresher

    What is the difference between undefined, unspecified and implementation-defined behaviour?

    Answered in the lesson Why i++ + ++i has no single answer in C++

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Fresher to mid

    Feature flags live in a std::uint64_t and are set with flags |= 1 << id. Flags 0 to 31 work, but ids from 32 upwards misbehave, and differently in debug and release builds. Explain what happens and fix it.

    What it tests
    Integral promotion, the shift-count rule of the standard, why undefined behaviour differs between builds, and validating a bit number that comes from outside.
    First step
    Look at the type of 1: it is an int, so 1 << id is computed in 32 bits and only then widened for the |=. For an id of 32 or more the count is at least the width of the promoted left operand, which is undefined behaviour, not a value that wraps or becomes zero.
    Follow-ups

    From the lesson C++ bitwise operators and the <bit> header

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; nothing renews unless you choose to. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  2. Mid to senior

    Each delivery route visits some of 50 cities, and you keep ten million routes in memory. You must answer, many times a second, how many cities two routes have in common. Choose the representation and the operations, and estimate the memory.

    What it tests
    Representing small sets as bit masks, counting with std::popcount, memory estimates, and knowing when a mask stops being the right representation.
    First step
    Fifty cities fit in one std::uint64_t, with bit i for city i. The common cities of two routes are then a & b, and their number is std::popcount(a & b), a few instructions per pair. Ten million routes take 80 MB as masks, against several times that as vectors of city numbers.
    Follow-ups

    From the lesson C++ bitwise operators and the <bit> header

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  3. Fresher to mid

    A function returns n! as a std::int64_t. Its run-time tests compare n up to 25 with hard-coded values; they pass with one compiler and fail with another. What is wrong, and how could constexpr and static_assert have caught it?

    What it tests
    Recognising signed overflow as undefined behaviour, why compilers may then differ, and using constant evaluation as a check that refuses undefined behaviour.
    First step
    Find the largest n whose factorial fits: 20! is about 2.4 × 10¹⁸ and 21! about 5.1 × 10¹⁹, above the int64 maximum of about 9.2 × 10¹⁸. Every test above 20 depends on undefined behaviour, so any result, and a different one per compiler, is possible.
    Follow-ups

    From the lesson C++ const, constexpr, consteval and constinit

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 2 minutes
  4. Mid to senior

    A global logger defined in one source file is used while a global cache in another file is initialised, and the program sometimes crashes before main starts. Explain the failure, and how constinit and other techniques prevent it.

    What it tests
    The static initialisation order problem, constant against dynamic initialisation, and what constinit does and does not guarantee.
    First step
    Both globals are initialised by code that runs at start-up, and between source files the standard leaves the order of that code open. If the cache runs first, it uses a logger whose constructor has not run yet. A constant- initialised object has no such order, because the compiler sets it up before any code runs.
    Follow-ups

    From the lesson C++ const, constexpr, consteval and constinit

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  5. Fresher to mid

    The loop for (auto order : orders) order.status = Status::shipped; compiles, but no order is ever marked as shipped, and a profile shows the loop spending most of its time copying. Explain both symptoms and the fix.

    What it tests
    How auto deduces in a range-based for loop, values against references, the cost of copies, and which warnings do and do not catch it.
    First step
    auto deduces Order, so each order is a copy of an element: the assignment changes the copy, which is destroyed at the end of the iteration, and every iteration copies a whole Order. Writing auto& order makes order refer to each element, which fixes both symptoms.
    Follow-ups

    From the lesson C++ auto, decltype and type aliases

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  6. Mid to senior

    A function returns std::vector<bool>, and a caller writes auto ok = check_all()[0]; and uses ok a few lines later. It seems to work in tests, but AddressSanitizer reports a heap-use-after-free. Explain why, and how to make such code safe.

    What it tests
    Proxy types, the lifetime of temporaries, what auto deduces from std::vector<bool>, and reading an AddressSanitizer report.
    First step
    check_all() returns a temporary vector, and std::vector<bool>::operator[] returns a proxy that points into the vector's storage. auto deduces the proxy, not bool, and the temporary is destroyed at the end of the statement, so the later read uses freed memory.
    Follow-ups

    From the lesson C++ auto, decltype and type aliases

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  7. Mid to senior

    A class stores a std::string_view member set from a constructor argument. Objects made from string literals work, but objects made from lines read in a loop print garbage later. Explain what happens and how you would fix the class.

    What it tests
    The lifetime of the text behind a view, owning against non-owning members, and why the bug depends on the input.
    First step
    A view owns nothing. A string literal lives for the whole program, but a line read in a loop is overwritten on the next iteration or destroyed when the loop ends, so every stored view points at characters that changed or were freed. A member that outlives the constructor call must own its text: store a std::string.
    Follow-ups

    From the lesson std::string and std::string_view

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  8. Mid to senior

    A log parser splits each 300-byte line into 12 fields stored as std::string, at 50,000 lines a second, and a profile shows most of the time in malloc. How would you reduce the cost, and what new risk do you take on?

    What it tests
    Estimating allocations, the small-string buffer of each standard library, std::string_view for parsing, and the lifetime risk that views add.
    First step
    Count first: each field longer than the inline buffer (15 characters with libstdc++, 22 with libc++) costs an allocation, so up to 12 per line, 600,000 a second, plus the vector. Splitting into std::string_view pieces of the line allocates nothing for the fields, as long as the line outlives the views.
    Follow-ups

    From the lesson std::string and std::string_view

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  9. Mid to senior

    A sign-up form allows names of up to 20 characters, and the C++ server checks name.size() <= 20 and truncates longer names. Users with Hindi and Tamil names say that short names are rejected and that stored names end in a broken letter. Explain both and design the fix.

    What it tests
    Bytes against code points against grapheme clusters, cutting text at a safe boundary, and choosing what a limit should count.
    First step
    size() counts UTF-8 bytes, and a Devanagari or Tamil letter takes 3, so a name of 7 letters is already over 20. The broken letters come from cutting after a byte count, which can split a code point or a grapheme cluster. Decide whether the limit protects storage (bytes) or describes what people see (grapheme clusters).
    Follow-ups

    From the lesson UTF-8, Unicode and text encodings in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  10. Mid to staff

    A C++ service receives text that should be UTF-8 from many clients and passes it to a JSON writer and a database. Why must it validate the text, what exactly should it reject, and what should it do with invalid input?

    What it tests
    The definition of well-formed UTF-8, the security history of lenient decoders, and a policy for invalid data at a system boundary.
    First step
    Validate once, where the text enters. Well-formed UTF-8 means complete sequences in their shortest form, no surrogates and nothing above U+10FFFF. RFC 3629 describes attacks in which overlong forms slipped past checks that looked only for the short ones.
    Follow-ups

    From the lesson UTF-8, Unicode and text encodings in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    An order system has enum class Status { placed, paid, shipped }, used in 40 switch statements across the code base. You must add a refunded status without missing any place that needs it. How do you make the compiler find every one?

    What it tests
    -Wswitch and -Wswitch-enum, the cost of default cases, warnings as errors, and testing code that handles states.
    First step
    Let the compiler search: build with -Wall -Werror=switch, so that every switch without a default that misses refunded stops the build. Then deal with the switches that have a default, which hide the new value, by turning on -Wswitch-enum for the change or searching for them.
    Follow-ups

    From the lesson C++ enums: enum class and std::to_underlying

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  12. Mid to senior

    A network message carries a one-byte message type, which your code converts with static_cast to enum class MessageType : std::uint8_t. What can go wrong with untrusted input, and how should the conversion be written?

    What it tests
    Which values an enumeration with a fixed underlying type can hold, validation at the boundary, and handling of unknown values.
    First step
    static_cast accepts every std::uint8_t, so a hostile or newer client can produce a MessageType that names no enumerator, and switches or tables indexed by it misbehave. Convert through a function that checks the value against the known enumerators and returns std::optional<MessageType>.
    Follow-ups

    From the lesson C++ enums: enum class and std::to_underlying

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  13. Fresher to mid

    A service fills struct Limits { int max_connections; int timeout_ms; }; from a configuration parser that skips missing keys. The struct is declared as Limits limits; inside a function. Tests pass, but in production timeout_ms is sometimes a huge random number. What is happening, and how do you make it impossible?

    What it tests
    Default-initialisation of aggregates, why the bug hides in tests, which tools find it on which platform, and a design that leaves no member without a value.
    First step
    Limits limits; default-initialises the struct, and for int members that means no initialisation at all: a field the parser never assigns keeps whatever bytes were on the stack, and reading it is undefined behaviour. List the keys that can be missing, then give every member a value where it is declared.
    Follow-ups

    From the lesson Variables and initialisation: =, () and {}

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  14. Mid to senior

    A code review changes std::vector<int> slots(count, 0); to std::vector<int> slots{count, 0}; "to use uniform initialisation everywhere". count is a std::size_t. What does the change do with Clang and with GCC, and what rule would you write into the team's style guide?

    What it tests
    How braces prefer an initializer_list constructor, narrowing inside braces, the different reactions of Clang and GCC, and turning that into a rule a reviewer can apply.
    First step
    Read the braces the way the compiler does: std::vector has a constructor taking std::initializer_list<int>, so {count, 0} asks for a vector holding the two values count and 0, not count zeros. Then notice that count is a std::size_t, and converting it to int inside braces is a narrowing conversion.
    Follow-ups

    From the lesson Variables and initialisation: =, () and {}

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  15. Mid to senior

    A tool writes struct Header { long timestamp; char kind; unsigned size; }; to disk with one fwrite on 64-bit Linux, and the Windows build of the same tool reads garbage from those files. List every reason, and design a header layout that any compiler reads the same way.

    What it tests
    Data models (LP64 and LLP64), padding and alignment, plain char and byte order, and serialising fields explicitly instead of copying memory.
    First step
    Compare the layouts before guessing. long is 8 bytes on 64-bit Linux and 4 on 64-bit Windows, so every later field moves and the padding moves with it: sizeof(Header) is 16 on Linux and 12 on Windows, and kind sits at offset 8 on one and 4 on the other. That alone explains the garbage.
    Follow-ups

    From the lesson C++ fundamental types: integers, floats, char, bool

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  16. Fresher to mid

    A timing helper returns the milliseconds since start-up as a long. It works on Linux servers, but on Windows machines that stay on for a few weeks, timeouts suddenly fire at once or never fire. When exactly does it break, and how do you fix the type?

    What it tests
    Knowing that long is 32 bits on Windows, working out when a millisecond counter overflows, and choosing std::chrono or a 64-bit type instead.
    First step
    Work out the limit: a 32-bit signed long holds at most 2,147,483,647, and that many milliseconds is about 24.86 days (2,147,483,647 ÷ 86,400,000). Counting past it overflows a signed type, which is undefined behaviour; in practice the value wraps to a large negative number, and every comparison with a deadline flips.
    Follow-ups

    From the lesson C++ fundamental types: integers, floats, char, bool

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  17. Fresher to mid

    This loop prints a vector backwards: for (std::size_t i = v.size() - 1; i >= 0; --i) std::cout << v[i]; It prints the elements and then garbage until the program crashes, and with an empty vector it crashes at once. Explain both, with the numbers involved, and give two correct ways to write it.

    What it tests
    Unsigned wrap-around, size() - 1 on an empty container, a condition that is always true, which warnings flag it, and idiomatic reverse iteration.
    First step
    Two unsigned facts explain everything. i >= 0 is always true for an unsigned i, so the loop has no exit; after i reaches 0, --i wraps to 18,446,744,073,709,551,615. And for an empty vector, v.size() - 1 is that same value from the start.
    Follow-ups

    From the lesson Integer overflow and signed/unsigned conversions

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  18. Mid to senior

    A binary search over a sorted array of 1.5 billion entries computes int mid = (lo + hi) / 2; and crashes, but only on the largest inputs. Why, what would UBSan report, and what are the correct ways to compute the midpoint?

    What it tests
    Spotting signed overflow in index arithmetic, reading a sanitizer report, and choosing between lo + (hi - lo) / 2, std::midpoint and wider index types.
    First step
    Put numbers in: with lo = 1,000,000,000 and hi = 1,500,000,000, lo + hi is 2,500,000,000, more than the largest int, 2,147,483,647. The addition overflows before the division, which is undefined behaviour; the wrapped sum halves to a negative index, and reading there crashes.
    Follow-ups

    From the lesson Integer overflow and signed/unsigned conversions

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  19. Fresher to mid

    A simulation steps its clock with for (double t = 0.0; t != 1.0; t += 0.1), and the loop never stops. Explain why, and write a loop that runs exactly eleven times, for t = 0.0, 0.1 … 1.0, with each t as accurate as possible.

    What it tests
    Accumulated rounding error, why != on computed doubles is fragile, integer loop counters, and the difference between step * 0.1 and step / 10.0.
    First step
    Print t with 17 significant digits after ten additions: it is 0.99999999999999989, not 1.0, so t != 1.0 stays true and t keeps growing past 1. Drive the loop with an integer that counts steps, and compute t from it on every iteration.
    Follow-ups

    From the lesson Floating-point precision and comparison in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  20. Mid to senior

    Sensor readings arrive as doubles, and a failed reading is stored as NaN. After std::sort, the vector is not even sorted, and the result differs between a Linux build and a Mac build. Explain why, and sort the readings with the failed ones at the end.

    What it tests
    Strict weak ordering and why NaN breaks it, why the outcome differs between standard libraries, and correct alternatives such as partitioning or a comparator that orders NaN.
    First step
    Check the comparator against the rules std::sort relies on: < is not a strict weak ordering once NaN is present, because NaN is "equal" to every value, yet those values are not equal to each other. Breaking that requirement is undefined behaviour, so each library's algorithm goes wrong in its own way.
    Follow-ups

    From the lesson Floating-point precision and comparison in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  21. Fresher to mid

    Permissions are bits: READ is 1, WRITE is 2, EXEC is 4. A check reads if (flags & WRITE == WRITE) allow_write();. Which users does it let write, which does it refuse, and how would you write and protect the check?

    What it tests
    Precedence of == over &, working the wrong expression through with numbers, compiler warnings as a gate, and safer ways to express flag tests.
    First step
    Group the expression the way the compiler does: == binds more tightly than &, so it is flags & (WRITE == WRITE), which is flags & 1. The check therefore tests the READ bit. Then try a read-only user, flags = 1, and a write-only user, flags = 2.
    Follow-ups

    From the lesson C++ operators, precedence and integer division

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  22. Mid to senior

    A ring buffer of size slots moves its read position backwards with pos = (pos - 1) % size;. With pos an int, it reads outside the buffer when pos is 0. With pos a std::size_t, it works when size is 16 but not when size is 10. Explain both, and step backwards correctly.

    What it tests
    The sign of % with negative operands, unsigned wrap-around combined with %, why powers of two hide the bug, and a correct backward step.
    First step
    Evaluate each case at pos = 0. As an int, 0 - 1 is -1, and -1 % size is -1, an index before the buffer. As a std::size_t, 0 - 1 wraps to 2⁶⁴ − 1, and the remainder of that by size is correct only when size divides 2⁶⁴.
    Follow-ups

    From the lesson C++ operators, precedence and integer division

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  23. Mid to senior

    Old code calls submit(std::unique_ptr<Job>(new Job(config)), next_priority()); and next_priority() can throw. Could this leak memory before C++17? Can it still leak today? What would you write instead, and why?

    What it tests
    How C++17 changed the sequencing of function arguments, exception safety across argument evaluation, and why std::make_unique is preferred anyway.
    First step
    List the steps the call involves: allocate and construct the Job, construct the unique_ptr, call next_priority. Before C++17 the steps of different arguments could interleave, so the allocation could happen, then the throwing call, before any unique_ptr owned the Job.
    Follow-ups

    From the lesson Why i++ + ++i has no single answer in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  24. Senior to staff

    Since C++17, std::cout << f() << g() calls f before g. Does the same hold for your own Logger with an overloaded operator<<, for a + b where both operands are calls, and for target() = make() with a user-defined operator=? What about target().operator=(make())?

    What it tests
    The rule that overloaded operators keep the sequencing of the built-in ones, which built-in operators have an order, and how explicit call syntax changes it.
    First step
    Recall the rule from [over.match.oper]: written with operator notation, an overloaded operator's operands are sequenced as the built-in operator prescribes. So answer each case by asking what the built-in <<, + and = do, and notice that the last example is not operator notation at all.
    Follow-ups

    From the lesson Why i++ + ++i has no single answer in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes

All C++ interview questions · The C++ track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.