C++ course

Lesson 2 recap

Objectives

Data types

- Union

- enum

- Structures

- C String (Null terminated string)

Statements

- Switch case statement

Theroy

- Stack & heap

Unions

Unions in C are variables that can be associated with several datatypes for the exact same memory location. The size of a union is the size of it's largest datatype

#include <iostream>
using namespace std;

union Pack {
    char   c;
    long   l;
};

int main()
{
    cout << sizeof(Pack) << endl;  // Size is 8 bytes which is the size of long 
}

Unions - cont

Unions are usually used with used with the company of a discriminator: a variable indicating which of the fields of the union is valid.

Usage

- Optimising memory consumption

- Accessing the same memory in multiple formats

#include <iostream>
using namespace std;
struct MyVariant_t {
  int type;  // This is a descriminator whose value can be 1 for char or 2 for long 
   union Pack {
     char   c;
     long   l;
   } data;
};
int main()
{
   MyVariant_t v;
   /** do some code */
   if (v.type == 1)      { /* use v.data.c  */ }
   else if (v.type == 2) { /* use v.data.l  */ }
}

Enum

Provide a logical name to an ordinal value. 

enum EyeColor (Black, Brown, Blue };
int main() {
  EyeColor myEyeColor = Brown; // Easy to read
}

The alternative would be to choose arbitrary values and use them

to identify the eye colour, but that is much less clear

int main() {
  int myEyeColor = 1; // We decided that 1 is brown, not immediately obvious.
}

Structures

Structs in other languages are also known as records, the ability to aggregate data together and provide it with a meaningful name.

Example:

// Provide a data type for person containing the attributes we care about
class Person {
   int age;
   enum EyeColor {Blue, Brown, Black} eyeColor;
   std::string name;
};

- Structs declaration must terminate with a semi-colon

- The size of a struct is at least the sum of the sizes of all it's constituents.

The different fields in the struct can be access via the dot operator.

Person p;

p.age = 56
p.name = "My Name"
p.eyeColor = Person::Brown;

C String

Also known as NULL terminated string.

String is not a primitive type in C, but rather an array of characters that gets a bit of a differential treatment in some C functions, than other types of arrays.

 

Consider the following code

 

char str[10];
strcpy(str, "Hello");

Hereunder is the representation of the memory pointed by variable str.

The size of the array is 10 bytes, the first 5 contain the value we set it the 6th byte is NULL (0x00) and the rest of the bytes are unused (We don't care about them)

C String - library functions

Given that C String is not a primitive type, there are no primitive operands that can manipulate Strings. We have to employ library functions for that

 

Here are a few most commonly used functions

  • strcat - concatenate two strings.
  • strchr - string scanning operation.
  • strcmp - compare two strings.
  • strcpy - copy a string.
  • strlen - get string length.
  • strncat - concatenate one string with part of another.
  • strncmp - compare parts of two strings.

 

Prior to the use of any of the functions, string.h header must be included.

#include <string.h>

Switch case

For better readability and to avoid long condition chains, such as this

enum EyeColor (Blue, Brown, Black, Green);

EyeColor colour = /* some value */

if (colour == Blue) {
  /* do something blue */
} else if (colour == Brown || colour == Green) { 
  /* Green & Brown have the same handling */ 
} else if (colour == Black) {
  /* do something black */
} else {
   /* Handle unexpected eye colour */
}

A switch case can make the code more readable and less verbose

enum EyeColor (Blue, Brown, Black, Green);

EyeColor colour = /* get some value */

switch (colour) {
  case Blue: /* Handle Blue colour */
             break;
  case Brown:
  case Green: /* Handle Blue & Green */
             break;
  case Blakc: /* Handle Black Color */
  default:
        /* Handle error, unexpected Eye colour */
};

without the  break keyword processing will continue to the next case.

Stack & Heap

In C/C++ we have 2 distinct memory locations that we should be aware of  

The stack whose memory management is entirely managed by the compiler, and whose workings we are supposed to understand only to only write more efficient code.

And the Heap which is synonymous to dynamic allocated memory. Memory that the developer is managing(Allocating and freeing on demand).

Stack - primitives

Also know as LIFO (Last In First Out), a stack has only 2 primitives push and pop, that grow and shrink the stack respectively.

Stack - Memory Management

In the diagram below, we see that first main's local variables are pushed into the stack, in step (b) main is preparing to call func1() prior to that it pushes the parameters required for func1() into the stack and than it calls func1() whose local variable are now pushed into the stack

In step (c) func1 finished processing and the stack is unwinding, meaning func1's locals and parameters are pop-ed. 

 

Heap

Unlike the stack, the heap is not a sequential growing and shrinking according to usage, but rather it's being allocated and freed for us by services that are provided by the operating system.

namely. malloc(), calloc() and free()

 

However, in C++ we use safer primitives to request memory and free it.

# Allocating memory
int* pInt       = new int(7);  // Allocates one integer size memory and assigns 7 to it.
int* pIntArray  = new int[10]; // Allocates 10 sequential integers. 

# Freeing the memory
delete pInt;           // Free the memory allocation to the 1 integer
delete [] pIntArray    // Free the memory allocation to the 10 integer array

The new/delete constructs are type safe and are object oriented aware unlike their C malloc/calloc/free counterpart.

C++L02

By perplexedpigmy