← Back to all posts

03 C++ Notes

Operators and Data Operations

C++ Notes


Operators

Precedence and Associativity


Prefix and Postfix

Prefix is preferred in most cases. Postfix is only worth reaching for when you specifically need the old value before the increment happens.

FormBehavior
++value (prefix)Increments first, then the new value is used in the same expression.
--value (prefix)Decrements first, then the new value is used.
value++ (postfix)The current value is used first, and it increments only for the next use.
value-- (postfix)The current value is used first, and it decrements only for the next use.

Compound Operators

Shorthand for performing an operation and assigning the result back to the same variable.

LonghandShorthand
value = value + 1;value += 1;
value = value / 2;value /= 2;
value = value * 10;value *= 10;

The same pattern applies for -=, %=, and every other arithmetic operator.


Relational Operators

Used to compare values. The result is always true or false, which prints as 1 or 0.

OperatorMeaning
>greater than
<less than
>=greater than or equal to
<=less than or equal to
==equality check
!=inequality check

Wrap a comparison in brackets when using it inside cout: std::cout << (num1 > num2) << '\n'; prints 1 or 0. Without the brackets, << can get parsed before the comparison and give you a result you didn't expect.


Logical Operators

OperatorBehavior
&& (AND)Combines two or more conditions. Returns true only if every condition is true.
|| (OR)Combines two or more conditions. Returns false only if every condition is false.
! (NOT)Negates a condition. !true becomes false, !false becomes true.

Output Formatting

Formatting Reference

ManipulatorWhat It DoesExample
'\n'Adds a newline character. Does not flush the buffer.
std::flushFlushes the buffer without adding a newline.
std::endlAdds a newline and flushes the buffer. Slower than '\n' for that reason.
std::setw(n)Sets the minimum field width for the next output only. If the text is shorter, a fill character is added. If longer, nothing gets cut."Hassan" with setw(10)" Hassan"
std::leftAligns text to the left, padding on the right."Left |"
std::rightAligns text to the right, padding on the left. This is the default." Right|"
std::internalPlaces padding between the sign and the number. Only affects signed numbers and base prefixes, does nothing for plain strings.-42 with width 6 → "- 42"
std::setfill(c)Sets the fill character instead of the default space.setfill('-') + setw(10) + "Ali""-------Ali"
std::boolalphaPrints bool as true/false instead of 1/0. Disable with std::noboolalpha.true"true"
std::showposAdds a + sign to positive numbers. Negative numbers already show their sign regardless. Disable with std::noshowpos.25"+25"
std::decPrints integers in decimal. Also used to reset back from hex or octal.717171"717171"
std::hexPrints integers in hexadecimal. Has no effect on floating point values.717171"af173"
std::octPrints integers in octal. Has no effect on floating point values.717171"2570573"
std::showbaseAdds the base prefix to output. Disable with std::noshowbase.hex → "0xaf173", oct → "02570573"
std::uppercaseUppercases hex digits and scientific notation. Does not affect strings, boolalpha, or chars. Disable with std::nouppercase."af173""AF173"
std::fixedForces fixed-point notation. Once set, setprecision means digits after the decimal point. Very small numbers may show as 0.setprecision(3): 12.3"12.300"
std::scientificForces scientific notation. setprecision means digits after the decimal point of the exponent form.setprecision(4): 0.00123456"1.2346e-03"
std::setprecision(n)Controls how many digits get printed. Default is 6 significant digits. Does not affect the stored value.setprecision(3): 123.456789"123"
std::showpointShows trailing zeros up to the set precision for floating point. Has no effect on int. Disable with std::noshowpoint.setprecision(5): 34.1"34.100"

To reset fixed or scientific back to the default auto notation: std::cout.unsetf(std::ios::scientific | std::ios::fixed);

Behavior: Temporary vs Permanent vs No State

CategoryMeaningWhich Manipulators
Temporary 1-shotAffects the very next output only, then resets automatically.std::setw(n)
Permanent stickyChanges the stream's state and stays active for all future output until you explicitly change it back.Every other manipulator above: setfill, setprecision, left, right, hex, fixed, and so on.
No state actionA one-time action with no persistence at all, there's no "state" to reset.'\n', std::endl, std::flush

This is the one thing about output formatting that trips people up the most: setw(10) quietly stops applying after a single use, while std::hex keeps every number in hex until you explicitly switch back with std::dec. Knowing which bucket a manipulator falls into saves you from chasing formatting bugs that are actually just leftover state from three lines earlier.


Code Example: main.cpp

C++
#include <iostream>
#include <iomanip>
#include <ios>

int main() {
    std::cout << "01) '\\n'\n";
    std::cout << "Line 1\n";
    std::cout << "Line 2\n\n";

    std::cout << "02) std::flush;\n";
    std::cout << "Flushing buffer..." << std::flush << "\n\n"; // flushes but no newline added by flush

    std::cout << "03) std::endl;\n";
    std::cout << "Using endl" << std::endl; // newline + flush

    std::cout << "\n16) std::setprecision(3);\n";
    double sep = 123.456789;
    std::cout << std::setprecision(3) << sep << "\n\n"; // prints: 123

    std::cout << "04) std::setw(10);\n";
    std::cout << std::setw(10) << "Hassan" << "\n\n"; // prints: "    Hassan"

    std::cout << "05) std::left / std::right / std::internal;\n";
    std::cout << std::left  << std::setw(10) << "Left"  << "|\n"; // prints: "Left      |"
    std::cout << std::right << std::setw(10) << "Right" << "|\n"; // prints: "     Right|"
    std::cout << std::internal << std::setw(6) << -42 << "\n\n"; // prints: "-   42"

    std::cout << "06) std::setfill();\n";
    std::cout << std::setfill('-') << std::setw(10) << "Ali" << "\n"; // prints: "-------Ali"
    std::cout << std::setfill(' ') << "\n"; // reset fill back to space

    std::cout << "07) std::boolalpha;\n";
    std::cout << std::boolalpha   << true << " " << false << "\n"; // prints: "true false"
    std::cout << std::noboolalpha << true << " " << false << "\n\n"; // prints: "1 0"

    std::cout << "08) std::showpos;\n";
    std::cout << std::showpos   << 25 << " " << -25 << "\n";   // prints: "+25 -25"
    std::cout << std::noshowpos << 25 << " " << -25 << "\n\n"; // prints: "25 -25"

    std::cout << "09) std::dec;\n";
    std::cout << std::dec << 717171 << "\n\n"; // prints: 717171

    std::cout << "10) std::hex;\n";
    std::cout << std::hex << 717171 << "\n\n"; // prints: af173

    std::cout << "11) std::oct;\n";
    std::cout << std::oct << 717171 << "\n\n"; // prints: 2570573

    std::cout << "12) std::showbase;\n";
    std::cout << std::showbase << std::hex << 717171 << "\n"; // prints: 0xaf173
    std::cout << std::oct << 717171 << "\n";                  // prints: 02570573
    std::cout << std::noshowbase << std::dec << 717171 << "\n\n"; // prints: 717171

    std::cout << "13) std::uppercase;\n";
    std::cout << std::uppercase   << std::hex << 717171 << "\n"; // prints: AF173
    std::cout << std::nouppercase << std::hex << 717171 << "\n"; // prints: af173
    std::cout << std::dec << "\n"; // reset back to decimal

    std::cout << "14) std::fixed;\n";
    double value = 12.345678;
    std::cout << std::fixed << std::setprecision(3) << value << "\n\n"; // prints: 12.346

    std::cout << "15) std::scientific;\n";
    std::cout << std::scientific << std::setprecision(4) << value << "\n\n"; // prints: 1.2346e+01

    std::cout.unsetf(std::ios::scientific | std::ios::fixed); // reset to default auto notation

    std::cout << "17) std::showpoint;\n";
    std::cout << std::setprecision(5);
    std::cout << std::showpoint  << 34.1 << "\n"; // prints: 34.100
    std::cout << std::noshowpoint << 34.1 << "\n\n"; // prints: 34.1

    return 0;
}

Numeric Limits

CallReturns
std::numeric_limits<T>::max()The largest value the type can hold.
std::numeric_limits<T>::min()The smallest value for integers, or the smallest safe positive value for decimals.
std::numeric_limits<T>::lowest()The actual most negative value the type can hold.

For integers, min() and lowest() give the exact same result. For decimal types they don't: min() gives the smallest full-precision positive number, not the most negative one, lowest() gives the actual most negative value, and denorm_min() gives the absolute smallest positive number possible, a subnormal value. In practice, stick to max() and lowest() for decimal types.


Code Example: main.cpp

C++
#include <iostream>
#include <limits>

int main() {
    std::cout << "short:        "
              << std::numeric_limits<short>::min() << " to "
              << std::numeric_limits<short>::max() << std::endl;
    // prints: -32768 to 32767

    std::cout << "int:          "
              << std::numeric_limits<int>::min() << " to "
              << std::numeric_limits<int>::max() << std::endl;
    // prints: -2147483648 to 2147483647

    std::cout << "unsigned int: "
              << std::numeric_limits<unsigned int>::min() << " to "
              << std::numeric_limits<unsigned int>::max() << std::endl;
    // prints: 0 to 4294967295

    std::cout << "long:         "
              << std::numeric_limits<long>::min() << " to "
              << std::numeric_limits<long>::max() << std::endl;
    // prints: -2147483648 to 2147483647 (or larger on 64-bit)

    std::cout << "float:        "
              << std::numeric_limits<float>::lowest() << " to "
              << std::numeric_limits<float>::max() << std::endl;
    // prints: -3.40282e+38 to 3.40282e+38

    std::cout << "double:       "
              << std::numeric_limits<double>::lowest() << " to "
              << std::numeric_limits<double>::max() << std::endl;
    // prints: -1.79769e+308 to 1.79769e+308

    return 0;
}

Math Functions

Requires #include <cmath>.

FunctionWhat It DoesExample
std::round(x)Rounds to the nearest integer. First digit after the decimal ≥ 5 rounds up, ≤ 4 rounds down.round(3.5) = 4, round(3.4) = 3
std::floor(x)Always rounds down to the lower integer.floor(3.7) = 3
std::ceil(x)Always rounds up to the upper integer.ceil(3.1) = 4
std::abs(x)Returns the absolute, positive value.abs(-5) = 5
std::pow(x, y)Returns x raised to the power y.pow(2, 3) = 8
std::exp(x)Returns e, Euler's constant (2.71828), raised to the power x.exp(4) = 54.598
std::log(x)Natural logarithm, base e. The inverse of exp().log(54.59) = 4
std::log10(x)Logarithm with base 10.log10(10000) = 4
std::sqrt(x)Returns the square root.sqrt(49) = 7
std::cos(x)Cosine of x, in radians.cos(0) = 1
std::sin(x)Sine of x, in radians.sin(0) = 0
std::tan(x)Tangent of x, in radians.tan(45) = 1.619

Code Example: main.cpp

C++
#include <iostream>
#include <cmath>

int main() {
    std::cout << "round(3.5)    = " << std::round(3.5)   << std::endl; // prints: 4
    std::cout << "round(3.4)    = " << std::round(3.4)   << std::endl; // prints: 3
    std::cout << "floor(3.7)    = " << std::floor(3.7)   << std::endl; // prints: 3
    std::cout << "ceil(3.1)     = " << std::ceil(3.1)    << std::endl; // prints: 4
    std::cout << "abs(-5)       = " << std::abs(-5)     << std::endl; // prints: 5
    std::cout << "pow(2, 3)     = " << std::pow(2, 3)  << std::endl; // prints: 8
    std::cout << "exp(4)        = " << std::exp(4)     << std::endl; // prints: 54.5982
    std::cout << "log(54.59)    = " << std::log(54.59)  << std::endl; // prints: ~4
    std::cout << "log10(10000)  = " << std::log10(10000) << std::endl; // prints: 4
    std::cout << "sqrt(49)      = " << std::sqrt(49)    << std::endl; // prints: 7
    std::cout << "cos(0)        = " << std::cos(0)     << std::endl; // prints: 1
    std::cout << "sin(0)        = " << std::sin(0)     << std::endl; // prints: 0
    std::cout << "tan(45)       = " << std::tan(45)    << std::endl; // prints: 1.6198 (radians)

    return 0;
}

Weird Integral Types: Integer Promotion

If you store the result of char + char back into a char, you may silently lose data, because the actual result of that addition is an int. Using auto instead sidesteps the whole problem, since it just stores whatever type the promotion actually produced.


Code Example: main.cpp

C++
#include <iostream>

int main() {
    short short1 = 10;
    short short2 = 20;
    auto shortsum = short1 + short2; // promoted to int before addition, result is int

    std::cout << "sizeof(short1)   = " << sizeof(short1)   << std::endl; // prints: 2
    std::cout << "sizeof(short2)   = " << sizeof(short2)   << std::endl; // prints: 2
    std::cout << "sizeof(shortsum) = " << sizeof(shortsum) << std::endl; // prints: 4 (int!)
    std::cout << "shortsum         = " << shortsum << std::endl;           // prints: 30

    char char1 = 5;
    char char2 = 10;
    auto charsum = char1 + char2; // promoted to int before addition, result is int

    std::cout << "\nsizeof(char1)   = " << sizeof(char1)   << std::endl; // prints: 1
    std::cout << "sizeof(char2)   = " << sizeof(char2)   << std::endl; // prints: 1
    std::cout << "sizeof(charsum) = " << sizeof(charsum) << std::endl; // prints: 4 (int!)
    std::cout << "charsum         = " << charsum << std::endl;           // prints: 15

    return 0;
}

Closing

That's the full set of operators, every output manipulator worth knowing, how to check a type's real limits, the math library, and the one promotion rule that silently rewrites the type of your arithmetic. Next up, arrays.

← Previous 02 - Variables and Data Types Next → 04 - Flow Control