Very fast intro to the basics of c++#
Although the c++ standard is really large, for this course we need to handle well a small subset of it. What is more important is to learn how to THINK like a programmer, not to memorize the actual syntax, which can actually obtained from google bard or chatGPT. In this section, we will learn the very basics and then we start practicing. Practice is the key in everything. For more details, see https://www.learncpp.com/ or a reference book.
Some good practices
Resources:
Minimal example#
This a minimal valid cpp program. It does “nothing”. But when you compile, a lot of things happen. For more info see https://hackingcpp.com/cpp/hello_world.html
int main(void) {
return 0;
}
Please write it in a file called minimal.cpp
. Now compile it and run it. Notice that now we are giving it a name for the executable:
g++ -std=c++17 minimal.cpp -o minimal.x
./minimal.x
Exercises
What happens if you remove the return statement?
After running the executable, check the status of the command in the shell by using the following instruction
echo $?
Look for some other standard library at https://en.cppreference.com/w/ . Choose one and try to describe, in general words, its use.
Variables#
See
https://www.learncpp.com/cpp-tutorial/introduction-to-objects-and-variables/
A variable in c++ is an object which has:
A datatype
A value
A name
A memory address
A scope
C++ is a typed lang. It means that you have to specify the type of the variable you are going to use. You have booleans, char, integers, float and double, arrays/vectors, structs, …. See https://hackingcpp.com/cpp/lang/fundamental_types.html
A variable in memory:
How to represent “reals”:
Variable types#
char, int, unsigned int, float, double, bool, … See:
https://hackingcpp.com/cpp/lang/fundamental_types.html
https://en.cppreference.com/w/cpp/language/types
Bugs: Gandhi the destroyer (unsigned char): https://www.youtube.com/watch?v=Iq_r7IcNmUk
Exercises#
Initialization#
Create a program that declares a variable of type
int
without initializing it.double
with initial value -9.87e-8.std::string
initialized with your first name .std::vector<double>
with 10 values not initialized.
# YOUR CODE HERE
raise NotImplementedError()
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
Cell In[1], line 2
1 # YOUR CODE HERE
----> 2 raise NotImplementedError()
NotImplementedError:
Python/C++ tutor#
Copy and visualize this program execution in the cpp python tutor.
Overflow and santizers#
Analyze the output of the following program:
#include <iostream>
int main(void) {
// con enteros
int b = -3000000000;
std::cout << "b = " << b << "\n";
int a = -2000000000;
std::cout << "a = " << a << "\n";
// con flotantes
double x = 3000000000;
std::cout << "x = " << x << "\n";
x = 3.24e307;
std::cout << "x = " << x << "\n";
x = 3.24e310;
std::cout << "x = " << x << "\n";
x = 3.24e-310;
std::cout << "x = " << x << "\n";
x = 3.24e-326;
std::cout << "x = " << x << "\n";
return 0;
}
Compile this program with sanitizers and run it again (-fsanitize=undefined
). To understand the output, look for an overflow/underflow error.
Operators#
Arithmetic#
Example: =, +, -, *, /, %, -=, +=, *=, /=, ++, --
int a = 10, b = 20, c = 0;
a = b; // assigns b value to a value
a = b + 60; // adds 60 to b value, then assigns that value to a
a += 60; // same as previous
a = 2*b; // multiplication. Here same as a *= b;
a = b/3; // division, notice truncation for integers. a /= 3;
a = b%7; // modulo operator, residue
a = 10;
b = 20;
a = b++; // post increment
a = 10;
b = 20;
a = ++b; // pre increment
Comparison#
You have ==, !=, >, >=, <, <=
int a = 10, b= 20;
bool result = true;
result = a == b; // is a value equal to b value
result = a != b; // is a value different than b value
result = a <= b; // is a value les or equal to b value
Logic#
You have &&, and, ||, or, !, not, &, |
int a = 10, b = 20;
bool result = false;
result = (a < b) and (3*a == b); // and , && , at value level
result = (a < b) or (3*a == b); // or, ||, at calue level
int c = a & b; // at bits value
Precedence#
Operators have a defined precedence: https://en.cppreference.com/w/cpp/language/operator_precedence
Exercises#
Arithmetic examples#
Write a program that shows examples for the arithmetic operators. Use a simple function (the main function must just call it).
Comparison examples#
Write a program that shows examples for the comparison operators. Use a simple function (the main function must just call it).
Logic operators#
Write a program that shows examples for the logic operators. Use a simple function (the main function must just call it).
Facebook genius#
What is the result of 6*2/3+1
? Make your prediction and then compute it.
Functions#
See: https://hackingcpp.com/cpp/lang/function_basics.html
Functions are of fundamental importance. They allow to encapsulate the implementation details and to create modular and more maintainable programs. Each function must do one thing well. Using functions also allows the program to be tested, profiled, and extended. Form now on, will keep clean the main
function, and use it to call other functions.
There are many more details about functions, like passing by value/reference, return types, overloading, templates, lambdas, …, but we will learn those details on the run. Please write the following code
//#include <iostream>
#include <print> // compile with -std=c++23
// function declaration: name and types
double average(double a, double b); // a and b are LOCAL variables
// main function. Execution starts here
int main(int argc, char **argv) {
double x{2.35};
double y{4.987};
double avg = average(x, y); // x value copied to a, y value copied to b, result of calling assigned to avg
//std::cout << "avg: " << avg << "\n";
std::print("avg: {0:20.16f}\n", avg);
return 0;
}
// function implementation: how it does that
double average(double a, double b)
{
return (a+b)/2;
}
Compile it and run it. From now on try to use functions all the time.
Exercises#
Write a program that prints the number of primes less than \(n\) for \(n \in [2, 1000]\). Plot it using either gnuplot or matplotlib
Conditionals#
This allows you to change the execution path according to a given condition. The general syntax is
if (condition) {
// do something if condition is true
} else {
// Do something if condition is false. The else is not mandatory
}
You can add also if ... else if ... else
as you want. Sometimes it would be more clear to use a switch
statement. The condition
can be as complex as you want.
if (condition 1) {
// perform action 1
} else if (condition2 ) {
// perform action 2
} else {
// perform action 3
}
%load_ext nb_js_diagrammers
%%mermaid_magic -h 250
graph LR
A[Start] -->B{Condition 1}
B -->|true| C[Action 1]
B -->|else if| D{Condition 2}
D -->|true| E[Action 2]
D -->|false| F[Action 3]
C --> X[End]
E --> X
F --> X
Example: Even/odd number (Please notice that the goal here is to learn if/else and the modulo operator, but the reading interactively is something not advisable)
#include <iostream>
void read_check_even_odd(void);
int main(int argc, char **argv)
{
read_check_even_odd();
return 0;
}
void read_check_even_odd(void)
{
int num = 0;
// solicitar el numero
std::cout << "Escriba un numero entero, por favor:\n";
// leer el numero
std::cin >> num;
std::cout << num << "\n";
// verificar que el numero es par o no
// imprimir
// si el numero es par, imprimir "el numero es par"
// si no, imprimir "el numero es impar"
if (num%2 == 0) {
std::cout << "El numero es par \n";
}
if (num%2 != 0) {
std::cout << "El numero es impar \n";
}
//else {
//cout << "El numero es impar" << endl;
//}
}
Compile and run it. As this is expecting to read from the terminal, run it in a real terminal, not on google collab.
Exercises#
You have to write a program that reads a number. If the number is smaller or equal than 10, verify if it is prime (you might need to write several if or if/else). If it is larger, write the message
Too large to verify manually
. To do so:Use https://draw.io to design the flow chart of the program
Write the program and test it with several inputs.
Write a program that computes the harmonic sum until some limit. Write, in the main function some tests for it.
Write a game to guess a number; Every time you guess the computer prints if the target is lrger ir smaller or prints the end of the game.
Loops#
See https://hackingcpp.com/cpp/lang/control_flow_basics.html
Classical loops#
for
loop#
Includes initialization, condition and step or change. Change must ensure reaching condition == false.
for (initialization; condition; change) { … }
// prints 0 1 2 3 4
for (int i = 0; i < 5; ++i) {
std::cout << i << ' ';
}
%%mermaid_magic -h 250
graph LR
A[Start] --> B[Initialization]
B -->|Condition| C{Condition true?}
C -->|Yes| D[Instructions]
D --> E[Change]
E -->|Condition| C
C -->|No| F[End]
while
loop#
Repeat while condition is true.
init;
while (condition) { … ; change; }
Example
int j = 5;
while (j < 10) {
std::cout << j << ' ';
++j;
}
do-while
loop#
Repeat while condition is true, BUT always do at least one step.
do { … } while (condition);
int j = 10;
do {
std::cout << j << ' ';
--j;
} while (j > 0);
Flow control#
You can also add a continue
or break
inside a loop if you need it. A continue
tells the program to ignore the lines in the loop behind it, and continue with the next iteration. A break
exist the loop.
Range based loops#
for (variable : range) { … }
Example: this loop prints all the elements from the vector, separated by a space. The limits and types are deduced automatically.
std::vector<int> v {1,2,3,4,5};
// print all elements of vector to console
for (auto x : v) { std::cout << x << ' '; }
Exercises#
Write a program that prints the numbers between 5 and 223 which are divisible by both 3 and 7. Print the numbers separated by a space, and a new line only at the end. Use the three types of classical loops (so your program must print the same three times)
Write a program that checks if a given number is prime.
Write a program that computes the sum of the multiples of 3 and 5 in the range \(1\le x \le 1123\).
Write a program that given a number, prints the sum of its digits
Write a program that checks if a number is palindrome
Functions again#
See: https://hackingcpp.com/cpp/lang/function_basics.html
Functions are of fundamental importance. They allow to encapsulate the implementation details and to create modular and more maintainable programs. Each function must do one thing well. Using functions also allows the program to be tested, profiled, and extended. Form now on, will keep clean the main
function, and use it to call other functions.
There are many more details about functions, like passing by value/reference, return types, overloading, templates, lambdas, …, but we will learn those details on the run.
Some very useful functions for us:
Math standard library: https://en.cppreference.com/w/cpp/header/cmath
Special functions: https://en.cppreference.com/w/cpp/numeric/special_functions
Pseudo random numbers: https://en.cppreference.com/w/cpp/numeric/random
Complex numbers: https://en.cppreference.com/w/cpp/numeric/complex
Valarray (to emulate math vectors): https://en.cppreference.com/w/cpp/numeric/valarray
Numerics algorithms: https://en.cppreference.com/w/cpp/numeric#Numeric_algorithms
Exercises#
Write different programs to solve the following (use functions extensively):
Create a function that receives an integers and return true if it is a prime, false otherwise. Use it in the main function to print the primes from 1 up to 150.
Compute the sum of the prime numbers between 500 and 1234.
Prints the cousin primes between 300 and 400.
Prints the sexy primes tuples between 300 and 450.
Prints the sexy primes triplets between 300 and 490.
Read https://hackingcpp.com/cpp/lang/command_line_arguments.html to be able to read command line arguments after processing
argc
andargv
. Rewrite your previous programs to read the limits from the command line. You can also check https://faculty.cs.niu.edu/~mcmahon/CS241/Notes/command_line_arguments.html
Workshop#
For all the following codes always modularize the files (separating the main function into another file) and write a test to be run with catch2. All parameters must be pased as command line parameters. Do not use cin
or similar.
Multiples of 3 and 5#
Write a program that reads a number from the command line and prints the number divisible by 3 and 5 smaller or equal than that number.
Square of *#
Write a program that reads a positive number from the command line and then prints a “square” with that number of asterisks. For example, ./a.out 5
must print
*****
* *
* *
* *
*****
Square of any char#
Now generalize the previous program and read both the number of characters and the character to be used.
Fixing loops#
What is wrong with the following codes?
for(;;) {
}
for(ii=0; ii>10; ++ii) {
}
For and while loops#
Transform the next loop into a
while
loop
for (int i=1; i <= n; i++) {
std::cout << i*i << " ";
}
Sum of squares and plot#
Write a program that reads a number from the command line and prints the sum of squares less than that number. Use it to plot the data.
Digits#
The program must read a number of four digits and print the digits, separated by a space. Some examples
./a.out 123
0 1 2 3
./a.out 2048
2 0 4 8
./a.out 3065
3 0 6 5
./a.out 10
0 0 1 0
./a.out 12345
Number has more than 4 digits. Ignoring
./a.out -1
Negative number. Ignoring
./a.out 0
0 0 0 0
Babilonian square root#
The babilonians found that to compute the square root of S, you can iterate the following expression \(x \leftarrow \dfrac{x+S/x}{2}\). Implement this algorithm in a function that receives the number of iterations to perform and the returns the estimation. Print in scientific notation with 15 decimals of precision using the following instructions in the main function
std::cout.setf(std::ios::scientific); // imprimir en notacipn cientifica
std::cout.precision(15); // imprimir con 15 cifras decimales
Some tests:
./a.out 0
0 1.000000000000000e+00
./a.out 2
2 1.416666666666667e+00
./a.out 4
4 1.414213562374690e+00
./a.out 6
6 1.414213562373095e+00
./a.out 8
8 1.414213562373095e+00
./a.out 10
10 1.414213562373095e+00
To print in that format you can also use a modern construct, std::format
, available in the c++23 standard. Check https://en.cppreference.com/w/cpp/io/print . Or , if it is installed, you can use https://fmt.dev/latest/index.html (also see https://hackingcpp.com/cpp/libs/fmt.html). Or if you prefer to be classic, you can use printf
.
Uncertainty#
Write a program that reads two floating-point numbers from the command line. One number represents the radius, and the other represents the uncertainty. Then, call a function that calculates and prints the area of the associated circle along with its propagated uncertainty. The area is calculated as , and the propagated uncertainty is \(2\pi r\Delta r\). If you do it correctly, the uncertainty must have only one significant figure, and it limits the number of decimals in the main quantity.
Primes again#
Check or improve your program to compute if a number is prime to be able to compute it with 10000019 or larger in a couple of seconds at most.
Largest prime smaller than number#
Write a program that reads a positive integer from the command line and prints the largest prime number that is less than or equal to the input number. The code should execute in less than 30 seconds, even for large numbers like 10000019 (a prime number) or 10000030 (not a prime number).
Some examples:
./a.out 2
2
./a.out 3
3
./a.out 7
7
./a.out 11
11
./a.out 12
11
./a.out 101
101
./a.out 617
617
./a.out 627
619
./a.out 5153
5153
./a.out 500167
500167
./a.out 8000033
8000033
./a.out 8000034
8000033
./a.out 80000069
80000069
./a.out 80000388
80000387
GCD#
Write a program that reads two positive integers from the command line and prints their greatest common divisor (GCD). Check the standard library for an immediate solution
Sum of primes#
Write a program that reads a positive integer from the command line and prints the sum of prime numbers that are less than the input number.
Collatz largest number#
Write a program that reads a positive integer from the command line and calculates the largest term obtained in the Collatz sequence for that number.
Collatz number of terms#
Write a program that reads a positive integer from the command line and calculates the number of terms in the Collatz sequence for that number.
Sexy primes smaller than number#
Write a program that reads a positive integer from the command line and prints the sexy prime numbers that are less than the input number. Two prime numbers are “sexy” if they are separated by 6 (e.g., 5 and 11, or 13 and 19).
Using the standar library for time#
https://www.cppstories.com/2024/chrono_dates_zones/
File modularization#
As you can see, the basic function is_prime
is used in all the problems, and we are repeating code per exercise. We can do better by modularizing the file, moving the utility functions to another file. Our structure would be something like:
primes.h
: file with DECLARATIONS of all the functions we think we will use int the future#pragma once bool is_prime(int n); void print_sexy_primes(int a, int b);
primes.cpp
: file with the IMPLEMENTATIONS of the functions declared inprimes.h
#include "primes.h" bool is_prime(int n) { // instructions } void print_sexy_primes(int a, int b) { // instructions to print the sexy primes in the specified range }
Notice that there is no
main
function.main_cousin.cpp
: The file where we solve this problem, and uses the primes utilities.#include <iostream> #include <string> #include "primes.h" int main(int arg, char **argv) { int minval = std::stoi(argv[1]); int maxval = std::stoi(argv[2]); print_sexy_numbers(minval, maxval); return 0; }
Notice how the
main
function now is so simple, and its intent is clear, although we do not know the implementation.Compilation:
g++ -std=c++17 primes.cpp main_cousin.cpp -o cousin.x
Execution
./cousin.x 200 400
Exercise Do this will all the functions previously written. So, include all the prime functions in the .h and .cpp files, and then create the corresponding main files.
Basic Testing#
Once we have modularized our code, not only using functions, but also files, it is possible to start testing our code so to be sure that we are not repeating old mistakes, etc. Here we will use catch2 to do so, with a simple example: the prime function. The idea is that, for each bug/problem you find, to implement a test case so to avoid repeating that in the future. The following is an example file
%%writefile test_primes.cpp
#include <catch2/catch_test_macros.hpp>
#include <cstdint>
#include "primes.h"
TEST_CASE( "Primes are computed", "[prime]" ) {
REQUIRE( is_prime( 1) == true );
REQUIRE( is_prime( 2) == true );
REQUIRE( is_prime( 3) == true );
REQUIRE( is_prime(10) == false );
}
Now compile as (we are using catch2 version 3 and it must be installed on the path)
g++ primes.cpp test_primes.cpp -l Catch2Main -l Catch2
and then run and you will get something like
./a.out
Randomness seeded to: 3750912043
===============================================================================
All tests passed (4 assertions in 1 test case)
if everything is fine.
Finally, you can automatize everything using a Makefile.
Classes and structs#
In C++ you can program using object oriented programming. In our case, we will use this to encapsulate data and some functionality. An example in our domain would be the simulation of some planets orbits. We can have an array of data for the x
positions, the y
positions, the z
positions, the velocities and so on. This can be overwhelming when indexing and processing. We might use a better computational model that resembles, better, the physical model. Our main object here is a planet. And a planet has several attributes, like its coordinates, its mass, radius, and so on. So it would be better if we can represent this as a new datatype, let’s calle it a Planet
. We can do it using structs or classes:
struct Planet {
// public attributes
std::valarray<double> R(3), V(3), F(3); // model position, velocity and force as vectors
double mass, radius;
// public functions
double speed() {
return std::sqrt((V*V).sum());
}
};
and then we can use it in the code as
...
Planet mercury, earth;
...
mercury.speed();
...
compute_force(earth, mercury);
...
You can also have private members using the keyword private
. Classes and structs in c++ are basically, but in a class everything is private by default, while for a struct everything is public by default.
As an exercise, create a struct or class to model a molecule.
We will be using structs/classes to create generalized data and functions, called functors, after overloading the ()
operator. So our class will represent a function with internal state, something very powerful.