TL;DR / Takeaways

Let’s write the smallest useful C++ program and explain every non-obvious thing inline—preprocessing, <iostream>, std namespace, what main actually promises, compilation/linking, the C/C++ runtime, and buffering. We’ll not deep-dive into std::cout’s formatting internals.

The program (annotated, line-by-line)

// ── 1) Preprocessor directive: handled before compilation proper.
//    The preprocessor *textually includes* the named header after macro expansion.
//    <iostream> declares standard I/O stream types and the global objects
//    (std::cout, std::cin, std::cerr). It mostly provides DECLARATIONS and
//    template definitions. Non-template parts live in the stdlib binary
//    (libstdc++/libc++), which the LINKER resolves later.
//    Angle brackets <...> search system include paths; "..." would search
//    the current directory first, then system paths.
#include <iostream>

// ── 2) Program entry point per the C++ standard's abstract machine.
//    • The return type MUST be int (process exit status). `void main` is non-standard.
//    • Valid forms: `int main()` or `int main(int argc, char* argv[])`.
//    • Falling off the end of main is equivalent to `return 0;`.
//    • The opening brace starts a BLOCK SCOPE; automatic objects inside obey RAII
//      (their destructors run when the scope ends).
int main() {

// ── 3) Output one line, then a newline character.
//    • We qualify with std:: to avoid polluting the global namespace
//      (prefer this over `using namespace std;`, especially in headers).
//    • The string literal type is `const char[15]` (14 chars + '\n' + '\0') and
//      typically resides in read-only memory; do not attempt to modify it.
//    • The trailing '\n' matters: when std::cout is line-buffered (commonly when
//      attached to a terminal), the newline triggers a flush so the text appears
//      promptly. `std::endl` also inserts '\n' *and* forces a flush, but has
//      extra overhead—use it when you specifically need an immediate flush.
//    • By default, iostreams synchronize with C stdio (`std::ios::sync_with_stdio(true)`)
//      so mixing printf/puts and cout keeps order (at some performance cost).
//      If you later disable sync for speed, avoid mixing the two systems or flush explicitly.
    std::cout << "Hello, world!\n";

// ── 4) Return a success status to the host environment.
//    • 0 conventionally means success; non-zero indicates failure.
//    • Portable names exist in <cstdlib>: EXIT_SUCCESS / EXIT_FAILURE.
//    • On return, standard streams are flushed; automatic objects in this scope
//      are destroyed before control leaves main.
    return 0;
} // ── 5) End of block: locals (if any) would be destroyed here; the runtime then
  //    records your int as the process exit code and proceeds with teardown.

<Callout type="info" title="Runtime & Toolchain Footnotes">
**A) What runs before main?**
• The real entry point is in the C runtime (CRT), often named _start (platform-specific).
• CRT sets up the process, stack, TLS, argc/argv/environment.
• Static/global objects with dynamic initialization (C++) are constructed before main.
• Then CRT calls your main(...).

**B) What happens after main returns (or throws)?**
• The CRT captures your int and uses it as the process exit status.
• Static/global objects are destroyed in reverse initialization order.
• If an exception escapes main, std::terminate() is called (usually aborts).

**C) Build pipeline even for this tiny file:**
<Steps>
  <Step title="Preprocess">Expands #include/macros into one translation unit.</Step>
  <Step title="Compile">Generates object code (.o/.obj), instantiates needed templates.</Step>
  <Step title="Link">Ties objects with C/C++ runtimes and the standard library (libstdc++/libc++).</Step>
</Steps>

**D) Why avoid `using namespace std;` (especially in headers)?**
• It injects many names into the global namespace → collisions, ODR headaches.
• Prefer qualified names (`std::cout`) or targeted using-declarations in small scopes.

**E) Buffering gotchas you will meet:**
• Missing '\n' → line may not appear until buffer flush or program exit.
• Mixing stdio and iostreams → keep sync ON (default) or flush manually.
• Turning sync OFF for speed (`std::ios::sync_with_stdio(false); std::cin.tie(nullptr);`)
  improves throughput but then don't interleave printf/cout unless you manage flushing.
</Callout>

Have a question or feedback?

I’d love to hear from you.