Variables Memory and Initialization - itzjac/cpplearning GitHub Wiki

Let me introduce you to the first concept in programming: memory. As ab huge topic as it is, the core concepts do not vary much from language to language. Your PC should usually contain three different kinds of memory: ROM, RAM, VRAM. More fine grained memory types can be defined, but let's stick to the coarse idea.

When we refer to memory in the context of a C/C++ program we refer to RAM unless stated explicitly. RAM is limited and can be thought as empty buckets that we fill in with data. Each bucket represents our data unit, it can't be further divided. Sometimes buckets already contained garbage, then buckets are emptied and re-fill every time we put something there. To distinguish one bucket from another the CPU uses a unique address number.

The data unit that you access (1 bucket) with a unique address number is 1 byte or 8 bits size. When your PC is a x64 architecture (i.e. capable of processing up to 64 bit data size at a time), it can process up to 8 bytes or 64 bits at once when necessary. The CPU can also manipulate data units of 1 byte, but it will still need to align to the wider 64 bit bus when fetching, loading and executing the instructions.

C/C++ creates a convenient data layer to define types that map to the amount of bytes representing. Those are the so called built-in data types. Being at the minimum 1 byte size data type, other common are 4 bytes, 8 bytes. Beyond those numbers the support varies according to your CPU is 16 bytes a (SSE2), 32 bytes (AVX2), 64 bytes (AVX512) and more to come.

Tools like CPU-Z can help you identifying what instructions are available on you machine.

image

Built-in data types

Also known as fundamental types or primitive data types, are types provided by a programming language as a basic building block.

A built-in type is data type for which the programming language provide built-in support.

Naming variables

There are some rules to declare valid names for a variable in C++, rules to valid names are also known as identifiers.

  • Should not use spaces between words
  • Should be composed of letters, numbers and underscore characters
  • No limit on the name length
  • Must begin with a letter or an underscore
  • C++ keywords can't be use for names
  • Two consecutive underscores are reserved for use in the standard library and is not recommended to use it
  • Should give some indication of its meaning
  • Variable names are case sensitive

A good programming practice is to always use identifiers that help readability and clarity of the code.

Some of the most basic and typical name conventions are

my_new_variable (delimiter separation words using underscore)
my-new-variable (delimiter separation words using hypen)
myNewVariable (Camel case notation)

Always be consistent with the notation you use in the entire program.

The general C++ syntax to declare a variable is:

datatype variablename;

If we want to declare a 4 byte variable, usually mapped to the integer type, it will be written

int myInteger;

Though the byte size of the built-in types, there are more language declarationss that help us request the exact amount of bytes.

Primitive built-in types

We can divide these types as

Logical type

  • bool

Integer types

  • int
  • unsigned
  • long
  • short

Floating point types

  • float
  • double
  • long double

Characters types

  • char
  • char arrays (C-strings)

Notice integers, chars, bools are all stored in memory as integers values, memory holds a whole number as a byte, but never a fraction.

Bytes and variables

To determine the size of a variable in bytes, we use the operator sizeof. Given an expression or type name it will return the size in bytes of the expression or type. The sizeof can be used to compute the bytes size for more complex or compounded elements beyond the built-in types.

sizeof(type);
sizeof expr;

Declare built-in types and print the size in bytes for each one. Immediately introducing the std::cout function to facilitate printing text to the console

#include <iostream>

int main() 
{
    int myInteger;
    bool myBool;
    float mySimpleFloat;
    double myDoubleFloat; 
    long double myLongDoubleFloat;
    char myCharacter; 
    std::cout<<"Size in bytes for the C++ built-in types"<<std::endl;
    std::cout<<"Size of int["<< sizeof(myInteger)<<"]"<<std::endl;
    std::cout<<"Size of bool["<< sizeof(myBool)<<"]"<<std::endl;
    std::cout<<"Size of float["<< sizeof(mySimpleFloat)<<"]"<<std::endl;
    std::cout<<"Size of double["<< sizeof(myDoubleFloat)<<"]"<<std::endl;
    std::cout<<"Size of long double["<< sizeof(myLongDoubleFloat)<<"]"<<std::endl;
    std::cout<<"Size of char["<< sizeof(myCharacter)<<"]"<<std::endl;
    return 0;
}

>sizeof.exe
Size in bytes for the C++ built-in types
Size of int[4]
Size of bool[1]
Size of float[4]
Size of double[8]
Size of long double[16]
Size of char[1]

The results of this program will vary depending the platform it is used to compile, but there are some rules that C++ standard guarantees.

1 == sizeof(char) <= sizeof(short) <= sizeof(int) <) sizeof(long) <= sizeof(long long)

Simple precision floating point follow IEEE-754 standard (about 7 precision digits), 32 bit
Double precision floating point follows IEEE-754 standard (about 15 precision digits), 64 bit
Extended precision floating point follow IEEE-754 standard, usually 80 bit
char is always size of 1 byte

Always use the C++ reference board for further details, this section elaborates about the Built-in types

Memory contents

Every time a variable is declared, the exact space of memory is reserved to allocate the variable and a unique address number assigned. When the variable is 1 byte size, maps directly to 1 address. Beyond the 1 byte size, more that 1 address is needed to give space to a variable. Using contiguous addresses starting from a first byte up to the amount of bytes the variable needs you manage to group n bytes in a meaningful way.

variableAndMemory

The contents of the variable are unknown, depending on the scope of the variables, they can contain garbage values but using them without careful can lead to an Undefined Behavior of a program.

Printing the contents of the variables demonstrates how there is no way to predict the content of the variable in memory.

#include <iostream>
int main()
{
    int myInteger;
    bool myBool;
    float mySimpleFloat;
    double myDoubleFloat;
    long double myLongDoubleFloat;
    char myCharacter;
    std::cout << "myInteger [" << myInteger << "]"<<std::endl;
    std::cout << "myBool [" << myBool << "]"<<std::endl;
    std::cout << "mySimpleFloat [" << mySimpleFloat << "]"<<std::endl;
    std::cout << "myDoubleFloat [" << myDoubleFloat << "]"<<std::endl;
    std::cout << "myLongDoubleFloat [" << myLongDoubleFloat << "]"<<std::endl;
    std::cout << "myCharacter [" << myCharacter << "]"<<std::endl;
    return 0;
}

>memory.exe
myInteger [0]
myBool [0]
mySimpleFloat [2.24208e-44]
myDoubleFloat [2.07533e-317]
myLongDoubleFloat [1.53343e-4944]
myCharacter [ ]
myString []

Changing memory contents

To change the contents of the memory allocated for a variable we use the assignment operator =

variablename = expresion;

This can be read: assign the constant expression on the right to the variable name on the left. Assignment operator is quite different from the one used in mathematics operations, please repeat that to yourself.

For the integer variable this can be written as

myInteger = 1000;

it is also possible to declare and assign in a single line

int myInteger = 1000;

Now the memory contents are changed as shown

variableassign

The char variable only affects 1 of the cell memory, which is of size 1 byte.

Instead the int variable affects four contiguous memory cells, that fits the 4 byte size. At this point, using the assignment operator for each of the variables, the contents of the memory are all known

#include <iostream>
int main()
{
    int myInteger = 1000;
    float mySimpleFloat = 3.32154f;
    double myDoubleFloat = 2.94256454;
    long double myLongDoubleFloat = 2.136465456434L;
    char myCharacter = 'c';
    std::string myString = "Hello String";
    std::cout<<"Size in bytes for the C++ built-in types"<<std::endl;
    return 0;
}

Bytes and range values

Getting the amount of bytes used in memory used for a variable, is easily computed using the sizeof() operator.

This time we want to obtain the range from minimum to maximum values that the variable can store in its memory for each of the built-in types. For that we will use the C/C++ library limits and float, it contains MACROS conveniently exposed.

#include <iostream>
#include <climits>
#include <cfloat>

int main()
{
    char charMinValue = CHAR_MIN, charMaxValue = CHAR_MAX;
    short shortMinValue = SHRT_MIN, shortMaxValue = SHRT_MAX;
    int  intMinValue = INT_MIN, intMaxValue = INT_MAX;
    long  longMinValue = LONG_MIN, longMaxValue = LONG_MAX;
    float  floatMinValue = FLT_MIN, floatMaxValue = FLT_MAX;
    double doubleMinValue = DBL_MIN, doubleMaxValue = DBL_MAX;
    unsigned char  ucharMinValue = 0, ucharMaxValue = UCHAR_MAX;
    unsigned short ushortMinValue = 0, ushortMaxValue = USHRT_MAX;
    unsigned int  uintMinValue = 0, uintMaxValue = UINT_MAX;
    unsigned long  ulongMinValue = 0, ulongMaxValue = ULONG_MAX;
    std::cout << "char range [" <<  (int)charMinValue << ", " << (int)charMaxValue << "]"<<std::endl;
    std::cout << "short range [" << shortMinValue << ", " << shortMaxValue << "]"<<std::endl;
    std::cout << "int range [" <<  intMinValue << ", " << intMaxValue << "]"<<std::endl;
    std::cout << "long range [" <<  longMinValue << ", " << longMaxValue << "]"<<std::endl;
    std::cout << "float range [" <<  floatMinValue << ", " << floatMaxValue << "]"<<std::endl;
    std::cout << "double range [" <<  doubleMinValue << ", " << doubleMaxValue << "]"<<std::endl;
    std::cout << "unsigned char range [" <<  (int)ucharMinValue << ", " << (int)ucharMaxValue << "]"<<std::endl;
    std::cout << "unsigned short range [" <<  ushortMinValue << ", " << ushortMaxValue<< "]"<<std::endl;
    std::cout << "unsigned long range [" <<  ulongMinValue << ", " << ulongMaxValue << "]"<<std::endl;      
    
    return 0;
}

>ranges.exe
char range [-128, 127]
short range [-32768, 32767]
int range [-2147483648, 2147483647]
long range [-2147483648, 2147483647]
float range [1.17549e-38, 3.40282e+38]
double range [2.22507e-308, 1.79769e+308]
unsigned char range [0, 255]
unsigned short range [0, 65535]
unsigned long range [0, 4294967295]

Beyond the range on each built-in type values are not supported and this is usually detected at runtime, the program generates an overflow condition detected by the CPU indicating that the value generated couldn't be stored in the variable. Modern compilers contain overflow detection mechanisms that prevent integer overflow at compile time. The compiler specifications might describe it as an undefined behavior, i.e. anything can happen and not supported. Given all the complexity and protection mechanisms of modern compilers, the simplest program to demonstrate integer overflow is better implemented in assembly language not with C/C++.

Ambiguity between float and double.

Type conversion. Casting

char c = 10;
short s = c; // s = 10

unsigned char uc = 256; // uc = 0

int i = 496512.546f; // i = 496512

short s = 496512.546f; // i = -27776 -> demonstrate it

const

typedef

Operators

Unary

  • Negation -
  • Increment operator ++
  • Decrement operator --

Binary

  • /
  • % - explain how it works

Compound Arithmetic Operation

  • x += y x = x + y
  • x -= y x = x - y
  • x *= y x = x * y
  • x /= y x = x / y
  • x %= y x = x % y

Operator Precedence

  • 2 + 3 * 5 = 2 + 15 = 17
  • Override precedence with parenthesis

QUIZ

  • what to include
  • operator precedence
  • valid variable types
  • variable name correctness
  • compilation process
  • true or false statements about comments
  • name of the operators << >> (insertion, extraction)
  • const variables
  • entry point of every c++ program

HINT: do you know C++ code can be tested and compiled online? Google it and test some of the compilers available online.

⚠️ **GitHub.com Fallback** ⚠️