system call diagrams
A BareMetal Image

Data types defines the structure of data and the operations which can be performed on them. Most programming languages have two categories of data types, i.e fundamental/primitive data types and derived/complex data types. C++ is no exception; it has both.

C++ like its subset C is statically typed, meaning, once a variable is defined in one type, it can never be used to store a value of a different type. Statically typed languages prevent errors due to wrong assignments.
All variables (named piece of memory) in C++ must be of a type. Here is a list of primitive data types in C++.

A table of primitive data types.
Type Common Size (Bytes)
char 1 byte
short 2 bytes
int 4 bytes
long int 4/8 bytes
long long int 8 bytes
float 4 bytes
double 8 bytes
long double 16 bytes

1 byte = 8 bits
It’s a rule that the size of preceding data type cannot be greater than its successor. It’s also good to realize these sizes depends on your machine. Numerical values can either be integers or floating-points. Floating-points are numbers with fractional parts. Integers are numbers without fractional parts i.e both negative, zero and positives.

Derived data types are made from the primitive types. And they include:
Arrays, Structures, Enums, Strings, Pointers, References, Functions, Classes / Objects

In these article we will only handle the primitive data types.
You can always confirm the amount of memory per type by using sizeof() operator. Derived data types do not have fixed size in the memory.

Cpp
        std::cout <<(sizeof(double))<<std::endl;
                    

Integers

Integers can either be signed or unsigned. Signed can be either positive or negative while unsigned cannot be assigned a negative variable. Since signed integers are the default, you may not need to prefix the keyword unsigned before the type name except for char which can be signed or unsigned by default depending on compiler. The addition of these keywords (signed/unsigned) is primarily for self-documenting code.

Zero Initialization

Sometimes in your code you’ll declare variables without using them immediately, by default they’ll contain garbage values they find at those memory locations. This is quite a disadvantage when you forget and use them before assigning your intended values. To avoid such bugs, use zero initialization which has this syntax:

dataType varName {};

Cpp
        int myWeight {};
        std::cout <<myWeight << std::endl; // outputs zero
                    

Constants

You will encounter instances when the values you declare should not change. These instances calls for use of keyword const, which makes such values to be called constants. Examples of values which should be constant are file paths, Pi, cmPerMeter etc.

Cpp
            const unsigned cmPerMeter {100};
            double inputMeter {1.34};
            std::cout<<"Centimers = "<<inputMeter*cmPerMeter<<std::endl;
                    

It is a common write const values in all uppercase. From our discussions, it’s clear that constants serves two important roles:

1. Enhance code readability, you are sure the value shouldn’t change.
2. Reduce bugs, the compiler will yell at you when you try changing constants.

Integer Literals

Literals are fixed values which can be used in expressions or assignments. There are many types of literals in C++. Some common ones are, string literals, character literals, integer literals, floating-point literals.

Examples of integer literals
Month Salary
-124 int
1000UL unsigned long
10000ul unsigned long
10’000 int
178’00’00ULL unsigne long long
100.00f float
10000ul unsigned long
100.00L long double

NOTE: Since C++14, the single quote has been used for readability.
The essence of these literals is that after you specified a given type for a variable, then you have to assign a corresponding value. For example:

long population {100000} // initializes a long with an integer

This works but due to the favor from the compiler which converts the integer to a long a process called implicit conversion. Better code should have been:

long population {100000L} // initializes a long with a long value
or
long population {100000l} // the lowercase l is not preferred because of confusion with 1

Hexadicimal Literals

You can assign hexadecimal values to variables or use them in expressions. You can’t write them directly like 40F because it might mean another thing altogether. A special notation is therefore used:

Cpp
        int date = 0xF; //initializes date to 15 in decimal
        std::cout<<date<<std::endl;
                    

Both 0x and 0X are acceptable.

Octal Literals

To express octal values in C++, you use a leading zero.

Cpp
        int date = 0b11; 
        std::cout<<date<<std::endl;
                    

Calculation Using Integers

Before we see how calculations can be done on integers, we will introduce some terminologies. Consider,
int sum = 23 + 7;
Here, both 23 and 7 are called operands. The addition sign +, is called operator.

Basic Arithmetic Operators
Operator Operation Example
+ Addition 100 + 100
- Subtraction income - tax
* Multiplication 30 * 12
/ Division 100 / children
% Modulus 100 % children

Modulus operation computes the remainder of division e.g

Cpp
        int evenCandidate {23};
        int remainder = evenCandidate % 2;
        std::cout<<remainder<<std::endl;//returns 1, the remainder
                    

It’s good to realize that division of integers does not include decimal parts in the answer, so you can combine division operation with modulus operation to obtain both quotient and remainder.

Compound Arithmetic Expressions

Arithmetic expressions also provide a disparity with daily mathematics because of (strong) operator precedence.

Cpp
        int length, width, perimeter;
        length = 10;
        width = 30;
        perimeter = length + width * 2; // wrong formula
        std::cout<<perimeter<<std::endl;
        perimeter = length*2 + width * 2; // right formula
        std::cout<<perimeter<<std::endl;
        perimeter = (length + width )* 2; // right formula
        std::cout<<perimeter<<std::endl;
                    

Operator precedence is enable division, multiplication and modulus to be execute before addition and subtraction. Addition and multiplication have equal precedence so whoever comes first is executed before the other. Similarly, multiplication, division and modulus have equal precedence. Parentheses can be used to override these precedences. You’re generally encourage to use them whenever not sure of the precedence of operators in your expression.

Compound Assignment Operators

Compound assignment operator or simply the op= operator is a shorthand for operation which involve operation on a variable an storage in the same variable.

These operators include:

Compound assignment operators table
Operator Operation
+= Addition
-= Subtraction
/= Division
*= Multiplication
%= Modulus
Compound assignment operators table (Bitwise)
Operator Operation
&= Bitwise AND
|= Bitwise OR
^= Bitwise Exclusive OR
<<= Left-shift
>>= Right-shift

Incrementing and Decrementing Integers

There’s a common notations for decrementing/incrementing integers using -- and ++. These signs can be use as prefix (--remaining) and postfix(remaining--). There is a subtle difference between the two, prefix decrements the variable and uses the decremented value in the expression while postfix will decrement the variable but will not use the updated value in the expression.

Cpp
        int length, width;
        length = 10;
        width = 30;
        std::cout<<length++<<std::endl; // length increased to 11 but not used
        std::cout<<++length<<std::endl; //11 increased to 12 and output is 12
        std::cout<<length-- + width<<std::endl; // 12 reduced by 1 but the change not used, output is 42
                    

NOTE: Do not read the variable once it has been modified in the same expression.

int sum = number1++ + number2 + number1; // undefined behavior

Floating-point Variables

You will certainly have scenarios where the values cannot be accurately expressed in integers. Some of those cases is recording heights, time in systems, distances.

Declaration of floating-points

There are three fundamental types under floating point. Each with increased precision compare to its predecessor. They are, float, double, long double.

const float PI {3.142f};
double height {1.93};
long double stride {1.03L};

NOTE: Precision describes the number of digits in the mantissa i.e the right side of decimal point in floating-points. Like data type sizes, precision is determined by your compiler.

Hazards of floating-points

Floating-point are used in arithmetic operations in a similar fashion as integers except that the modulus operation cannot be done. The ++ and -- have same effect as in integers, incrementing or decrementing the value by 1. There are some subtle but unforgiving pitfalls of floating-point usage:

1. Many decimal values don’t convert exactly to floating points
2. Values that differ several orders in magnitude can lead to errors.
3. Catastrophic cancellation – loss of precision after finding difference between nearly identical floats.

Numerical Functions in the cmath Header

The cmath library has a huge set of mathematical functions which you’ll love using. It is true you can write the functions for yourself but chances are your version may not be as optimized or accurate. The table below list a small portion of what cmath offers.

A table of primitive data types.
Function Usage
sqrt(argument) Computer square root of argument.
pow(base, expo) Computes base raised to power expo.
exp(argument) Computes the value of eargument.
floor(argument) Rounds down the float argument to whole number.
ceil(argument) Rounds up the float argument to a whole number.
round(argument) Rounds argument to the nearest integer.
abs(argument) Computes the absolute value of the argument.
log(argument) Computes natural logarithms to base e of argument.
cos(angleRadians) Computes the cosine of the argument.

NOTE: You must include the header cmath and std namespace to use these functions. Again, you have to convert all your angles to radians for the trigonometric functions.

Formatting the Output Stream

The iomanip header contain stream manipulators which can be used to format data while it’s written in the output stream. A stream manipulator is applied by using insertion operator, <<. Most iomanip manipulators does not require arguments.

A table of primitive data types.
Manipulator Effect
std::fixed Sets al subsequent floating-point outputs to fixed-data notation..
std::scientific Sets al subsequent floating-point outputs to scientific notation.
std::dec Sets all subsequent integer outputs to decimal.
std::hex Sets all subsequent integer outputs to hexadecimal.
std::oct Sets all subsequent integer outputs to octal.
std::left Sets the output to the left.
std::right Sets the output to the right.
std::defaultfloat Reverts to the default floating-point notation.
std::setprecision(n) Sets the floating-point precision or decimal places to n.
std::setfill(c) Fill the output field width with the character c.
std::setw(n) Sets the output field with to n.

Example using code snippet

Cpp
        int age {20};
        float height {19.30};
        std::cout<<"John is "<<age<<". His height is "<<std::setprecision(1)<<height<<" dm."<<std::endl; //John is 20. His height is 2e+01 dm.
                    

Type Conversions

All binary arithmetic operators require all the operands to be of same type. That is something since you’re likely to have expressions with so diverse data. C++ language eases things a bit by doing implicit conversion i.e it converts the smaller sized variable to the larger-size variable.

Cpp
        int personHeight {2};
        float shoeHeight {0.04f};
        float sum {shoeHeight + personHeight};
        std::cout<<sum<<std::endl;
                    

That is not guaranteed since some types are incompatible. Converting variables explicitly in your code is called (strong) explicit conversion. The previous snippet can be extended to show explicit conversion:

Cpp
        sum = static_cast(shoeHeight) + static_cast(personHeight);
        std::cout<<sum<<std::endl;
                    

This may not matter much but it gives clarity. And of course there are scenarios where you need just this.

Character Variables

Huge chunk of our examples have used integer types except the smallest in that set, char. Char doesn’t look like an integer from a disinterested glance. Variables of type char are use primarily for storage of character codes. That means you have two options while initializing char types:

Using character codes

Even though it doesn’t have to be ASCII, ASCII codes is the most common character encoding used for char codes.

Cpp
        char firstLetter = 65;
        std::cout<<"First letter in the alphabet is "<<firstLetter<<std::endl;
                    

Using characters

Unlike string literals, character literals are enclosed in single quotes.

Cpp
        char firstLetter = 'A';
        std::cout<<"First letter in the alphabet is "<<firstLetter<<std::endl;
                    

NOTE: Being the smallest in size, it is possible to explicitly convert characters into all other integer types. All the arithmetic operations on integers works on char too.

Cpp
        char firstLetter = 'A';
        firstLetter += 1;
        std::cout<<"Second letter in the alphabet is "<<firstLetter<<std::endl;dl;
                    

Auto Keyword

As we discussed earlier, variables in C++ must have a type. Sometimes you may not be sure of the type of your initial values. In such cases, the auto keyword is invaluable since it deduces the type for you. However, you cannot write lines like this:
auto age;
age = 20;
This fails terribly because auto uses the initial values to determine the type. Correct usage will be:

auto age = 20;

Seasoned programmers will advice you against usage of auto with the fundamental types. It is more suited for pointers, references, lambdas, iterators, templates, containers and etc.

Your programs will always work on data. Therefore, understanding types is so important it can’t be overemphasized. Put in more practice until you’re comfortable.



Thanks for Reading BareMetal