I/O Performance and Formatting
Utilizing <cstdio> over standard streams provides significant throughput advantages in computational environments where input volume is high. The scanf and printf functions bypass stream synchronization overhead. When handling floating-point precision, %.2f truncates output to two decimal places. Note that printf does not support %lf for double; %f suffices. Conversely, scanf requires %lf for double variables, and cannot restrict input precision via format specifiers like %.2lf, which yields undefined behavior or zero.
#include <cstdio>
int main() {
double value = 0.0;
// Correct usage for reading doubles
scanf("%lf", &value);
// Formatting output to two decimal places
printf("Formatted: %.2f\n", value);
return 0;
}
Variable Declaration and Arithmetic Operators
Every identifier must be declared with an explicit type before use. Reusing names within the same scope triggers compilation errors. Arithmetic operations follow standard precedence rules. Integer division truncates fractional parts, whereas floating-point division preserves decimals. Pre-increment (++i) modifies the variable prior to evaluation, while post-increment (i++) evaluates first, then increments. Type casting can be explicit (type)value or implicit through promotion rules.
#include <iostream>
int main() {
int base_price = 150;
double tax_rate = 0.18;
double final_amount = static_cast<double>(base_price) * (1.0 + tax_rate);
std::cout << final_amount << '\n';
return 0;
}
Conditional Execution
The if-else construct directs program flow based on Boolean evaluations. Comparison operators include >, <, >=, <=, ==, and !=. Logical operators allow complex condition aggregation: && (AND), || (OR), and ! (NOT). Chaining multiple else if branches handles mutually exclusive ranges efficiently. Nested conditions are valid but should be flattened for readability when possible.
#include <iostream>
void evaluate_score(int mark) {
if (mark >= 90) {
std::puts("Distinction");
} else if (mark >= 75) {
std::puts("Pass");
} else {
std::puts("Fail");
}
}
int main() {
evaluate_score(82);
return 0;
}
Iterative Structures
Looping constructs automate repetitive tasks. A while loop evaluates its condition before each iteration. A do-while loop guarantees at least one execution by checking the condition afterward. The for loop consolidates initialization, termination criteria, and iteration updates into a single header line, keeping the loop body uncluttered. Jump statements alter control flow: break terminates the innermost loop immediately, while continue skips the remainder of the current iteration. Infinite loops occur when the termination condition never evaluates to false; always ensure progress toward exit.
#include <iostream>
int main() {
int limit = 50;
int accumulator = 0;
for (int index = 1; index <= limit; ++index) {
accumulator += index * index;
}
std::cout << accumulator << '\n';
return 0;
}
Array Management
Static arrays allocate fixed memory blocks. Elements are accessed via zero-based indexing. Uninitialized local arrays contain indeterminate values; explicit initialization or global placement resolves this. Multidimensional arrays store data in row-major order. Traversal requires nested iterations. Bounds checking is manual; accessing arr[size] invokes undefined behavior.
#include <iostream>
int main() {
const int rows = 3, cols = 4;
int grid[rows][cols] = {{0}};
// Populate matrix diagonally
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
grid[r][c] = (r + 1) * (c + 1);
}
}
// Output transposed view
for (int c = 0; c < cols; ++c) {
for (int r = 0; r < rows; ++r) {
std::cout << grid[r][c] << ' ';
}
std::cout << '\n';
}
return 0;
}
String Processing and Character Encodings
Plain character arrays require manual management of the null terminator \0. The fgets function safely reads lines from streams, stopping at newline or buffer limits, and automatically appends \0. Direct manipulation involves strlen, strcmp, and strcpy from <cstring>. Modern C++ favors std::string, which manages memory dynamically, supports concatenation via +, and provides member functions like empty() and size(). Character-to-integer conversion relies on ASCII mappings: '0' is 48, 'A' is 65, 'a' is 97. Subtraction between characters yields their numeric difference.
#include <string>
#include <iostream>
void shift_characters(const std::string& source) {
std::string transformed = "";
for (char ch : source) {
if (ch >= 'a' && ch <= 'z') {
transformed += static_cast<char>('a' + (ch - 'a' + 1) % 26);
} else {
transformed += ch;
}
}
std::cout << transformed << '\n';
}
int main() {
shift_characters("abcxyz");
return 0;
}
Function Abstraction and Recursion
Functions encapsulate reusable logic. Definitions consist of a return type, identifier, parameter list, and body. Pass-by-value creates independent copies; pass-by-reference (&) allows modification of original arguments. Default parameters must trail required ones. Recursive functions call themselves with reduced problem size, requiring a base case to prevent stack overflow. Scope resolution dictates whether a variable refers to a local or global entity.
#include <iostream>
long long compute_factorial(int n) {
if (n <= 1) return 1;
return static_cast<long long>(n) * compute_factorial(n - 1);
}
int main() {
std::cout << compute_factorial(6) << '\n';
return 0;
}
Object Modeling and Memory Addressing
Classes bundle state (data members) and behavior (member functions) under access specifiers. private members restrict external access, enforcing encapsulation via public getters/setters. struct defaults to public visibility. Pointers store memory addresses and enable dynamic allocation via new/delete or malloc/free. Dereferencing retrieves values. References act as permanent aliases to existing objects, simplifying syntax compared to pointer indirection.
#include <iostream>
#include <string>
class Counter {
private:
int m_value;
public:
Counter() : m_value(0) {}
void increment() { ++m_value; }
int get() const { return m_value; }
};
int main() {
Counter tracker;
tracker.increment();
std::cout << tracker.get() << '\n';
return 0;
}
Standard Template Library (STL) Containers
Dynamic sequences rely heavily on STL abstractions. std::vector offers contiguous storage with amortized O(1) append operations. Access uses iterators or square brackets. std::queue implements FIFO semantics; priority_queue provides heap-based sorting with O(log n) insertion/removal. std::stack enforces LIFO constraints. std::deque extends vector capabilities with efficient front-end insertions. Ordered collections std::set and std::map maintain sorted keys via red-black trees, enabling O(log n) lookups. Map subscripts automatically insert missing keys with default-constructed values.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> dataset = {9, 3, 7, 1, 5};
std::sort(dataset.begin(), dataset.end());
auto it = std::lower_bound(dataset.begin(), dataset.end(), 5);
if (it != dataset.end()) {
std::cout << "Found target: " << *it << '\n';
}
return 0;
}
Bitwise Manipulation and Utility Algorithms
Low-level operations leverage machine registers directly. Bitwise AND (&), OR (|), XOR (^), NOT (~), and shifts (<<, >>) perform fast mathematical shortcuts. Extracting the k-th bit uses (x >> k) & 1. Isolating the lowest set bit employs x & -x. Standard utilities streamline data preparation: std::reverse swaps element positions, std::unique removes consecutive duplicates and returns an iterator to the new logical end, allowing subsequent resize or range erasure. Sorting accepts custom comparators or operator overloading. Binary search functions std::lower_bound and std::upper_bound locate insertion points in sorted ranges without full traversal.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> nums = {10, 10, 5, 5, 3, 8, 8, 2};
std::sort(nums.begin(), nums.end());
auto last = std::unique(nums.begin(), nums.end());
nums.erase(last, nums.end());
for (int val : nums) {
std::cout << val << ' ';
}
std::cout << '\n';
return 0;
}