Chapter 02 of 24 | C++

Your first program

Make a source file named hello.cpp. A minimal complete program can display a greeting and return a success status:

#include <iostream>

int main()
{
    std::cout << "Hello, C++23!\n";
    return 0;
}

#include <iostream> makes the stream declarations available. int main() declares the program's entry point. The braces form a block, and std::cout writes to standard output. The insertion operator << sends text to the stream; \n ends the line. Returning zero conventionally reports success.

Read the program one line at a time

The #include line is a preprocessor directive, not an ordinary C++ statement. It makes declarations from the standard input/output header available in this translation unit. Without it, the compiler has not been told what std::cout means.

The declaration int main() says that the program's entry-point function returns an integer and takes no parameters. The operating system starts the program by arranging to call main. The body between braces contains the statements that run. Each complete expression statement ends with a semicolon; the semicolon is part of the syntax, not decoration.

In std::cout, std is the namespace that contains standard-library names and cout is the output stream. The :: operator qualifies a name with its namespace. Writing std::cout makes the origin of the name explicit and avoids accidentally colliding with another identifier named cout.

The string literal between quotation marks is text known at compile time. The escape sequence \n represents a newline character. It differs from writing std::endl, which also flushes the stream. Flushing after every line can be unnecessary work; use a newline for ordinary output and flush only when the program needs output to appear immediately.

What happens when the command runs?

The compiler command reads your source and attempts to produce an executable. A successful compiler exit code means the build completed, but it says nothing about whether the program prints the intended message. Running the executable is a separate step. If compilation fails, do not assume there is a new executable: an old one may still be present, which can make it look as though the changed code ran.

Command prompts use different syntax for launching a program from the current directory. On some shells, the executable name is sufficient; on others, you must write a relative path such as ./hello or specify the Windows executable name. Compiler and shell commands are tools around the C++ language, so consult your environment's instructions when the same command does not work everywhere.

Useful first debugging habits

If you see "expected ';'" or a similar syntax message, inspect the indicated line and the one before it. Compilers often notice a missing punctuation mark only when they reach the next token. If the compiler says a name is undeclared, verify spelling, capitalization, included headers, and whether the name is in a namespace. C++ identifiers are case-sensitive: Message and message are different names.

Make one edit at a time and rebuild. This gives you evidence about which change caused a new result. When a program behaves unexpectedly, add a temporary output that displays the values used in the calculation, or use a debugger to inspect them. Remove diagnostic output once you understand the cause.

Compile and run

With a recent GCC or Clang, a useful starting command is:

g++ -std=c++23 -Wall -Wextra -Wpedantic hello.cpp -o hello

Use clang++ instead of g++ when appropriate. On Windows, a GCC-based toolchain may produce hello.exe; with Visual C++, use its developer command prompt and the compiler's documented standard-mode option. Compiler options are tool-specific. If C++23 is not fully supported, use a supported mode for the features you are learning.

Read compiler messages carefully

A missing semicolon, misspelled identifier, or unmatched brace can produce several follow-on diagnostics. Start with the first useful error and inspect the indicated line and the line immediately before it. Warnings often reveal suspicious conversions or unused values even when a program technically compiles.

Small experiments

  1. Change the greeting, compile, and run again.
  2. Print two values in one statement, separated by a space.
  3. Remove the semicolon after the output statement, compile, and explain the diagnostic; then restore it.
  4. Try omitting return 0. In main, reaching the closing brace also reports success in modern C++.
  5. Print a short sentence on two lines. Try both a newline escape and two output statements.
  6. Change the program to print an integer after the greeting and predict the output before compiling.

Key idea: a complete C++ program has an entry point, declarations for the tools it uses, and statements grouped into blocks. Compile frequently so small mistakes stay easy to diagnose.

Build the example as a controlled experiment

Once the greeting works, change one feature at a time. First change a string literal, then add a value, then move the output into a function. Recompile after every change. This sequence creates a useful mental model of which declarations are required, how expressions flow into a stream, and which errors are caught before execution. If several changes are made at once, a diagnostic may point to a later symptom instead of the original mistake.

Compiler diagnostics are evidence, not a proof of the exact location of the mistake. A missing delimiter can make the parser misinterpret later lines. Read the first diagnostic that is plausibly connected to your edit, inspect the surrounding syntax, and avoid “fixing” every later message independently until the earliest error is resolved.

Process arguments and exit status

A command-line program can receive arguments through the two-parameter form of main: an argument count and an array of character pointers. Arguments are text, even when they represent numbers, so parse and validate them before use. The argument count includes the program name. Avoid assuming that a required argument exists before checking the count, and report usage in a form a new user can follow.

The integer returned from main becomes the process exit status. Zero conventionally means success; a nonzero value signals failure to a shell script, editor, or parent process. Use a consistent convention and send diagnostics to standard error so callers can distinguish errors from ordinary output.

Source form, encoding, and the machine

The source file contains characters that the compiler decodes before parsing C++ tokens. Source and execution character sets are related but not identical concepts, and terminal encoding is yet another layer. Plain English string literals work in a broad range of environments, but text with non-ASCII characters requires deliberate encoding choices when interoperability matters.

Likewise, the source expression int describes a language type, not a promised processor register. The compiler chooses a representation appropriate to the target, subject to the standard's guarantees. Learn the observable type properties through sizeof and the numeric-limits facilities rather than assuming a particular machine layout.

Advanced exercise

  1. Extend the greeting program to accept a name as a command-line argument and return a nonzero status when it is missing.
  2. Inspect the type and size of an integer on your target, then explain why the result is not a portable fixed-width guarantee.
  3. Build with debug information and step through main in a debugger. Compare source-level steps with the compiler's generated instructions.
0% read