Heap, Stack and Recursion

By Rainer Stropek (@rstropek)

Samples

  • This presentation's samples are written in C and TypeScript
    • You can learn about the differences by comparing them line-by-line
       
  • C is a rather old, established, and system-near programming language
    • C is good for learning to understand how computers work
    • Normally, you do not write C code like the one shown here. Use higher languages like C++, Rust, C#, Java, etc. instead.
       
  • Try all samples online at http://pythontutor.com/ to understand how the different storage types work

Heap and Pointers

Global Variables

#include <stdio.h>
#include <stdbool.h>

// These are "global variables" of different types.
// They are called "global" because they can be used in
// all functions.
double floatingpoint = 4.2;
int integer = 42;
char character = 'a';
bool boolean = true;
struct values { int a, b, c; } v = { 1, 2, 3 };

int main(void) {
  printVariables();
}

void printVariables() {
  printf("Variable values: %f, %d, %c, %d, {%d, %d, %d}", 
    floatingpoint, integer, character, boolean, v.a, v.b, v.c);
}



// These are "global variables" of different types.
// They are called "global" because they can be used in
// all functions.
const floatingpoint: number = 4.2;
const integer: number = 42;
const character: string = 'a';
const boolean: boolean = true;
// JavaScript has no C-like structs

function main(): void {
  printVariables();
}

function printVariables(): void {
  console.log("Variable values: "
    +floatingpoint+', '+integer+', '+character+', '+boolean);
}

main();

Pointers to the Heap

#include <stdio.h>
#define BYTE unsigned char

// A string is in fact a "pointer" to memory on the heap.
char *greeting = "Hello World!";

// An array is in fact a "pointer" to memory on the heap.
BYTE *data;
int i;

// Lets declare a pointer to a structure
typedef struct { double x, y, z; } vector;
vector *v;

int main(void) {
  // Passing a string to a function means passing a pointer.
  puts(greeting);
  
  // Alloc memory on the heap and let v point to it
  v = malloc(sizeof(vector));
  v->x = v->y = v->z = 42.0;
  printf("Vector points to %f / %f / %f", v->x, v->y, v->z);
  free(v); // Free heap memory. v becomes invalid.

  // Alloc array on the heap and initialize it
  data = malloc(3);
  for(i = 0; i < 3; i++) { data[i] = i + 1; }
  free(data); // Free heap memory
}



// JavaScript has a separate data type for strings
const greeting: string = "Hello World!";

// An array is a reference to the heap
let data: number[];
let i;

// Lets declare a reference to an object
interface Vector { x: number; y: number; z: number } 
let v: Vector;

((): void => {
  // Passing a string to a function means passing a reference.
  console.log(greeting);
  
  // Allocate a JavaScript object on the heap
  v = { x: 1, y: 2, z: 3 };
  
  console.log('Vector points to '+v.x+'/'+v.y+'/'+v.z);
  // No need to free memory. Done by JS automatically.

  // Allocate JS array on the heap
  data = []; // No fixed size in JS
  for(i = 0; i < 3; i++) { data.push(i + 1); }
  // No need to free memory. Done by JS automatically.
})();

Pointers in Structures

// Pointers can point to structures
// which contain pointers to structures
// which contain pointers to structures...
typedef struct {
  char *street;
  char *city;
} address;

typedef struct {
  char *firstName;
  char *lastName;
  address *address;
  int age;
} person;
person *p;

int main(void) {
  p = malloc(sizeof(person));
  p->age = 42;
  p->firstName = "Foo";
  p->lastName = "Bar";
  
  p->address = malloc(sizeof(address));
  p->address->street = "Somewhere";
  p->address->city = "Anywhere";
  
  free(p->address);
  free(p);
}
// References can refer to objects
// which contain references to objects
// which contain references to objects...
interface Address {
  street: string;
  city: string;
};

interface Person {
  firstName: string;
  lastName: string;
  address?: Address;
  age: number;
};
let p: Person;

(() => {
  p = {
    age: 42,
    firstName: 'Foo',
    lastName: 'Foo'
  };
  p.address = {
    street: 'Somewhere',
    city: 'Anywhere'
  };
  
  console.log('Done!');
})();

Tree Structure

// Structure can recursively point to elements
// of the same type. Here you see a classic data
// structure in programming: A "tree" structure.
// https://en.wikipedia.org/wiki/Tree_(data_structure)
typedef struct {
  struct node *left;
  struct node *right;
  int value;
} treeNode;
treeNode *leftLeaf;
treeNode *rightLeaf;
treeNode *rootNode;

int main(void) {
  leftLeaf = malloc(sizeof(treeNode));
  leftLeaf->value = 1;

  rightLeaf = malloc(sizeof(treeNode));
  rightLeaf->value = 3;
  
  rootNode = malloc(sizeof(treeNode));
  rootNode->value = 2;
  rootNode->left = leftLeaf;
  rootNode->right = rightLeaf;
  
  free(rootNode->left);
  free(rootNode->right);
  free(rootNode);
}
// Structures can recursively reference elements
// of the same type. Here you see a classic data
// structure in programming: A "tree" structure.
// https://en.wikipedia.org/wiki/Tree_(data_structure)
interface TreeNode {
  left?: TreeNode;
  right?: TreeNode;
  value?: number;
};
let leftLeaf: TreeNode;
let rightLeaf: TreeNode;
let rootNode: TreeNode;

(() => {
  leftLeaf = { };
  leftLeaf.value = 1;

  rightLeaf = { };
  rightLeaf.value = 3;
  
  rootNode = { };
  rootNode.value = 2
  rootNode.left = leftLeaf;
  rootNode.right = rightLeaf;



  console.log(rootNode);
})();

Memory for Strings

#include <stdio.h>
#include <string.h>

char *greeting1;
char *greeting2;

int main(void) {
  // Assigning a string (=pointer) does NOT copy the heap memory.
  // Note: IMMUTABLE string that cannot change.
  greeting1 = "Hello!"; 
  greeting2 = greeting1;
  puts(greeting2);

  greeting1 = greeting2 = NULL;  

  // Allocate heap memory that CAN change (MUTABLE)
  // and copy some text into allocated heap.
  greeting1 = malloc(sizeof(char) * 4);
  strcpy(greeting1, "Hi!");

  // Again, assigning a string (=pointer) does 
  // NOT copy the heap memory.
  greeting2 = greeting1;
  
  // Changing greeting1 ALSO changes greeting2 because 
  // both point to the same memory.
  greeting1[0] = 'h';
  puts(greeting2);
  
  free(greeting1);
}



let greeting1: string;
let greeting2: string;

(() => {
  // JS handles memory for string for you. You do not
  // see the details. String in JS are always IMMUTABLE.
  greeting1 = 'Hello!'; 
  greeting2 = greeting1;
  console.log(greeting2);

  greeting1 = greeting2 = undefined;  

  // JavaScript does NOT have a mutable string


  greeting1 = 'Hi!';



  greeting2 = greeting1;
  
  // If we change greeting1, greeting2 is NOT changing, too.
  
  greeting1 = 'h' + greeting1.substr(1);
  console.log(greeting2);
  
  console.log('Done');
})();

Copying Heap Memory

#include <stdio.h>
#include <string.h>
typedef unsigned char BYTE;

BYTE *values1;
BYTE *values2;
BYTE i;

#define SIZE 3

int main(void) {
  // Allocate array of numbers and write values into it.
  values1 = malloc(SIZE);
  for(i = 0; i < SIZE; i++) { values1[i] = i; }
  
  // Assignment does NOT copy the content of the array
  values2 = values1;
  
  // This time, we want to COPY THE CONTENT of the
  // memory on the heap.
  values2 = malloc(SIZE);
  memcpy(values2, values1, SIZE);
  
  free(values2);
  free(values1);
}




let values1: number[];
let values2: number[];
let i: number;

const SIZE = 3;

(() => {
  // Allocate array of numbers and write values into it.
  values1 = [];
  for(i = 0; i < SIZE; i++) { values1[i] = i; }
  
  // Assignment does NOT copy the content of the array
  values2 = values1;
  
  // This time, we want to COPY THE CONTENT of the
  // memory on the heap.
  
  values2 = values1.slice(); // in modern JS: [...values1]
  

  console.log('Done');
})();

Local Variables on the Stack

Local Variables

#include <stdio.h>

int main(void) {
  // Declare two LOCAL variables. They are only 
  // accessible in "main".
  int a = 21;
  int b = 21;
  
  // The parameters values of a and b are COPIED 
  // onto the stack for "add".
  int sum = add(a, b);
  
  printf("%d + %d = %d", a, b, sum);
}

int add(int a, int b) {
  // Note that a, b, and result are LOCAL variables in "add". 
  // They are stored on the stack.
  int result = a + b;
  return result;
  
  // We do NOT need to free stack memory.
  // This is done automatically.
}


function main(): void {
  // Declare two LOCAL variables. They are only 
  // accessible in "main".
  const a = 21;
  const b = 21;
  
  // The parameters values of a and b are COPIED 
  // onto the stack for "add".
  const sum = add(a, b);
  
  console.log(a + ' + ' + b + ' = ' + sum);
}

function add(a: number, b: number): number {
  // Note that a, b, and result are LOCAL variables in "add". 
  // They are stored on the stack.
  const result = a + b;
  return result;
  
  // We do NOT need to free stack memory.
  // This is done automatically.
}

main();
console.log('Done');

Call by Reference



function main(): void {
  const numbers: number[] = [];
  for (let i = 0; i < 3; i++) { numbers[i] = i; }
    
  // Passing numbers to sum does NOT copy the array,
  // only the reference is copied.
  const result = sum(numbers);
  console.log(result);
  
}

function sum(numbers: number[]): number {
  let result = 0;
  for(let i = 0; i < numbers.length; i++) { result += numbers[i]; }
  
  return result;
}

main();
console.log('Done');


void main() {
  int *numbers = malloc(sizeof(int) * 3);
  for (int i = 0; i < 3; i++) { numbers[i] = i; }
  
  // Passing numbers to sum does NOT copy the array,
  // only the pointer is copied.
  int result = sum(numbers, 3);
  printf("%d", result);
  free(numbers);
}

int sum(int *numbers, int length) {
  int result = 0;
  for(int i = 0; i < length; i++) { result += numbers[i]; }
  
  return result;
}

Block-Scoped Variables (TS)

(() => {
  let a = 1;
  if (1 === 1) {
    // The next "a" is different from the "a" above
    // because it is inside a new block.
    let a = 2;
    if (1 === 1) {
      // The next "a" is different from the "a" above
      // because it is inside a new block.
      let a = 3;
      console.log(a);
    }
    console.log(a);
  }
  console.log(a);
  console.log('Done');
})();

Recursion

Fibonacci Numbers

const maxValue = 13;

function fibonacci(last: number, secondLast: number): void {
    const next = last + secondLast;
    if (next > maxValue) return;

    console.log(next);
    fibonacci(next, last);
}

console.log(1);
console.log(1);
fibonacci(1, 1);
console.log('Done');

Backtracking

How to Learn More?

Made with Slides.com