Ch4: Operations on Data¶
TL;DR: Arithmetic and Shift Operators: C++ provides standard arithmetic, compound, increment, decrement, and bitwise operators that manipulate memory states directly. | Every fundamental numeric type has hard boundaries defined by the standard library (
std::numeric_limits) and by how many bits it uses to represent values in binary — signed types use two's complement, unsigned types wrap around.
⚡ Quick Reference¶
#include <iostream>
int main() {
int a = 10;
int b = 3;
// a + b, a - b, a * b, a / b, a % b - Standard arithmetic operations.
int sum = a + b;
int diff = a - b;
int prod = a * b;
int quot = a / b;
int rem = a % b;
// ++a, a++, --a, a-- - Increment and decrement operators.
int x1 = ++a; // Prefix: increment first, then assign.
int x2 = a++; // Postfix: assign first, then increment.
// a += b, a -= b - Compound assignment operators.
a += b;
// a < b, a > b, a == b - Comparison operators.
bool eq = (a == b);
// a && b, a || b, !a - Logical operations.
bool res = (a > 5 && b < 10);
// std::ios::boolalpha, std::noboolalpha - Outputs booleans as text (true/false) or numbers (1/0).
std::cout << std::boolalpha << res << std::noboolalpha << std::endl;
return 0;
}
Numeric Limits & Binary Representation¶
#include <iostream>
#include <limits>
#include <bitset>
#include <cstdint>
int main() {
// std::numeric_limits<T> - query min/max/precision for any numeric type at compile time
std::cout << "int min: " << std::numeric_limits<int>::min() << '\n';
std::cout << "int max: " << std::numeric_limits<int>::max() << '\n';
std::cout << "size_t max: " << std::numeric_limits<size_t>::max() << '\n';
// std::numeric_limits<T>::digits - number of bits used for the value (excludes sign bit)
std::cout << "int digits: " << std::numeric_limits<int>::digits << '\n';
// Fixed-width integer types (<cstdint>) - guarantee exact bit width across platforms
int32_t a{100};
uint8_t b{255};
// std::bitset<N> - view the raw binary representation of a value
std::bitset<8> bits(b);
std::cout << "255 as bits: " << bits << '\n';
// Two's complement: negative signed ints are represented by inverting bits and adding 1
int8_t neg{-1};
std::bitset<8> negBits(static_cast<uint8_t>(neg));
std::cout << "-1 as bits: " << negBits << '\n'; // 11111111
return 0;
}
🧠 Core Concepts¶
- Arithmetic and Shift Operators: C++ provides standard arithmetic, compound, increment, decrement, and bitwise operators that manipulate memory states directly.
- Constant Correctness (
const,constexpr,constinit): C++ offers compile-time constraints likeconst(runtime read-only),constexpr(compile-time evaluate), andconstinit(guaranteed compile-time initialization of mutable variables). - Integral Promotion & Type Conversion: In arithmetic, smaller types like
shortandcharare promoted toint, and explicit casting viastatic_castavoids compiler warnings during narrowing. std::numeric_limits<T>: A compile-time queryable trait class that exposes the min, max, precision, and other properties of any arithmetic type — safer than hardcoding magic numbers like2147483647.- Two's Complement: The standard binary representation for signed integers. The most significant bit acts as the sign bit; negative values are stored by inverting all bits of the positive value and adding 1.
- Fixed-Width Types:
<cstdint>provides types likeint32_t,uint8_t,int64_tthat guarantee an exact bit width regardless of platform, unlikeintorlongwhose size can vary by compiler/architecture.
⚠️ Pitfalls (Quick Scan)¶
| Mistake | Fix |
|---|---|
| Missing semicolon on statement | Always end statements with ; (Ch 4.3, 5.2) |
Nesting block comments / / / / |
Never nest block comments; use single-line comments (//) instead (Ch 4.2) |
| Division by zero | Verify that the divisor is not zero before dividing (Ch 4.3) |
Forgetting library #include directive |
Include the required header file at the top of the file (Ch 4.1) |
| Using functional initialization with narrowing values | Use braced initialization {} to catch narrowing conversions at compile time (6.2, 7.2) |
| Using uninitialized variables | Always initialize variables, using {} for default zero-init (5.2, 9.2) |
Using std::cin >> for space-separated input |
Use std::getline(cin, var) for strings containing space characters (7.2) |
Preceding integer literals with a leading zero (0) |
Never prefix decimal integer literals with a leading zero (6.2) |
Assuming std::setw persists across multiple outputs |
Reapply std::setw before printing every aligned value in a table (5.8) |
| Relying on implicit narrowing in assignments | Use static_cast<int>() explicitly or braced initialization to flag errors (7.2) |
| Overflowing unsigned integer limits | Check ranges using std::numeric_limits and ensure types are large enough (7.4) |
| Underflowing unsigned variables | Ensure values are strictly positive before decrementing unsigned types (7.4) |
Mixing small integral types (short/char) in arithmetic |
Be aware of integral promotion and cast the result back if necessary (5.11) |
Initializing constinit from non-constexpr variables |
Ensure the initializing expression is constexpr or a compile-time constant (6.5) |
Combining constexpr and constinit on the same declaration |
Use constexpr for immutable constants, and constinit for mutable compile-time initialized globals (6.5) |
Using non-constexpr values in static_assert |
Ensure the asserted condition only involves compile-time constants (6.4) |
| Assuming relational expressions print as true/false by default | Configure the stream using std::cout << std::boolalpha (5.6) |
| Assuming hex values print in uppercase by default | Use std::uppercase to output uppercase hexadecimal digits (5.8) |
| Setting precision beyond the limit of the floating-point type | Do not exceed 7 digits for float or 15 digits for double in setprecision (5.8) |
Hardcoding min/max values (e.g. 2147483647) |
Use std::numeric_limits<T>::max() instead |
Assuming int is always 4 bytes on every platform |
Use fixed-width types (int32_t) when exact size matters |
| Comparing signed and unsigned values directly | Cast explicitly or use same-signedness types to avoid silent conversion |
| Assuming negative numbers just "flip the sign bit" | Understand two's complement inverts all bits and adds 1 |
Using int8_t/uint8_t in std::cout expecting a number |
These are typically char aliases — cast to int first or output prints a character |
📖 Full Details¶
Cause → Effect → Fix with timestamp (click to expand)
* **Missing semicolon on statement** -> **Compile-time error ("expected semicolon before return")** -> **Always end statements with `;` (Ch 4.3, 5.2)** * **Nesting block comments `/* /* */ */`** -> **Confusing compiler error about unterminated comment** -> **Never nest block comments; use single-line comments (`//`) instead (Ch 4.2)** * **Division by zero** -> **Compiler warning followed by runtime crash when result is captured** -> **Verify that the divisor is not zero before dividing (Ch 4.3)** * **Forgetting library `#include` directive** -> **Compile-time error stating namespace std has no member** -> **Include the required header file at the top of the file (Ch 4.1)** * **Using functional initialization with narrowing values** -> **Floating-point value is silently truncated without compiler warnings** -> **Use braced initialization `{}` to catch narrowing conversions at compile time (6.2, 7.2)** * **Using uninitialized variables** -> **Retrieving indeterminate garbage values from memory causes undefined behavior** -> **Always initialize variables, using `{}` for default zero-init (5.2, 9.2)** * **Using `std::cin >>` for space-separated input** -> **Input string is truncated at the first space, leaving remaining input in buffer** -> **Use `std::getline(cin, var)` for strings containing space characters (7.2)** * **Preceding integer literals with a leading zero (`0`)** -> **Literal is interpreted as octal, producing unexpected values** -> **Never prefix decimal integer literals with a leading zero (6.2)** * **Assuming `std::setw` persists across multiple outputs** -> **Padding only affects the immediately following output field** -> **Reapply `std::setw` before printing every aligned value in a table (5.8)** * **Relying on implicit narrowing in assignments** -> **Floating-point values are silently truncated, resulting in data loss** -> **Use `static_cast📎 Repo Files¶
05.OperationsOnData/5.2.BasicOperations/main.cpp05.OperationsOnData/5.3.PrecedenceAndAssociativity/main.cpp05.OperationsOnData/5.4.PrefixPostfixIncrementDecrement/main.cpp05.OperationsOnData/5.5.CompoundAssignmentOperators/main.cpp05.OperationsOnData/5.6.RelationalOperators/main.cpp05.OperationsOnData/5.7.LogicalOperators/main.cpp05.OperationsOnData/5.8.OutputFormatting/main.cpp05.OperationsOnData/5.9.NumericLimits/main.cpp05.OperationsOnData/5.10.MathFunctions/main.cpp05.OperationsOnData/5.11.WeirdIntegralTypes/main.cpp06.LiteralsAndConstants/6.2Literals/main.cpp06.LiteralsAndConstants/6.3Constants/main.cpp06.LiteralsAndConstants/6.4ConstantExpressions/main.cpp06.LiteralsAndConstants/6.5constinit/main.cpp07.ConversionsOverflowAndUnderflow/7.2ImplicitDataConversions/main.cpp07.ConversionsOverflowAndUnderflow/7.3ExplicitDataConversions/main.cpp07.ConversionsOverflowAndUnderflow/7.4OverflowAndUnderflow/main.cpp08.BitwiseOperators/8.2PrintingIntegersInBinary/main.cpp08.BitwiseOperators/8.3ShiftOperators/main.cpp08.BitwiseOperators/8.4LogicalBitwiseOperators/main.cpp08.BitwiseOperators/8.5CompoundBitwiseOperators/main.cpp08.BitwiseOperators/8.6Masks/main.cpp08.BitwiseOperators/8.7MasksExample/main.cpp08.BitwiseOperators/8.8PackingColorInformation/main.cpp09.VariableLifetimeAndScope/9.2VariableScope/main.cpp07.ConversionsOverflowAndUnderflow/main.cpp08.BitwiseOperators/main.cpp