marp | math | theme | footer | paginate |
---|---|---|---|---|
true |
katex |
custom-theme |
true |
- How to create variables
- Fundamental types
- Naming variables
const
,constexpr
- References
📺 Watch the related YouTube video!
- 🎨 - Style recommendation
- 🎓 - Software design recommendation
- 😱 - Not a good practice! Avoid in real life!
- ✅ - Good practice!
- ❌ - Whatever is marked with this is wrong
- 🚨 - Alert! Important information!
- 💡 - Hint or a useful exercise
Style (🎨) and software design (🎓) recommendations mostly come from Google Style Sheet and the CppCoreGuidelines
Variable declaration follows one of the following patterns:
Type variable_name; // 😱 Uninitialized! Contains random value!
Type variable_name = value;
Type variable_name{value}; // Follows "uniform initialization"
Type variable_name{}; // "Value initialization"
Type variable_name(value); // 😱 Mostly not used nowadays
- Every variable has a type - one of the defining features of C++
- Variables, once declared cannot change their type
- Name must start with a letter
- C++ is case sensitive:
some_var
$\ne$ some_Var
$\ne$ someVar
- 🎨 Give variables meaningful names
- 🎨 Don't be afraid to use longer names
- 🎨 Name variables in
snake_case
(all lowercase, underscores separate words) - 🎨:x: Don't include type in the name
- 🎨:x: Don't use negation in the name
See Google naming rules for more details
Fundamental types in C++
void
type to represent "nothing" (stay tuned)- Integer types represent integer numbers. Different types cover different ranges of values
- Boolean type:
bool
to representtrue
orfalse
- Character type:
char
used to:- represent characters like
'a'
or'\n'
- represent raw memory (stay tuned)
- represent characters like
float
anddouble
are used to represent floating point numbers:float
- single precision floating point numberdouble
- double precision floating point number
char caret_return = '\n'; // Single character
int meaning_of_life = 42; // Integer number
short smaller_int{42}; // Short number (using {})
long bigger_int = 42L; // Long number (L is a literal)
float fraction = 0.01f; // Single precision float
double precise_num = 0.01; // Double precision float
double scientific = 2.42e-10; // Double precision float
auto some_int{42}; // Automatic type [int] (using {})
auto some_float = 13.0F; // Automatic type [float]
auto some_double = 13.0; // Automatic type [double]
bool this_is_fun = false; // Boolean: true or false
There are also unsigned
equivalents for integer types
unsigned int meaning_of_life = 42U; // Unsigned integer number
unsigned long bigger_int = 42UL; // Unsigned long number
auto number = 42'232'424ul; // Automatic type [unsigned long]
auto number_copy = number; // Automatic type [unsigned long]
unsigned long explicit_copy = number; // This works too!
- The difference is in the range of representable values
- We will talk about this later in the course
- We can get the range for any type
T
:std::numeric_limits<T>::min()
- minimum valuestd::numeric_limits<T>::max()
- maximum value- they live in the
<limits>
header
#include <limits>
#include <cstdio>
int main() {
std::printf("Min: %d, max: %d\n",
std::numeric_limits<int>::min(),
std::numeric_limits<int>::max());
return 0;
}
💡 Try it with other integer types!
(unless measured slow, stay tuned to a later point in the course)
int sad_uninitialized_variable; // 😱 Really, don't do this!
bool initializing_is_good = true; // ✅ Always initialize!
bool use_uniform_initialization{true};
auto also_works_with_auto{true};
auto this_works_too = true;
bool set_to_default_value{}; // Initialize to false
int some_int_number{}; // Initialize to 0
double some_double_number{}; // Initialize to 0.0
float some_float_number{}; // Initialize to 0.0F
auto does_not_work{}; // ❌ no info on the type!
Let's consider an example:
#include <cstdio>
int main() {
double uninitialized_variable; // 😱 Don't do this!
std::printf("%e\n", uninitialized_variable);
}
Here is the typical output:
λ › ./test
2.14188e-314
λ › ./test
2.12718e-314
λ › ./test
2.12991e-314
- The value of
uninitialized_variable
is "undefined" - Your results will (probably) be different!
- Relying on this value causes "Undefined Behavior" (UB)
We can also initialize the variables from other variables
int some_int{other_int};
auto some_int_copy = some_int;
Also, we can assign a value to an existing variable
some_int_copy = 42;
some_int = some_int_copy;
Both initialization and assignment use =
to set value
Here is a rule of thumb to tell them apart 👍
- If a statement creates a new variable - it is an initialization
- If not - it is an assignment
const
- constant, constexpr
- constant expression
- Can be used for constants created at run time
- Can be initialized with any provided value
- Sometimes will be available at compile time
int a = 42; const int b = a; // ✅ const int c = 23; // ✅ const int d = c; // ✅ const auto e = c; // ✅
Here is the rule of thumb to declare anything:
There are some exemptions from this rule, but it mostly works
- We can create a reference to any variable
- Allows to borrow a variable to a different context
- Use
&
to state that a variable is a referencefloat & ref = original_variable;
- Reference is part of the type:
variable
ref
has type<OriginalType> &
- Changing a reference changes the variable and vice versa
int some_variable = 42; int& some_variable_ref = some_variable; some_variable_ref = 23; // some_variable is now == 23
- Yields performance gain as references avoid copying data Will be more important when we talk about functions later
- References are fast but reduce control
- To avoid unwanted changes use
const
const auto & ref = original_variable;
- Here type of
ref
isconst <OriginalType> &
#include <cstdio>
int main() {
int number = 42;
int& ref = number; // Name has to fit on the slides ¯\_(ツ)_/¯
const int& const_ref = number;
std::printf("ref: %d, const_ref: %d\n", ref, const_ref);
ref = 0;
std::printf("ref: %d, const_ref: %d\n", ref, const_ref);
number = 23;
std::printf("ref: %d, const_ref: %d\n", ref, const_ref);
return 0;
}
- There is a single global scope (outside of all functions)
- Local scopes start with
{
and end with}
- All variables live in the scope where they have been declared
- All variables die in the end of their scope
- 🚨 This is the core principle of C++ memory management!
- Variables can "shadow" other variables from outer scopes
constexpr auto kGlobalVariable{42};
int main() { // Start of main scope
float some_float = 13.13F; // Create variable
{ // New inner scope
auto some_float = 42.42F; // "Shadows" some_float
auto another_float = some_float; // Copy variable
} // another_float and the shadow of some_float die
return 0;
} // Variable some_float dies
// kGlobalVariable dies at the end of the program
- 🎨 Name constants in the global scope in
CamelCase
starting with a small letterk
:constexpr float kImportantFloat = 42.42f; const int kHello{42};
- 🎨 Name constants in any local scope in
snake_case
const auto local_scope_constant{42UL};
- Keyword
const
is part of the variable's type: variablekHello
above has typeconst int
Check out the Google C++ Style Guide for more details
- All character, integer and floating point types are arithmetic
- Arithmetic operations:
+
,-
,*
,/
,%
(modulo division) - Comparisons
<
,>
,<=
,>=
,==
returnbool
-
a += 1
$\Leftrightarrow$ a = a + 1
, same for-=
,*=
,/=
, etc. - Avoid
==
for floating point types (we'll cover a better way later)
#include <cstdio>
int main() {
std::printf("%u\n", 23U - 42U);
return 0;
}
Prints 4294967277
due to an "underflow" (stay tuned)
- Logical operations defined over boolean variables:
or:
||
, and:&&
, not:!
bool is_happy = (!is_hungry && is_warm) || is_rich;
- Additional operations on integer variables:
-
/
is integer division: i.e.7 / 3 == 2
-
%
is modulo division: i.e.7 % 3 == 1
-
Increment operator:
a++
$\Leftrightarrow$ ++a
$\Leftrightarrow$ a += 1
-
Decrement operator:
a--
$\Leftrightarrow$ --a
$\Leftrightarrow$ a -= 1
- There is a slight difference between them, stay tuned
- ❌ Do not use de- increment operators within another expression, i.e., 😱
a = (a++) + ++b
😱
- write
main()
function - add a constant
- use
printf
to print it - add other variables and print them, also use
auto
- create variables from operations and use them on variables
- Create a variable with
+
, or/
- Use
+
after a variable
- Create a variable with
- Showcase the UB when
double
left uninitialized - Try to assign a
double
to anunsigned int
- Showcase the underflow with unsigned integers