CS 202 - Notes 2018-11-07

The heap

If we want persistent memory (for example, so we can return an array from a function), we need to allocate it manually. We ask the system for a chunk of memory and it returns a pointer to the allocated chunk.

A downside to this is that we are responsible for memory management. We need to tell the system when we are done with the allocated memory, or it will be reserved for us until the program quits. This is called a memory leak. Some languages have compilers or interpreters that include a garbage collector, which can track down and eliminate these orphaned pieces of allocated memory.

Our main allocation function is malloc(size). malloc takes in the number of bytes we would like and returns a pointer (sizeof() is a handy function for determining the size of things). The pointer is a void *, so we typically need to type cast it to an appropriate pointer type. We also have a convenience function for creating arrays called calloc(count, size). This does the same thing as malloc, but will reserve count * size bytes, so it can hold an array of length count containing objects of size size.

To release our data, we need to call free(), passing it the pointer to the allocated memory.

Here is an example of calloc in action in an alterative for of our rot13 assignment.

#include <stdio.h>                                                                           
#include <stdlib.h>                                                                          
#include <string.h>                                                                          
                                                                                             
char * rot13(char * str){                                                                    
  int length = strlen(str);                                                                  
  char * newstr = (char *) calloc(length+1, sizeof(char));                                   
  newstr[length] ='\0'; // make sure our new string as a null terminator                     
                                                                                             
  for (int i = 0; i < length; i++){                                                          
    char c = str[i];                                                                         
    if (c >= 'a' && c <= 'z'){                                                               
      // this technique works by moving the base of the alphabet down to 0 so we can         
      // do modulo arithmetic                                                                
      c = c - 'a'; // shift 'a' to 0                                                         
      c = (c + 13) % 26; // add 13 with the wrap around if we go over                        
      c = c + 'a'; // shift the alphabet back into place                                     
    }else if (c >= 'A' && c <= 'Z'){                                                         
      // This uses the same math, but this time we will be clever                            
      // and make use of the fact that 13 is half 26                                         
      c = ((c+13) <= 'Z') ? (c+13) : (c - 13);                                               
    }                                                                                        
    newstr[i] = c;                                                                           
  }                                                                                          
  return newstr;                                                                             
}                                                                                            
                                                                                  
int main(int argc, char * argv[]){                                                           
                                                                                             
  char * code = rot13(argv[1]);                                                              
                                                                                             
  printf("%s\n", code);                                                                      
  free(code);                                                          
} 

Structs

The struct in C is a way to make more complex data structures. It works a bit like an array, in that we have multiple pieces of data bundled together in memory that we can talk about as a single entity. However, unlike arrays, the data is heterogenous, and each field is labeled individually.

Creating a new color type:

typedef unsigned char byte;                                                                  
                                                                                             
typedef struct {                                                                 
  byte red;                                                                                  
  byte green;                                                                                
  byte blue;  
  float alpha;
} color;  

Note that we used the typedef command to create a name for our new type (and for a byte type while we were at it).

To use our new type, we can just create new variables using it, accessing the fields using dot notation.

color c1;
c1.red = 255;                                                                              
c1.green = 127;                                                                            
c1. blue = 0;                                                                              
c1.alpha = 1.0;
printf("red: %x, green: %x, blue: %x, alpha: %f\n", c1.red, c1.green, c1.blue, c1.alpha);  
printf("red: %p, green: %p, blue: %p, alpha: %p\n", &c1.red, &c1.green, &c1.blue, &c1.alpha);

When we run this code, we get:

red: ff, green: 7f, blue: 0, alpha: 1.000000
red: 0x7fff41055ab0, green: 0x7fff41055ab1, blue: 0x7fff41055ab2, alpha: 0x7fff41055ab4

As we observed in class, we can see that the data is tightly packed together, but there is a gap between the blue and alpha fields.

We can write a simple function for creating new color structures, allocating them on the heap:

color * make_color(byte r, byte g, byte b, float a){                                         
  color * cp = (color *) malloc(sizeof(color));                                              
  (*cp).red = r;                                                                             
  cp->green = g;                                                                             
  cp->blue = b;                                                                              
  cp->alpha = a;                                                                             
  return cp;                                                                                 
}

Notice the two different syntaxes for accessing fields. Since cp is a pointer, it needs to be dereferenced before we can access its fields. The conventional way to do this would be (*cp).red, but this comes up so often, that we have some special syntax that just stands for "dereference and access the field": cp->red.

color *c2 = make_color(0,127,255,1.0);
printf("red: %x, green: %x, blue: %x, alpha: %f\n", c2->red, c2->green, c2->blue, c2->alpha);
printf("red: %p, green: %p, blue: %p, alpha: %p\n", &c2->red, &c2->green, &c2->blue, &c2->alpha);  

Unions

Unions are closely related to structs, and look identical in their construction. The difference is that their fields all point to the same location in memory. Here is a color unioned with an integer (I changed the color definition a little so that it only consumed four bytes).

typedef struct {                                                                             
  byte red;                                                                                  
  byte green;                                                                                
  byte blue;                                                                                 
  byte alpha;                                                                                
} color_2;                                                                                   
                                                                                             
                                                                                             
typedef union{                                                                               
  color_2 c;                                                                                 
  int i;                                                                                     
} color_3;  

We can set the value with one field and read it out from another.

color_3 c3;
c3.i = 0x77007f00;
printf("red: %x, green: %x, blue: %x, alpha: %x\n", c3.c.red, c3.c.green, c3.c.blue, c3.c.alpha); 

This prints

red: 0, green: 7f, blue: 0, alpha: 77

Note the effect of endianess on this.

More typically, we wouldn't use this to move between representations, but to create a space in a struct that could hold different types of data depending on context.

Full source

Here is the full source of the examples we did in class

#include <stdio.h>                                                                           
#include <stdlib.h>                                                                          
                                                                                             
typedef unsigned char byte;                                                                  
                                                                                             
typedef struct {                                                                             
                                                                                             
  byte red;                                                                                  
  byte green;                                                                                
  byte blue;                                                                                 
  float alpha;                                                                               
} color;                                                                                     
                                                                                             
typedef struct {                                                                             
  byte red;                                                                                  
  byte green;                                                                                
  byte blue;                                                                                 
  byte alpha;                                                                                
} color_2;                                                                                   
                                                                                             
                                                                                             
typedef union{                                                                               
  color_2 c;                                                                                 
  int i;                                                                                     
} color_3;                                                                                   
                                                                                             
                                                                                             
color * make_color(byte r, byte g, byte b, float a){                                         
  color * cp = (color *) malloc(sizeof(color));                                              
  (*cp).red = r;                                                                             
  cp->green = g;                                                                             
  cp->blue = b;                                                                              
  cp->alpha = a;                                                                             
  return cp;                                                                                 
}                                                                                            
                                                                                             
                                                                                             
color * add_colors(color * c1, color* c2){                                                   
                                                                                             
  return make_color(c1->red+c2->red, c1->green+c2->green, c1->blue+c2->blue, c1->alpha+c2->alpha);                                                                                       
}                                                                                            
                                                                                             
                                                                                             
                                                                                             
                                                                                             
int main(int argc, char * argv[]){                                                           
  color c1;                                                                                  
  color *c2 = make_color(0,127,255,1.0);                                                     
  color_3 c3;                                                                                
                                                                                             
                                                                                             
  c1.red = 255;                                                                              
  c1.green = 127;                                                                            
  c1. blue = 0;                                                                              
  c1.alpha = 1.0;                                                                            
                                                                                             
  printf("red: %x, green: %x, blue: %x, alpha: %f\n", c1.red, c1.green, c1.blue, c1.alpha);  
  printf("red: %p, green: %p, blue: %p, alpha: %p\n", &c1.red, &c1.green, &c1.blue, &c1.alpha);                                                                                          
                                                                                             
  printf("red: %x, green: %x, blue: %x, alpha: %f\n", c2->red, c2->green, c2->blue, c2->alpha);                                                                                          
  printf("red: %p, green: %p, blue: %p, alpha: %p\n", &c2->red, &c2->green, &c2->blue, &c2->alpha);                                                                                      
                                                                                             
  c3.i = 0x77007f00;                                                                         
  printf("red: %x, green: %x, blue: %x, alpha: %x\n", c3.c.red, c3.c.green, c3.c.blue, c3.c.alpha);                                                                                      
                                                                                             
  free(c2);                                                                                  
} 
Last Updated: 11/8/2018, 4:21:08 PM