C and C++ remain essential for systems programming, game development and performance-critical applications. This guide covers all 66 C/C++ tutorials on TheCodeForge, from pointers to the STL.
C and C++ are usually taught as one subject and are really two languages with a shared syntax and opposite philosophies. C gives you a small, complete model of the machine and expects you to manage everything. Modern C++ gives you abstractions that manage resources for you, and asks you to understand what they cost.
Writing C++ as though it were C — raw new and delete, C arrays, manual cleanup on every error path — is the single most common way to end up with the worst of both. Nearly every memory bug in C++ written since 2011 exists in code that ignored what C++11 added.
Resource Acquisition Is Initialization says: tie every resource to the lifetime of an object. Acquire in the constructor, release in the destructor, and the language's own scope rules handle cleanup — including when an exception unwinds the stack, which is the case manual cleanup almost always gets wrong.
Every modern C++ facility is an application of this. std::vector is RAII for a heap array. std::lock_guard is RAII for a mutex. std::unique_ptr is RAII for a single owned pointer. Once you see the pattern, the guidance to avoid raw new and delete stops being a style rule and becomes obvious.
| Instead of | Use | Why |
|---|---|---|
new / delete | std::make_unique, std::make_shared | Ownership is in the type; no leak on an exception path |
C arrays, malloc | std::vector, std::array | Knows its own size; bounds-checked access available |
char* and strcpy | std::string, std::string_view | No manual length arithmetic, no missing terminator |
Manual lock() / unlock() | std::lock_guard, std::scoped_lock | Cannot forget to unlock, including when throwing |
| Returning an error code nobody checks | std::optional, std::expected | The absence of a value is in the type, not in a convention |
This is the concept that most separates people who are comfortable in C and C++ from people who are not. Undefined behaviour does not mean the program does something unpredictable at that line. It means the compiler is entitled to assume it never happens, and to optimise on that assumption — so a null check after a dereference can be deleted entirely, because the dereference already promised the pointer was valid.
That is why UB bugs are so strange: the symptom appears far from the cause, changes with optimisation level, and disappears when you add a print statement. Signed integer overflow, reading an uninitialised value, out-of-bounds access, use after free and strict-aliasing violations are all in this category, and none of them reliably crash.
-fsanitize=address,undefined -g. The sanitizers turn silent UB into an immediate, precise report with a stack trace. Adopting them is the highest-value change most C and C++ projects can make in an afternoon.The question a C++ reviewer asks about every pointer is not 'can it be null' but 'who owns this'. The modern convention encodes the answer in the type, so it does not have to live in a comment:
| Type | Means | Caller must |
|---|---|---|
T& / const T& | Borrowed, never null, must outlive the call | Nothing |
T* | Borrowed, may be null, non-owning | Check for null; not free it |
std::unique_ptr<T> | Sole ownership, transferred by move | Accept that the source is now empty |
std::shared_ptr<T> | Shared ownership, refcounted | Watch for cycles — break them with std::weak_ptr |
std::string_view / std::span | Non-owning view over someone else's buffer | Guarantee the underlying data outlives the view |
string_view and span are the newest members and the easiest to misuse: returning one that refers to a local, or binding one to a temporary, produces a dangling view with no warning from older compilers. They are views, and a view is only as valid as the thing it views.
A large share of the time lost in C and C++ is spent not on the language but on compiling and linking. It helps to keep the pipeline stages distinct, because each produces a distinct class of error: the preprocessor pastes text, the compiler turns one translation unit into an object file, and the linker resolves symbols across all of them.
So an undefined reference is a linker error and never a syntax problem — the declaration was visible, the definition was not linked. The usual causes are a library missing from the link line, libraries listed in the wrong order, a definition that was declared but never written, or a C library included from C++ without extern "C", so the mangled name does not match.
std::optional.unique_ptr unless you can state why ownership is genuinely shared. It has zero overhead over a raw pointer, and single ownership is easier to reason about. shared_ptr costs atomic refcount updates and introduces the possibility of reference cycles that never free. Reaching for it by default is a common source of both.optional, string_view and filesystem. C++20 adds concepts, ranges and modules and is well supported by current compilers. Use C++20 for new work unless a target platform pins you lower.Every tutorial starts with a plain-English analogy — then real code, then interview questions.
Browse C / C++ Tutorials →