C++ Module 1 – Start here: toolchains, compiling and first programs
C++ program structure, naming and formatting
Lay out a small C++ program in functions and blocks, pick one naming style, format code with clang-format, and see why using namespace std; causes clashes.
What you will learn
- Organise a small program into functions and blocks
- Apply consistent formatting with clang-format
- Explain why 'using namespace std;' must never appear in a header
Before you start
What you’ll learn · Before you start
What you will learn
- Organise a small program into functions and blocks
- Apply consistent formatting with clang-format
- Explain why 'using namespace std;' must never appear in a header
Before you start
On this page
A C++ program that others can read follows a few conventions: a file runs from its #include lines to main,
the work is split into small functions with names that say what they do, every block is indented the same way, and
names from std are written as std:: names. None of this changes what the compiler produces, but it decides how
quickly you, a teammate or an examiner can find a mistake. This lesson shows the usual layout of a small program, how
braces limit where a name exists, how to choose names, how clang-format formats a whole file in one command, and
why using namespace std; breaks a program that has a variable called count.
The parts of a small program
This program counts the fours and sixes in an innings, read ball by ball from standard input:
// Counts the fours and sixes in an innings, read ball by ball from standard input.
#include <iostream>
#include <print>
#include <vector>
constexpr int four_runs = 4;
constexpr int six_runs = 6;
std::vector<int> read_runs();
int count_boundaries(const std::vector<int>& runs);
int main() {
const std::vector<int> runs = read_runs();
std::println("{} balls, {} boundaries", runs.size(), count_boundaries(runs));
}
// Reads whole numbers until the input ends or holds something that is not a number.
std::vector<int> read_runs() {
std::vector<int> runs;
int run = 0;
while (std::cin >> run) {
runs.push_back(run);
}
return runs;
}
int count_boundaries(const std::vector<int>& runs) {
int boundaries = 0;
for (int run : runs) {
if (run == four_runs || run == six_runs) {
++boundaries;
}
}
return boundaries;
} from session import run
run("""
cat innings.txt
clang++ -std=c++23 -Wall -Wextra -Wpedantic boundaries.cpp -o boundaries && ./boundaries < innings.txt
g++-16 -std=c++23 -Wall -Wextra -Wpedantic boundaries.cpp -o boundaries && ./boundaries < innings.txt
""") Output
$ cat innings.txt 1 4 0 6 2 4 1 $ clang++ -std=c++23 -Wall -Wextra -Wpedantic boundaries.cpp -o boundaries && ./boundaries < innings.txt 7 balls, 3 boundaries $ g++-16 -std=c++23 -Wall -Wextra -Wpedantic boundaries.cpp -o boundaries && ./boundaries < innings.txt 7 balls, 3 boundaries
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 boundaries_session.py
With 1 4 0 6 and 2 4 1 as input it printed 7 balls, 3 boundaries. Read from the top, the file has six parts:
One file, top to bottom
Text description of the diagram
The diagram shows six numbered boxes, in two columns, in the order they appear in the file boundaries.cpp.
- A comment that says what the program does.
- The #include lines, one for each header the file uses.
- Named constants, such as constexpr int four_runs = 4;.
- Declarations of the helper functions, so that main can call them before their definitions.
- int main(), which reads as an outline of the whole program.
- The definitions of the helper functions.
- A comment on the first line says what the program is for.
- The
#includelines, one per header that the file uses, here sorted alphabetically. - Named constants replace bare numbers:
four_runssays what4means, andconstexprmakes it a constant the compiler knows at compile time. - Declarations such as
int count_boundaries(const std::vector<int>& runs);tell the compiler that a function exists, somaincan call it before its definition. mainstays short and reads like an outline: read the runs, count the boundaries, print the result.- The definitions of the helper functions follow, each doing one job.
Putting the helpers first and main last also works, and then the declarations are not needed. Pick one order for
a project and keep it. When a program grows past a few hundred lines, the declarations move into a header file, which
the lesson on headers covers.
Blocks and scope
A pair of braces makes a block, and a name declared inside a block exists only until its closing brace. The part of the program where a name can be used is its scope:
#include <iostream>
int main() {
int runs = 7;
if (runs > 6) {
int bonus = runs - 6;
std::cout << "Bonus: " << bonus << '\n';
}
std::cout << "Bonus again: " << bonus << '\n';
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra -c scope.cpp -o scope.o
g++-16 -std=c++23 -Wall -Wextra -c scope.cpp -o scope.o
""") Output
$ clang++ -std=c++23 -Wall -Wextra -c scope.cpp -o scope.o
scope.cpp:9:37: error: use of undeclared identifier 'bonus'
9 | std::cout << "Bonus again: " << bonus << '\n';
| ^~~~~
1 error generated.
[exit status 1]
$ g++-16 -std=c++23 -Wall -Wextra -c scope.cpp -o scope.o
scope.cpp: In function 'int main()':
scope.cpp:9:37: error: 'bonus' was not declared in this scope
9 | std::cout << "Bonus again: " << bonus << '\n';
| ^~~~~
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 scope_session.py
bonus is declared inside the if block, so on line 9, after the closing brace, it no longer exists. Clang said
use of undeclared identifier 'bonus' and GCC said 'bonus' was not declared in this scope. Declare a name in the
smallest block that needs it, and declare it outside the block when code after the block needs it too. Small scopes
keep names from being changed by code that has no business with them, and every function body is a block of its own.
Naming: one style, used everywhere
C++ itself does not prescribe a naming style, and well-known style guides disagree:
| Style | Types | Functions | Variables | Constants |
|---|---|---|---|---|
| The standard library | snake_case (std::string, std::vector) |
snake_case (push_back) |
snake_case |
snake_case |
| C++ Core Guidelines | Prefer underscore_style names (rule NL.10) |
|||
| Google C++ Style Guide | PascalCase |
PascalCase |
snake_case |
kPascalCase |
| LLVM Coding Standards | PascalCase |
camelCase |
PascalCase |
The Core Guidelines’ rule NL.8, “Use a consistent naming style”, matters more than which style you choose. This
track uses the standard library’s style, snake_case for everything, so its own names look like the library names
around them. Whatever the style:
- Name things for what they are:
boundaries, notb;read_runs, notfunc2. Short names such asiare fine for a loop counter that lives for three lines. - Name functions for what they do: a verb, such as
count_boundaries. - Never start a name with an underscore and a capital letter, and never use two underscores in a row. The standard reserves such names for the compiler and the library, and names that start with an underscore are reserved at global scope too.
Formatting with clang-format
Formatting by hand is slow and never quite consistent. clang-format rewrites the layout of a file to a chosen style, without touching what the code does. This file was typed with no care at all:
#include <vector>
#include <iostream>
int Total(const std::vector<int>&v){int t=0;
for(int x:v){ if(x>0)
t+=x;}
return t;}
int main( ) {
std::vector<int> runs {4,0,6,1, 2};
std::cout<<"Total: "<<Total(runs)<<'\n';
return 0;
} from session import run
# On macOS, xcrun runs the clang-format of the Xcode Command Line Tools.
run("""
xcrun clang-format --version
xcrun clang-format --style=llvm style/messy.cpp
xcrun clang-format --style=google style/messy.cpp
""") Output
$ xcrun clang-format --version
Apple clang-format version 21.0.0 (clang-2100.3.34.2)
$ xcrun clang-format --style=llvm style/messy.cpp
#include <iostream>
#include <vector>
int Total(const std::vector<int> &v) {
int t = 0;
for (int x : v) {
if (x > 0)
t += x;
}
return t;
}
int main() {
std::vector<int> runs{4, 0, 6, 1, 2};
std::cout << "Total: " << Total(runs) << '\n';
return 0;
}
$ xcrun clang-format --style=google style/messy.cpp
#include <iostream>
#include <vector>
int Total(const std::vector<int>& v) {
int t = 0;
for (int x : v) {
if (x > 0) t += x;
}
return t;
}
int main() {
std::vector<int> runs{4, 0, 6, 1, 2};
std::cout << "Total: " << Total(runs) << '\n';
return 0;
}
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 presets_session.py
clang-format --style=llvm messy.cpp printed the file in LLVM’s style, two-space indentation and 80 columns, with
const std::vector<int> &v and the body of the if on its own line. --style=google printed
const std::vector<int>& v and kept if (x > 0) t += x; on one line. Both sorted the two #include lines.
clang-format is part of the LLVM tools: install it with your compiler’s LLVM package or a package manager. On this
track’s Mac it came with the Xcode Command Line Tools, and xcrun clang-format runs it.
A project writes its choice down in a file named .clang-format (or _clang-format) in its top folder.
clang-format looks for that file in the folder of each source file and the folders above it, and editors that run
clang-format use it too. This is the style of this track’s examples:
# This track's formatting: LLVM's style with 4-space indentation, 100 columns, & next to the type,
# and a short if on one line when it has no else.
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100
PointerAlignment: Left
AllowShortIfStatementsOnASingleLine: WithoutElse BasedOnStyle: LLVM starts from LLVM’s rules and the other lines change four of them. With that file next to
messy.cpp, plain clang-format messy.cpp uses it:
from session import run
# With no --style, clang-format reads style/_clang-format, next to the file.
run("""
xcrun clang-format style/messy.cpp
xcrun clang-format --dry-run --Werror style/scores.cpp 2>&1 | grep -c "error:"
xcrun clang-format --dry-run --Werror style/scores_solution.cpp && echo "nothing to change"
""") Output
$ xcrun clang-format style/messy.cpp
#include <iostream>
#include <vector>
int Total(const std::vector<int>& v) {
int t = 0;
for (int x : v) {
if (x > 0) t += x;
}
return t;
}
int main() {
std::vector<int> runs{4, 0, 6, 1, 2};
std::cout << "Total: " << Total(runs) << '\n';
return 0;
}
$ xcrun clang-format --dry-run --Werror style/scores.cpp 2>&1 | grep -c "error:"
39
$ xcrun clang-format --dry-run --Werror style/scores_solution.cpp && echo "nothing to change"
nothing to change
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 track_style_session.py
clang-format fixed the layout but not the names: Total, t, v and x say nothing about the data, and no
formatter renames them for you. The session’s last two commands are checks: --dry-run --Werror found 39 places to
change in the exercise file scores.cpp further down, and nothing in its solution. Three commands cover daily use:
| Command | What it does |
|---|---|
clang-format -i main.cpp |
Rewrites the file in place |
clang-format --dry-run --Werror main.cpp |
Changes nothing, and fails with one error for each place that is not formatted; useful in CI |
clang-format --style=llvm --dump-config > .clang-format |
Writes a complete style file to start from |
Run the formatter before every commit, or let the editor run it on save, so that changes to a file show only real edits, never reformatted lines.
Text Diff Paste a file before and after clang-format to see every line it changed.using namespace std; and name clashes
using namespace std; makes every name of namespace std usable without std::. Short programs often start with
it, and it fails in a way that is hard to predict:
#include <iostream>
using namespace std;
int count = 0;
int main() {
for (int run : {4, 0, 6, 1}) {
if (run == 4 || run == 6) {
++count;
}
}
cout << "Boundaries: " << count << '\n';
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra -c count_clash.cpp -o count_clash.o
g++-16 -std=c++23 -Wall -Wextra -c count_clash.cpp -o count_clash.o
""") Output
$ clang++ -std=c++23 -Wall -Wextra -c count_clash.cpp -o count_clash.o
count_clash.cpp:9:15: error: reference to 'count' is ambiguous
9 | ++count;
| ^
count_clash.cpp:4:5: note: candidate found by name lookup is 'count'
4 | int count = 0;
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__algorithm/count.h:86:1: note: candidate found by name lookup is 'std::count'
86 | count(_InputIterator __first, _InputIterator __last, const _Tp& __value) {
| ^
count_clash.cpp:12:31: error: reference to 'count' is ambiguous
12 | cout << "Boundaries: " << count << '\n';
| ^
count_clash.cpp:4:5: note: candidate found by name lookup is 'count'
4 | int count = 0;
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__algorithm/count.h:86:1: note: candidate found by name lookup is 'std::count'
86 | count(_InputIterator __first, _InputIterator __last, const _Tp& __value) {
| ^
2 errors generated.
[exit status 1]
$ g++-16 -std=c++23 -Wall -Wextra -c count_clash.cpp -o count_clash.o
count_clash.cpp: In function 'int main()':
count_clash.cpp:9:15: error: reference to 'count' is ambiguous
9 | ++count;
| ^~~~~
• there are 2 candidates
In file included from /opt/homebrew/Cellar/gcc/16.2.0/include/c++/16/bits/unicode.h:39,
from /opt/homebrew/Cellar/gcc/16.2.0/include/c++/16/format:62,
from /opt/homebrew/Cellar/gcc/16.2.0/include/c++/16/ostream:45,
from /opt/homebrew/Cellar/gcc/16.2.0/include/c++/16/iostream:43,
from count_clash.cpp:1:
• candidate 1: 'template<class _IIter, class _Tp> constexpr typename std::iterator_traits<_Iterator>::difference_type std::count(_IIter, _IIter, const _Tp&)'
/opt/homebrew/Cellar/gcc/16.2.0/include/c++/16/bits/stl_algo.h:4046:5:
4046 | count(_InputIterator __first, _InputIterator __last, const _Tp& __value)
| ^~~~~
• candidate 2: 'int count'
count_clash.cpp:4:5:
4 | int count = 0;
| ^~~~~
count_clash.cpp:12:31: error: reference to 'count' is ambiguous
12 | cout << "Boundaries: " << count << '\n';
| ^~~~~
• there are 2 candidates
• candidate 1: 'template<class _IIter, class _Tp> constexpr typename std::iterator_traits<_Iterator>::difference_type std::count(_IIter, _IIter, const _Tp&)'
/opt/homebrew/Cellar/gcc/16.2.0/include/c++/16/bits/stl_algo.h:4046:5:
4046 | count(_InputIterator __first, _InputIterator __last, const _Tp& __value)
| ^~~~~
• candidate 2: 'int count'
count_clash.cpp:4:5:
4 | int count = 0;
| ^~~~~
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 count_session.py
Both compilers rejected line 9 with reference to 'count' is ambiguous: the program’s global count and the
standard library’s algorithm std::count are both visible, and the compiler cannot choose. This file includes only
<iostream>, yet std::count was there anyway, because both libraries’ <iostream> include parts of the
algorithms library: Clang’s note pointed into libc++’s __algorithm/count.h, and GCC’s into libstdc++’s
stl_algo.h. A program can compile today and break when the next version of a library includes one header more.
There are three fixes, and the first is the rule this track follows:
- Write
std::names:std::cout,std::vector. It costs five characters and never clashes. - Name the few things you use:
using std::cout;brings in only that one name. - Keep your own names specific:
boundary_countwould not have clashed even withusing namespace std;.
#include <iostream>
int boundary_count = 0;
int main() {
for (int run : {4, 0, 6, 1}) {
if (run == 4 || run == 6) {
++boundary_count;
}
}
std::cout << "Boundaries: " << boundary_count << '\n';
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra -Wpedantic count_fixed.cpp -o count_fixed && ./count_fixed
g++-16 -std=c++23 -Wall -Wextra -Wpedantic count_fixed.cpp -o count_fixed && ./count_fixed
""") Output
$ clang++ -std=c++23 -Wall -Wextra -Wpedantic count_fixed.cpp -o count_fixed && ./count_fixed Boundaries: 2 $ g++-16 -std=c++23 -Wall -Wextra -Wpedantic count_fixed.cpp -o count_fixed && ./count_fixed Boundaries: 2
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 count_fixed_session.py
It printed Boundaries: 2. In a header file, using namespace std; is never acceptable, because a header is
pasted into every file that includes it: the directive then applies to code whose authors never wrote it and cannot
undo it. The C++ Core Guidelines make this their rule SF.7, which rules out a global using namespace in any header,
and their rule SF.6 keeps it elsewhere for transition, for foundation libraries such as std, or for a local scope.
Google’s style guide forbids using-directives altogether, and LLVM’s says its code never uses using namespace std;.
Comments: why, not what
A comment that repeats the code goes stale and helps nobody: ++boundaries; // add one to boundaries. A useful
comment says what the code cannot: why it does something, what it assumes, or what the reader would otherwise get
wrong. The Core Guidelines make this two rules: NL.1 keeps out of comments what the code already says clearly, and
NL.2 asks comments to state the intent. boundaries.cpp has two comments of that kind, on its purpose and on when
read_runs stops reading. Good names make most other comments unnecessary.
Exercise: format and rename a program
This program works and prints 73.6667, but it breaks almost every rule of this lesson:
#include<iostream>
#include<vector>
using namespace std;
int CNT=0;
double AVG(vector<int> V){double S=0;for(int i=0;i<V.size();i++){S=S+V[i];}return S/V.size();}
int main(){vector<int> MarksList={72,85,64};
cout<<AVG(MarksList)<<endl;} Copy it, together with _clang-format, into a folder of your own. Then:
- Remove
using namespace std;and writestd::names. - Rename everything in the track’s style, remove the unused global, and take the vector as
const std::vector<int>&instead of copying it. - Run
clang-format -i scores.cpp, then check withclang-format --dry-run --Werror scores.cpp, which must print nothing.
The program must still print 73.6667, and it must build without warnings with
-std=c++23 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Werror.
One solution
#include <iostream>
#include <vector>
double average(const std::vector<int>& marks) {
double sum = 0;
for (int mark : marks) {
sum += mark;
}
return sum / static_cast<double>(marks.size());
}
int main() {
const std::vector<int> marks{72, 85, 64};
std::cout << average(marks) << '\n';
} from session import run
run("""
clang++ -std=c++23 style/scores.cpp -o scores && ./scores
clang++ -std=c++23 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Werror style/scores_solution.cpp -o scores_solution && ./scores_solution
g++-16 -std=c++23 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Werror style/scores_solution.cpp -o scores_solution && ./scores_solution
""") Output
$ clang++ -std=c++23 style/scores.cpp -o scores && ./scores 73.6667 $ clang++ -std=c++23 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Werror style/scores_solution.cpp -o scores_solution && ./scores_solution 73.6667 $ g++-16 -std=c++23 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Werror style/scores_solution.cpp -o scores_solution && ./scores_solution 73.6667
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 scores_session.py
It printed 73.6667, built without a warning under those flags with both compilers, and
clang-format --dry-run --Werror printed nothing for it. The range-based for also removed the comparison of an int
with size().
Check yourself
Check yourself
4 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
Interview questions
Why should a header never contain using namespace std;? (warm-up)
A header is copied into every file that includes it, so a using-directive in it applies to all of those files,
including ones written by people who never asked for it and cannot remove it. Every name of std then competes with
their own names: a global variable called count, distance or size becomes ambiguous with std::count,
std::distance or std::size, and the error appears in their code, far from the header that caused it. Which names
clash also depends on what the standard library’s headers happen to include, so code can break after a compiler
upgrade. Rule SF.7 of the C++ Core Guidelines rules out a global using-directive in a header for this reason. In a
header, write std:: names; in a source file, prefer them too, or a using std::name; declaration.
Key takeaways
- Lay out a file as: purpose comment, includes, constants, declarations, a short
main, then the helper functions. - A name declared in a block ends at the block’s closing brace; declare each name in the smallest scope that needs it.
- Use one naming style everywhere; this track uses the standard library’s
snake_case. Never begin a name with an underscore and a capital letter, or use a double underscore. - clang-format sets the layout from a
.clang-formatfile:-ito rewrite,--dry-run --Werrorto check in CI. It fixes layout, not names. using namespace std;made a globalcountambiguous withstd::countwith only<iostream>included; writestd::names, and never put the directive in a header.- Comments should say why, not repeat what the code says.
References
- ClangFormat (LLVM Project)
- C++ Core Guidelines (Standard C++ Foundation)
- Google C++ Style Guide (Google)
- LLVM Coding Standards (LLVM Project)
- Working Draft, Programming Languages: C++ (N4950) (ISO/IEC JTC1/SC22/WG21)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress