Basic Syntax
Variables and Data Types
Fundamental data types and variable declarations
Interview: Core concept for all interviews
Variables and Data Types
C++ is a strongly and statically typed language. Every variable's data type must be declared explicitly (or deduced by the compiler) before compile-time. This guarantees deterministic memory layout, allows compiler-level optimization, and helps prevent runtime type errors.
Under the hood, declaring a variable reserves a specific number of bytes in memory. The interpretation of these bytes is governed by the variable's type.
Primitive Data Types and Memory Footprint
C++ primitive data types can vary in size depending on the target architecture (32-bit vs. 64-bit systems) and compiler ABI rules. The table below represents standard sizing on modern systems:
| Type | Standard Size | Common Value Range | Purpose |
|---|---|---|---|
| bool | 1 Byte | true or false | Boolean flags |
| char | 1 Byte | -128 to 127 | ASCII character representations |
| int | 4 Bytes | -2,147,483,648 to 2,147,483,647 | Standard integer values |
| float | 4 Bytes | ~3.4e+/-38 (7 decimal digits) | Single-precision floating point |
| double | 8 Bytes | ~1.7e+/-308 (15 decimal digits) | Double-precision floating point |
Integer Overflow and Underflow
Unsigned integers: Defined by the C++ standard to exhibit wrapping behavior (modulo arithmetic). For instance, incrementing the maximum value of a 32-bit unsigned integer wraps it cleanly back to 0.
Signed integers: Overflow behaves as Undefined Behavior (UB). The compiler assumes signed overflow can never occur, which can lead it to silently optimize away safety bounds checks or output unpredictable compilation patterns.
Modern Initialization Paradigms (C++11 Brace Initialization)
Modern C++ strongly advocates for Brace (or Uniform) Initialization using curly braces {} instead of parentheses or assignment operators. It brings unified syntax across arrays, classes, and aggregates, while adding safety by preventing narrowing conversions (e.g., trying to compile double-to-int without explicit cast).
Interview Corner
Q: What is a narrowing conversion, and how does uniform initialization prevent it?
A: Narrowing conversion occurs when a value of a larger type is assigned to a smaller type (e.g., double to int) which leads to loss of data precision. Traditional assignment style int x = 3.14; allows it silently by truncating. Using uniform initialization int x{3.14}; forces a compiler error, preventing data loss.
Q: What is the behavior of signed vs. unsigned overflow in C++?
A: Unsigned overflow wraps around deterministically using modulo arithmetic. Signed overflow is Undefined Behavior, allowing the compiler to optimize the code in unexpected ways (e.g. optimizing away checks like x + 1 > x).
Common Pitfalls
- Relying on Signed Overflow: Assuming a signed variable will wrap to a negative value when it overflows. Avoid this to prevent compiler-generated security bugs.
- Direct float comparison: Checking floating-point values using
==. Precision loss makes0.1 + 0.2 == 0.3evaluate to false. Always check within a tiny tolerance (epsilon).
Best Practices
- Prefer Brace Initialization (
int val{0};) for all variable definitions to ensure type safety. - Use standard fixed-width integers from
<cstdint>(likestd::int32_torstd::uint64_t) for predictable memory sizing in cross-platform systems. - Always default to
doublerather thanfloatunless memory storage is extremely constrained.
Use Cases
Fixed-Width Integers: Network packet construction and binary serialization formats.
Uniform Initialization: Robust aggregate constructors and preventing silent precision loss in algorithms.
Numeric Limits: Setting initialization bounds in searching or optimization algorithms.
Common Mistakes
Performing == comparison on floating-point data types.
Assuming signed integer overflow wraps around cleanly.
Mixing signed and unsigned variables in loops and comparisons.