Skip to content
Topos Uranos

Summary

This lesson is the opening unit of the course and sets out its complete syllabus. It states the purpose of the course, which is programming itself and not a language; it explains why its fundamentals are taught in C++, a language that does not hide the machine, and not in one with a gentler learning curve; it identifies the students for whom it is intended, the little it takes for granted and the courses to which it leads; it states the seven global learning objectives and the units that contribute to each one; it describes its eight content units and the lessons that make them up; it estimates the total workload the course requires, its distribution by unit, a suggested pace of study and its credits; it explains how its lessons are built, and, finally, it gives advice on how to study it, on the use of artificial intelligence assistants and on version control.

Learning objectives

  1. Describe the sequence of the eight units of the course and explain what each one contributes to the following ones.
  2. State the seven global learning objectives of the course and recognize which units contribute to each one.
  3. Explain with an example why the course teaches the fundamentals of programming in C++ and not in a language that hides types and memory.
  4. Calculate, from the estimated hours per unit, the workload required by the course or by a part of it, and use it to plan a personal pace of study.

Purpose

The course teaches how to write correct programs from scratch: how to turn a problem stated in words into a precise sequence of instructions, how to choose the representation of the data, how to govern the order in which what is written is executed and how to organize the code into functions with a clear interface. If this is so, its object is not a language, but programming itself: devising an algorithm, representing the data, decomposing a problem and reasoning about the correctness and the cost of what is written. That is why it is called “Fundamentals of Programming,” and that is why each construct of C++ is presented together with the problem it solves and with the way to reason about it, and never as a catalog of syntax.

C++ is, therefore, the instrument of the course and not its end. Now, an instrument is not indifferent to what is learned with it, and the choice of this one, which at first sight seems the most difficult, is deliberate; the next section justifies it. It is worth anticipating, however, its most visible consequence: each unit synthesis contains a fixed section, “What holds in any language,” which separates what has been learned from its form in C++ and says how it is expressed, or what changes, in other languages. It follows that the student who finishes the course will not only know how to write in C++, but will also recognize in any language the same ideas in different clothing.

Why C++ and not a friendlier language

There are languages with a gentler learning curve, such as Python, in which a first useful program is written in a few lines, without declaring types, without compiling and without asking where a piece of data lives. If the purpose were to write programs that work as soon as possible, that would be the path. The purpose of this course, by contrast, is to understand what a program does, and for that a language that does not hide the machine is preferable. C++ makes one see six things that more comfortable languages resolve silently:

  • Types. Each piece of data is declared with a type, which fixes what values it can take, how much space it occupies and what operations it admits; the compiler rejects what does not match before running anything.
  • Compilation. Writing, compiling, linking and running are distinct moments, and each error belongs to one of them; knowing which one is half of fixing it.
  • Memory. A variable is a named cell, with a lifetime and a scope; a reference is another name for the same cell, and a reference to a cell that no longer exists is a real error.
  • The cost of copying. Passing a vector to a function can copy it whole; choosing between value and reference is a decision of the programmer, not of the machine.
  • The limits of numbers. A 32-bit integer does not go beyond 2 147 483 6472\,147\,483\,647, and a floating-point number does not store 0.10.1 exactly.
  • Undefined behavior. What the standard does not define (going outside an array, overflowing a signed integer, reading an uninitialized variable) does not produce a guaranteed error, but anything at all; that is why the course always compiles with warnings and with sanitizers.

The following example shows it on a small scale. The program computes the factorials from 11 to 1515 in a variable of type int. In Python, whose integers grow without limit, the same computation would print without complaint 13!=6 227 020 80013! = 6\,227\,020\,800; in C++, the product of 12!12! by 1313 does not fit in an int, and the undefined behavior sanitizer, with which the course compiles all its examples, stops the program at the exact line:

C++ program
#include <print>​int main(){    int factorial{1};    for (int n{1}; n <= 15; ++n) {        factorial = factorial * n;   // does the product fit in an int?        std::println("{}! = {}", n, factorial);    }}
Output
1! = 12! = 23! = 64! = 245! = 1206! = 7207! = 50408! = 403209! = 36288010! = 362880011! = 3991680012! = 479001600main.cpp:7:31: runtime error: signed integer overflow: 479001600 * 13 cannot be represented in type 'int'

The program exited with code 1.

If this is so, the language has forced one to ask a question that the other would have hidden: how much fits in the chosen type and what to do when it does not fit. Neither of the two answers is better in the abstract; what matters is knowing that the question exists. The lesson on integers and their representation answers it, and the section on undefined behavior in each synthesis returns to it.

It follows that there is an asymmetry, which is the underlying reason for the choice. Whoever learns the fundamentals in C++ understands later what the more comfortable languages do for them: the garbage collector, unlimited integers, index checking, the silent conversion of types. Whoever learns first in one of those, by contrast, leaves those layers unseen, and stumbles on them later, when a real program fails for one of the causes that the language had hidden. Therefore, the path the course chooses is more demanding at the start, but more transferable: what is learned here holds in any language, and what is learned in a language that hides it does not always hold here.

Now, C++ is a large language, and a course on fundamentals must resist the temptation to teach all of it. This first course is limited to structured programming (data, expressions, control flow, sequences, text and functions) in the C++23 standard, with its modern forms: output with std::println, sequences with std::array and std::vector, text with std::string, the library algorithms in their ranges form. It leaves for Programming in C++ II what requires that foundation: pointers and dynamic memory, classes with methods, custom templates, anonymous functions and exceptions.

Intended students and prior knowledge

The course is intended for anyone who has never programmed, from any program of study and from the first year: computer science students who need a firm foundation before the courses on data structures and on systems, engineering and science students who need to compute with a computer without trusting it blindly, and mathematics students who want to see their algorithms turned into programs that run.

It is a level 1 course, with no prerequisites and self-contained. It takes for granted only two things: school arithmetic, with integer division and its remainder, and the elementary use of a computer (creating a folder, saving a file, opening a program). What it uses of binary representation, of reasoning by cases, of the logic of conditions or of induction it teaches at the point where it needs it: the binary system, in the lesson on integers; De Morgan’s laws, in the lesson on relations and logic; induction, in the lesson on recursion. The references to other courses on the site that appear in the lessons are, therefore, ways to go deeper, not requirements.

Where it leads

From the above follows the place of the course on the site. It has no prerequisites and is a goal of two programs of study, Informatics and Computing, and Engineering, because in both, programming is at once tool and object. It is advisable, therefore, for it to be one of the first courses of those programs, together with LaTeX for Academic Writing and Mathematical Logic I.

It has a complementary course, which can be followed before, in parallel or afterward. Mathematical Logic I studies as theory what is used here as practice: the compound conditions, the truth tables and De Morgan’s laws of unit 3, and the idea of proving that a procedure is correct, which in this course takes the form of the invariant of a loop and of induction on a recursion. Algebra and Geometry I, for its part, proves the results about numbers that the course turns into programs: Euclidean division, the Euclidean algorithm, numeral systems and Horner’s rule. And LaTeX for Academic Writing teaches, in its lesson Writing as a team, version control with Git, which this course recommends using from the first program.

Now, the course deliberately leaves out what requires its foundation. Its planned continuation is Programming in C++ II, which will deal with pointers and dynamic memory, classes and objects, templates, anonymous functions, exceptions and the standard library in depth. Hence the course is not the first stretch of a C++ manual, but a complete course on fundamentals, which can end here; whoever continues, on the other hand, will find in course II the same discipline applied to programs whose size no longer allows doing without it.

Global learning objectives

The following objectives describe what a student who completes the course should be able to do. Each is formulated as an observable performance, so that it can be checked with a concrete task; each content lesson, in turn, states its own objectives, which serve one or more of the global ones, and the closing lesson shows in which units and lessons each one is worked on. The seven objectives are numbered so that they can be cited.

  1. Objective 1. Solve problems with programs: specify what goes in and what comes out, design an algorithm, trace it by hand, and write, compile, run and debug it, interpreting the messages and the warnings of the compiler.
  2. Objective 2. Choose the appropriate type for each piece of data and reason about the limits of its representation: the range of integers, overflow, floating-point rounding and text encoding.
  3. Objective 3. Build correct expressions, knowing precedence, type conversions, side effects and what the standard leaves undefined.
  4. Objective 4. Design control flow with correct decisions and loops, which terminate, and justify their correctness with an invariant.
  5. Objective 5. Decompose a problem into functions with clear interfaces, choose how to pass the parameters, and solve problems by recursion.
  6. Objective 6. Represent and process compound data (sequences, tables, records, text and files) with the basic algorithms and with the standard library, estimating their cost.
  7. Objective 7. Organize larger programs into several files, specify them with a precondition and a postcondition, test them systematically and argue for their correctness.

Five objectives have their own unit: objective 2, the one on data and types; objective 3, the one on expressions; objective 4, the one on control flow; objective 5, the one on functions; and objective 6, the one on compound data, which extends into the one on text and files, where objective 2 reappears with UTF-8 encoding. The other two, by contrast, run through the whole course. Objective 1 has as its main unit the first, which teaches the cycle of specifying, writing, compiling and running, along with the lesson on debugging in the last; but it is exercised in all of them, because every program in the course is compiled, run and corrected. Objective 7 has as its main unit the last, the one on larger programs; but it begins in the style guide of the first, continues in the constants of the second, and takes shape in the contracts of the fifth, which from then on accompany every function, and in the invariants of the fourth and the sixth. It follows that their achievement cannot be attributed to a single unit, and that it is advisable to cultivate them throughout the whole course.

Contents

The course is organized in ten units: this overview, eight content units and a closing. Each content unit opens with an introduction lesson, which presents its lessons, what it takes for granted and the problem it solves, and begins with an entry assessment; it closes with a synthesis lesson, which reconstructs the chain of concepts of the unit, separates what holds in any language from its form in C++, gathers the undefined behavior of the unit, repeats in an exit assessment the skills of the entry assessment and proposes challenges of greater difficulty than the problems of the lessons.

Unit 0. Course overview

It consists of this lesson, which sets out the purpose, the choice of language, the global objectives, the contents, the workload and the design of the course.

Unit 1. First steps

Its objective is to take the student from a problem stated in words to a program that compiles, runs, reads data and writes results, knowing what each tool did along the way. It begins without code, because the algorithm precedes the program; at the end, the student still knows little C++, but knows how to work with it.

  1. Introduction: first steps.
  2. From the problem to the algorithm: the specification of inputs and outputs, numbered steps, tracing by hand, sequence, decision and repetition.
  3. Programs, compilers and C++: preprocessing, compilation and linking; the compiled versus the interpreted; the standards of the language.
  4. Installing the environment on Windows, macOS and Linux: the three complete guides, the compiler, Visual Studio Code and its C++ extension, verification and frequent failures.
  5. Compiling, running and configuring: the course options, errors and warnings, sanitizers, the build task of the editor and the encoding of the console.
  6. Anatomy of a program: inclusion, main, statements, blocks, comments and the style guide of the course.
  7. Input, output and errors: std::cin, std::println and format specifications, malformed input and the classification of errors.
  8. Synthesis and challenges: first steps.

Unit 2. Data and types

Its objective is to teach that every piece of data lives in memory with a type that fixes what values it can take, how much space it occupies and what operations it admits, and that a number in a program is not a number of mathematics, but a representation with limits.

  1. Introduction: data and types.
  2. Variables and memory: declaration and brace initialization, assignment, tracing the state, names and the uninitialized variable.
  3. Integers and their representation: bases 2 and 16, the range of each type, two’s complement and signed and unsigned overflow.
  4. Floating-point numbers: why 0.10.1 is not exact, accumulated rounding, comparison with a tolerance, infinity and the not-a-number value.
  5. Characters, Booleans, conversions and constants: the code of a character, implicit and explicit conversions, the rejection of conversions that lose information, const, constexpr and auto.
  6. Synthesis and challenges: data and types.

Unit 3. Expressions and operators

Its objective is to teach how to build expressions whose value can be predicted with certainty: which operation is done first, what type the result has, what side effects it has and which expressions lack meaning. The unit does not yet have decisions or loops: conditions are evaluated and printed.

  1. Introduction: expressions and operators.
  2. Arithmetic: quotient and remainder with negatives, the tree of an expression, precedence, associativity and the type of the result.
  3. Relations and logic: compound conditions, their negation by De Morgan, short-circuit evaluation and the error of 1 < x < 5.
  4. Assignment, side effects and undefined behavior: the value and the effect of an expression, increments, the order of evaluation and the catalog of what the standard does not define.
  5. Bits and the math library: bitwise operators, shifts, masks, <cmath> and the constants of <numbers>.
  6. Synthesis and challenges: expressions and operators.

Unit 4. Control flow

Its objective is to teach how to govern the order of execution with decisions and loops, and how to reason about their correctness: that each case is handled, that each loop does what it must and that it terminates. It carries out what the first lesson of the course announced: decision and repetition, now written and verified.

  1. Introduction: control flow.
  2. Decisions: chained and nested if and else, disjoint and exhaustive cases, switch with enum class and its pitfalls.
  3. Loops: while, do and for, the trace of a loop, the conversion between its forms and nested loops.
  4. Designing correct loops: the counter, accumulator, maximum and sentinel patterns, the invariant, termination and a first contact with the debugger.
  5. Numerical algorithms: primality, the Euclidean algorithm, change of base, Horner’s rule and bisection, each with its justification.
  6. Synthesis and challenges: control flow.

Unit 5. Functions

Its objective is to teach how to decompose a problem into named parts, each with an interface and a contract, and how to solve problems by recursion. It turns the long programs of unit 4 into programs composed of parts that are verified one by one.

  1. Introduction: functions.
  2. Functions: declaration, definition and call, the call stack, the precondition and the postcondition, and the function that returns no value.
  3. Parameters and scope: pass by value, by reference and by const reference, multiple results with structured bindings, the scope and the storage duration of variables.
  4. Designing with functions: stepwise refinement, the interface before the body, the rule “one function, one task” and overloading.
  5. Recursion: the base case and the recursive case, correctness and termination by induction, the stack and the cost of recursion.
  6. Synthesis and challenges: functions.

Unit 6. Compound data

Its objective is to teach how to represent collections of data and how to process them with the basic algorithms, written by hand and taken from the library, estimating how much they cost. It leads from the isolated piece of data to the collection, and from the question “is it correct?” to the question “how much does it cost?”

  1. Introduction: compound data.
  2. Arrays: std::array, traversal by index and by range, the index within bounds and C arrays as a contrast.
  3. Vectors: std::vector, reading a series of unknown length, accumulation, filtering and transformation, size and capacity.
  4. The cost of an algorithm: counting the dominant operations, orders of growth and measuring time.
  5. Searching and sorting: linear search and binary search, selection sort and insertion sort with their invariants, and the library algorithms.
  6. Tables: two-dimensional tables, their traversals by rows and by columns, the transpose, the product and the table in a flat vector.
  7. Records: struct, designated initializers, records as parameters and sorting by a field.
  8. Synthesis and challenges: compound data.

Unit 7. Text and files

Its objective is to teach how to handle text as data and how to make a program read and write files without breaking on malformed input. At the end, the student can write programs that process real data, accented letters and errors included.

  1. Introduction: text and files.
  2. Strings: std::string and its operations, npos, std::string_view views and C strings as a contrast.
  3. Text is not ASCII: UTF-8 encoding, bytes and characters, and the operations that break text.
  4. Reading well: std::getline, the stream state, recovery after invalid input and validation with std::from_chars.
  5. Text files: opening, reading line by line, writing, appending, and processing a file of comma-separated values.
  6. Synthesis and challenges: text and files.

Unit 8. Larger programs

Its objective is to teach how to organize, verify and debug programs that no longer fit in one file or in one’s head. It closes the circle opened by the first lesson: specify, build, verify; its synthesis ends with a final project that brings together the whole course.

  1. Introduction: larger programs.
  2. Multiple files and custom libraries: the header and the implementation, include guards, the one-definition rule and separate compilation.
  3. Testing: test cases derived from the specification, automated tests, assert and static_assert.
  4. Debugging: the Visual Studio Code debugger, breakpoints, the call stack and a systematic strategy for locating an error.
  5. Synthesis and challenges: larger programs.

Unit 9. Course closing

It consists of the lesson Final review of Programming in C++ I, which recalls the seven global objectives, shows in a figure in which units and lessons each one is worked on, gathers by objective the first attempt at the exit assessments and points to where study continues.

Workload and credits

The workload of a course is not measured by contact hours, which do not exist in an asynchronous course, but by the student’s total work: reading, writing and compiling the examples, solving problems and reviewing. The estimate follows the same method in every course on the site: it counts the reading time of the text of each lesson, including the code of its examples, at one hundred words per minute, twenty minutes per solved problem, thirty-five per proposed problem, forty-five per challenge and twelve per entry or exit assessment, and adds a margin of one tenth for review.

Results

The lessons of the course contain in all 105105 solved problems, 105105 proposed problems and 5252 challenges, as well as sixteen unit assessments and seventy short exercises that the site checks instantly. The estimated workload per unit, rounded to the hour, is as follows:

  • Unit 0, course overview: 11 hour.
  • Unit 1, first steps: 2828 hours.
  • Unit 2, data and types: 2222 hours.
  • Unit 3, expressions and operators: 2222 hours.
  • Unit 4, control flow: 2222 hours.
  • Unit 5, functions: 2222 hours.
  • Unit 6, compound data: 3030 hours.
  • Unit 7, text and files: 2121 hours.
  • Unit 8, larger programs: 2020 hours.
  • Unit 9, course closing: less than 11 hour.

The total is about 188188 hours of student work. This figure should be read as what it is: an estimate for a student who writes and compiles all the examples and solves all the problems and challenges. Someone who has already programmed in another language will spend considerably less time on the first units; someone who has never installed a program or read an error message may need more, above all in unit 1, which is also, with the one on compound data, the most extensive.

Now, the figure is nominal: it is the one that fixes the credits of the course, and it assumes reading at a constant pace and three quarters of an hour per challenge. Until it is measured with real students, it is advisable to accompany it with a realistic band. If reading advanced at half the assumed pace, as happens when each example is typed by hand, compiled and altered to see what changes, and each challenge required seventy-five minutes instead of forty-five, the same method would give about 250250 hours. Therefore, the expected workload lies between about 188188 and about 250250 hours. The upper end is a prudent bound, not a measurement; the band will be corrected when the time spent by those who study the course is known, and until then the credits are calculated with the nominal figure.

Suggested pace

To name the paces, this syllabus uses the same criterion as the other syllabi on the site: full-time study is that of an academic year of 6060 credits, about 16501650 hours spread over some forty weeks, that is, about 4040 hours a week, and half-time study is half of that, about 2020 hours a week. It follows that the recommended pace is a semester of sixteen weeks, with about 1212 hours of study a week, somewhat less than a third of full-time study; with the realistic band, from 1212 to 1616 hours a week. That semester can be shared with Mathematical Logic I, whose truth tables and whose idea of proof can be recognized in the conditions and in the invariants of this course.

An intensive pace, of about 2020 hours a week, that is, half-time study, completes the course in nine or ten weeks. A more relaxed pace, of about 66 hours a week, stretches it over two semesters, which divide it almost in half: units 0 to 4, from the algorithm to control flow, take about 9595 hours, and units 5 to 9, from functions to the final project, about 9393. The cut is natural: on finishing unit 4, the student already writes complete programs in a single main, and the second half teaches how to organize them.

Credits

The credits of this site measure the total workload that a course requires of the person studying it (reading, problems, challenges and review), not class hours. One credit is equivalent to about 27.527.5 hours of work, and a year of full-time study to 6060 credits, that is, to about 16501650 hours; this is the same criterion with which the preceding subsection names the paces of study.

Now, dividing the estimated 188188 hours by 27.527.5 gives 6.8, which is rounded to 77. If a band of 2525 to 3030 hours of work is allowed for the credit, the number CC of credits of the course is bounded between 6.3 and 7.5:

(18830)≤C≤(18825)\left( \frac{188}{30} \right) \leq C \leq \left( \frac{188}{25} \right)

Therefore, the course has 77 credits, somewhat more than a ninth of a year of full-time study. The figure is not an oversight: the course lasts as long as it must to teach the fundamentals, and it was not trimmed to fit a round number of credits.

Their use is practical: they make it possible to compare the workload of the courses on the site with one another and to decide how many to study at the same time without exceeding the time available. Someone who can devote half-time study, for example, to a sixteen-week semester, that is, about 320320 hours, can take on courses adding up to about 1212 credits; with full-time study over the same semester, about 2323.

These credits are a measure specific to this site, for planning study: they do not correspond to any official credit system, nor do they entail academic recognition by any institution.

Course design

Mode of study

The course is asynchronous, online and self-paced: it has no dates, live sessions or deadlines, and each student advances at the speed that their preparation and their time allow. It will be offered in the fourteen languages of the site. The identifiers in the programs are in English and are the same in all versions; the comments and the messages that the programs display, by contrast, are in the language of the page, so that each version is compiled and run with its own output. The lessons state which other lessons it is advisable to have studied first, so that the recommended order is explicit.

The structure of a lesson

All content lessons follow the same structure. They begin with a summary and with a list of their own learning objectives, formulated as observable performances. Then comes the exposition, whose core is the programs. Every complete program in the course was compiled, before the site was built, with the C++23 standard, with all warnings enabled and with the sanitizers for memory errors and for undefined behavior, and was run; what is published under the code is its real output, or the real messages of the compiler when what is being taught is an error or a warning, or the real report of the sanitizer when the program commits undefined behavior. If this is so, no output in the course is written by hand: the site checks, when it is built, that each program has stored the output of its current version, and it would not be published if one of them lacked it. Other figures animate step by step what the text reasons out: the memory cells with their name and their value, the call stack, the run of a loop, the bits of a number.

Each lesson closes with three solved problems, with their step-by-step solution, and three proposed problems, each with a folded hint and, after it, the answer, sometimes accompanied by a worked solution that is best opened only after having written one’s own. The problems demand an idea, never the mechanical repetition of an example, and use only what has been taught up to that lesson. Each lesson also includes two short exercises that the site checks instantly: in one, the exact output of a program is predicted, and in the other, a fragment is completed or corrected. It is worth knowing what that check does and does not do: the site does not compile the student’s code, but checks with patterns that the essentials are present and that the targeted error has disappeared. Therefore, an exercise passed on the site must still be compiled and run on one’s own computer, which is the only definitive judge.

Introduction and synthesis lessons have a structure of their own. The introduction presents the unit, its lessons and the thread that unites them, what it takes for granted and the problem it solves, and contains the entry assessment. The synthesis reconstructs the chain of concepts of the unit, with the lesson in which each link is established and what each one uses; it contains two fixed sections, “What holds in any language” and “The undefined behavior of the unit,” the exit assessment and six challenges (ten in the last unit, whose final project is broken down into stages), each with a hint, an answer and, when the reasoning warrants it, a folded worked solution.

The voice reader

Each lesson can be listened to aloud with the reader of the site, which uses the voices of the device itself: the text is read as it is written, the code quoted in it is pronounced meaningfully and the code blocks are announced, without being read character by character. This makes it possible to review the exposition away from the screen; the programs, by contrast, always call for the editor.

Assessment

The course does not grade: its assessment is formative and rests on self-assessment. The proposed problems make it possible to check the achievement of the objectives of each lesson, and the challenges, that of the objectives of each unit; in all of them, the final judge is the compiler, with its warnings and its sanitizers, because a program that compiles without warnings, passes its test cases and wakes no sanitizer is evidence that admits no dispute, although, as the lesson on testing teaches, not a proof.

To this are added the unit assessments. The entry assessment, in the introduction, is a short self-assessment that measures the starting point; the exit assessment, in the synthesis and before the challenges, measures what has been achieved and is compared with the entry assessment. Both are corrected in the browser itself: each answer receives an explanation, each typical error a remark that identifies it, and each question refers to the lesson worth reviewing. Neither of them grades or certifies, and their results are stored only in the student’s browser; the validation of the skills acquired will be carried out later on another platform. The final review gathers the first attempt at each exit assessment into a profile by global objective.

Artificial intelligence assistants

There are today artificial intelligence assistants capable of writing in seconds the solution to almost any problem in this course. It is worth saying clearly what place belongs to them, and the reason is the same one that justifies everything else: the course does not train the production of programs, but a capacity, that of thinking a problem through until it becomes a correct program. If this is so, delegating the exercise to an assistant cancels the exercise, in the same way that looking at the answer to a problem before attempting it turns it into reading. The resulting program may be correct; the person who submits it, however, has not learned to write it, and will not know how to do so when the assistant is wrong, which is precisely when it is most needed.

It follows that there is a rule of use, not a prohibition. An assistant serves to explain an error message that one does not understand, to propose test cases that one had not thought of, to compare one’s own finished solution with another, or to clarify an idea of the exposition from another angle. It does not serve to write the exercises, the problems or the challenges. And in every case a principle holds that applies to any code written by others, whether it comes from an assistant, a forum or a classmate: it is read, tested and understood before it is accepted. An assistant can produce code that compiles and seems correct, but that commits undefined behavior or uses an old form of the language; the student who has done the course will be able to recognize it, and the one who has delegated the course will not.

How to study this course

The course is designed to be studied in order. Each unit presupposes the previous ones: expressions are written with the types of the second unit, loops with the expressions of the third, functions encapsulate the loops of the fourth, and compound data are processed with functions; skipping a lesson forces one, sooner or later, to come back to it.

The main rule is a single one: write and compile every example. Programming is not learned by reading, but by writing, compiling and reading what the compiler replies. That is why the student installs their own environment following the lesson Installing the environment on Windows, macOS and Linux, and reproduces each program in their own Visual Studio Code, with the options that the lesson Compiling, running and configuring teaches how to set; whoever uses a computer on which they cannot install programs has, provisionally, an online compiler, which the installation lesson describes. It is advisable to type the examples by hand instead of copying them, because the hand retains what the eye overlooks; it is advisable to predict the output of each program before running it, because the difference between what was predicted and what was obtained is exactly what remains to be learned; and it is advisable to read each warning of the compiler as a question that deserves an answer, and not as noise to be tolerated. After a program works, it is also advisable to alter it on purpose (delete a semicolon, change a type, go outside a vector) and read what the compiler or the sanitizer says, because each provoked error is an error that will be recognized later.

Each unit has two checkpoints. The entry assessment, in the introduction, is the starting point: it is advisable to answer it before studying the unit, without consulting anything, and to note the lessons to which the errors refer. The exit assessment, in the synthesis and before the challenges, is the contrast: it is answered on finishing the lessons, also without consulting anything, and compared with the entry assessment. In both, what counts is the first attempt, which is the one that is compared and the one that the profile of the final review gathers. In the proposed problems and in the challenges, it is advisable to open the hint only when the attempt has been exhausted, and the answer only at the end, to check. A problem whose answer was consulted before compiling one’s own has not been solved, but read.

Version control

Finally, it is advisable to keep the work of the course in a version control system from the first program. Git records each state of a working folder, makes it possible to go back to any of them and shows what changed between two; for someone who programs, this means being able to experiment without fear of losing what worked, and to locate, by comparing versions, the change that introduced an error, which brings to the history of the program the bisection taught by the lesson on debugging. The course does not teach Git, because another course on the site already does: the lesson Writing as a team, from the course LaTeX for Academic Writing, explains the minimal workflow, which files are kept and which are excluded, and how a conflict is resolved. What holds there for the sources of a document holds equally for those of a program; it suffices to exclude, instead of the auxiliary files of LaTeX, the executables and the object files that compilation regenerates.