Week 02


Stacks, Queues, Priority Queues


Stacks, Queues, PriorityQs2/82

Computer systems frequently make use of:


... Stacks, Queues, PriorityQs3/82

All of these data structures have

Data structures differ in


Example Stack/Queue/PriorityQ Usage4/82

Starting from data containing one item, insert 3
 

[Diagram:Pics/clang/sqp1-small.png]


... Example Stack/Queue/PriorityQ Usage5/82

Then insert 6
 

[Diagram:Pics/clang/sqp2-small.png]


... Example Stack/Queue/PriorityQ Usage6/82

Then insert 2
 

[Diagram:Pics/clang/sqp3-small.png]


... Example Stack/Queue/PriorityQ Usage7/82

Then remove item
 

[Diagram:Pics/clang/sqp4-small.png]


... Example Stack/Queue/PriorityQ Usage8/82

Then insert 4
 

[Diagram:Pics/clang/sqp5-small.png]


... Example Stack/Queue/PriorityQ Usage9/82

Then remove item
 

[Diagram:Pics/clang/sqp6-small.png]


... Example Stack/Queue/PriorityQ Usage10/82

Then remove item
 

[Diagram:Pics/clang/sqp7-small.png]


Priority Queue Data Structure11/82

Priority Queue: Highest-priority-out protocol

Priority Queue interface:

typedef struct priorityq * PriorityQ;
PriorityQ initPriorityQ(void);
int  enterPriorityQ(PriorityQ q, Item val);
Item leavePriorityQ(PriorityQ q);
int  isEmptyPriorityQ(PriorityQ q);
void destroyPriorityQ(PriorityQ q);


Example Priority Queue Client12/82

A program to use a priority Queue. Assume Item has been defined as

typedef struct item Item;
struct item{
    int priority;
    char * data;
};

#include <stdio.h>
#include "PriorityQueue.h"
int main(void)
{
   char buffer[MAX_INPUT];
   PriorityQ pq = initPriorityQ();
   Item   item;
   int priority;
   while(fgets(buffer,MAX_INPUT,stdin) != NULL){
        if(sscanf(buffer,"%d %[^\n]",&priority,buffer) != 2) break;
        if(enterPriorityQ(pq,newItem(priority,buffer)) == 0) break;
   }    

   while (!isEmptyPriorityQ(pq)) {
      item = leavePriorityQ(pq);
      printf("%s\n", item->data);
      destroyItem(item);
   }
   destroyPriorityQ(pq);
   return 0;
}


Exercise 1: Implement a Priority Queue13/82


Bit Manipulation


Bits in Bytes in Words15/82

Values that we normally treat as atomic can be viewed as bits, e.g.

The above are common sizes and don't apply on all hardware
(e.g. sizeof(int) might be 16 or 64 bits, sizeof(double) might be 32 bits)

C provides a set of operators that act bit-by-bit on pairs of bytes.

E.g. (10101010 & 11110000) yields 10100000   (bitwise AND)

C bitwise operators:    &  |  ^  ~  <<  >>


Binary Constants16/82

Literal numbers in decimal, hexadecimal, octal, binary.

In hexadecimal, each digit represents 4 bits

   0100 1000 1111 1010 1011 1100 1001 0111
0x   4    8    F    A    B    C    9    7

In octal, each digit represents 3 bits

   01 001 000 111 110 101 011 110 010 010 111
0   1  1   0   7   6   5   3   6   2   2   7

In binary, each digit represents 1 bit

0b01001000111110101011110010010111


Bitwise AND17/82

The & operator

Example:

  00100111           AND | 0  1
& 11100011           ----|-----
  --------             0 | 0  0
  00100011             1 | 0  1

Used for e.g. checking whether a bit is set


Exercise 2: Checking for odd numbers18/82

One obvious way to check for odd numbers in C

int isOdd(int n) { return (n%2) == 1; }

Could we use & to achieve the same thing? How?


Aside: an alternative to the above

#define isOdd(n)  ((n)%2) == 1)

What's the difference between the function and the macro?


Bitwise OR19/82

The | operator

Example:

  00100111            OR | 0  1
| 11100011           ----|-----
  --------             0 | 0  1
  11100111             1 | 1  1

Used for e.g. ensuring that a bit is set


Bitwise NEG20/82

The ~ operator

Example:

~ 00100111           NEG | 0  1
  --------           ----|-----
  11011000               | 1  0

Used for e.g. creating useful bit patterns


Exercise 3: Bit-manipulation21/82

Assuming 8-bit quantities and writing answers as 8-bit bit-strings ...

What are the values of the following:

How can we achieve each of the following:


Bitwise XOR22/82

The ^ operator

Example:

  00100111           XOR | 0  1
^ 11100011           ----|-----
  --------             0 | 0  1
  11000100             1 | 1  0

Used in e.g. generating random numbers, building adder circuits


Left Shift23/82

The << operator

Example:

  00100111 << 2      00100111 << 8
  --------           --------
  10011100           00000000

** undefined and not recommended for signed quantities.


Right Shift24/82

The >> operator

Example:

  00100111 >> 2      00100111 >> 8
  --------           --------
  00001001           00000000

** if signed quantity, sign bit replaces left-end bit


Exercise 4: Bitwise Operations25/82

Given the following variable declarations:

    // a signed 8-bit value
signed char x = 0b01010101;
    // an 8-bit value
unsigned char y = 0b11001100;

What is the value of each of the following expressions:


Exercise 5: Bit Shifting Exercises26/82


Memory


The C View of Data28/82

A C program sees data as a collection of variables

int g = 2;

int main(void)
{
    int   i;
    int   v[5]
    char *s = "Hello";
    int  *n = malloc(sizeof(int));
    ...
}


Each variable has properties e.g.  name,  type, ...


... The C View of Data29/82

Variables are examples of computational objects

Each computational object has


... The C View of Data30/82

Memory regions during C program execution ...

[Diagram:Pics/memory/regions-small.png]


... The C View of Data31/82

Example of runtime stack during call to h()

[Diagram:Pics/memory/stack-frames-small.png]


Exercise 6: Properties of Variables32/82

Identify the properties of each of the named objects in the following:

int a;           // global int variable

int main(void) {
   int  b;       // local int variable
   static char c;// local static char variable
   char d[10];   // local char array
   ...
}

int e;           // global? int variable

int f(int g) {  // function + parameter
   double h;     // local double variable
   ...
} 


The Physical View of Data33/82

Memory = indexed array of bytes

[Diagram:Pics/memory/byte-array-small.png]

Indexes are "memory addresses" (a.k.a. pointers)


Memory34/82

Properties of physical memory

When addressing objects in memory ...


... Memory35/82

Memories can be categorised as big-endian or little-endian

[Diagram:Pics/memory/endian-small.png]


Exercise 7: Endianness36/82

Write code to print out an int, byte by byte. Is your system big or little endian?


Data Representation


Data Representation38/82

Ultimately, memory allows you to

What you are presented with is a string of 8,16,32,64 bits

Need to interpret this bit-string as a meaningful value

Data representations provide a way of assigning meaning to bit-strings


Character Data39/82

Character data has several possible representations (encodings)

The two most common:


ASCII Character Encoding40/82

Uses values in the range 0x00 to 0x7F (0..127)

Characters partitioned into sequential groups

Sequential nature of groups allows for things like (ch - '0') Eg. '4' - '0' converts the character '4' into the integer 4.

See   man 7 ascii


Unicode41/82

Basically, a 32-bit representation of a wide range of symbols

Using 32-bits for every symbol would be too expensive More compact character encodings have been developed (e.g. UTF-8)


UTF-8 Character Encoding42/82

UTF-8 uses a variable-length encoding as follows

#bytes #bits Byte 1 Byte 2 Byte 3 Byte 4
170xxxxxxx---
211110xxxxx10xxxxxx--
3161110xxxx10xxxxxx10xxxxxx-
42111110xxx10xxxxxx10xxxxxx10xxxxxx

The 127 1-byte codes are compatible with ASCII

The 2048 2-byte codes include most Latin-script alphabets

The 65536 3-byte codes include most Asian languages

The 2097152 4-byte codes include symbols and emojis and ...


... UTF-8 Character Encoding43/82

UTF-8 examples

ch unicode bits simple binary UTF-8 binary
$ U+0024 7 010 0100 00100100
¢ U+00A2 11 000 1010 0010 11000010 10100010
U+20AC 16 0010 0000 1010 1100 11100010 10000010 10101100
𐍈 U+10348 21 0 0001 0000 0011 0100 1000 11110000 10010000 10001101 10001000

Unicode strings can be manipulated in C (e.g. "")

Like other C strings, they are terminated by a 0 byte (i.e. '\0')

Warning: Functions like strlen may not work as expected.


Exercise 8: UTF-8 Unicode Encoding44/82

For each of the following symbols, with their Unicode value

Symbols: Given that has the code U+02665


Numeric Data45/82

Numeric data comes in two major forms


[Diagram:Pics/memory/numbers-small.png]


Integer Constants46/82

Four ways to write integer constants in C

Variations Invalid constants lie outside the range for their type, e.g.


Unsigned integers47/82

The unsigned int data type

[Diagram:Pics/memory/unsigned-int-small.png]


... Unsigned integers48/82

Value interpreted as binary number

E.g. consider an 8-bit unsigned int

01001101  =  26 + 23 + 22 + 20  =  64 + 8 + 4 + 1  =  77

Addition is bitwise with carry

  00000001     00000001     01001101     11111111
+ 00000010   + 00000011   + 00001011   + 00000001
  --------     --------     --------     --------
  00000011     00000100     01011000     00000000

Most machines will also flag the overflow in the fourth example


Exercise 9: Hexadecimal ↔ Binary ↔ Decimal49/82

When working at the machine level

Convert these 8-bit binary numbers to hexadecimal: Convert these 8-bit binary numbers to decimal: Convert the following decimal numbers to 8-bit binary:


Signed integers50/82

The int data type

[Diagram:Pics/memory/signed-int-small.png]


... Signed integers51/82

Several possible representations for negative values

In all representations, +ve numbers have 0 in leftmost bit

Examples: representations of (8-bit) -5 (where 5 is 00000101)


... Signed integers52/82

Signed magnitude: Easy to form -X from X ... XOR in high-order bit

A problem (using 8-bit ints) ...

Two zeroes ... one positive, one negative

Another problem:  x + -x ≠ 0 (mostly) with simple addition

  00000011  3     00101010  42      01111111  127
+ 10000011 -3   + 10101010 -42    + 11111111 -127
  --------        --------          --------
  10000110 !0     11010100  !0      01111110   !0

To fix requires extra hardware in ALU (Arithmetic Logic Unit) in the CPU


... Signed integers53/82

Ones complement: Easy to form -X from X ... NEG all bits

A problem (using 8-bit ints) ...

Two zeroes ... one positive, one negative

At least  x + -x is equal to one of the zeroes with simple addition

  00000011  3     00101010  42      01111111
+ 11111100 -3   + 11010101 -42    + 10000000
  --------        --------          --------
  11111111 -0     11111111  -0      11111111 -0


... Signed integers54/82

Twos complement: to form -X from X ... NEG all bits, then add 1

Now have only one representation for zero (00000000)

Only one zero value.   Also, -(-x) = x

Even better,  x + -x = 0 in all cases with simple addition

  00000011  3     00101010  42      01111111
+ 11111101 -3   + 11010110 -42    + 10000001
  --------        --------          --------
  00000000  0     00000000   0      00000000  0

Always produces an "overflow" bit, but can ignore this


Exercise 10: Binary↔decimal Conversion55/82

Convert these decimal numbers to 8-bit 2's complement binary:


What decimal numbers do these 8-bit 2's complement represent:
Demonstrate the addition of x + -x, where x is


Pointers56/82

Pointers represent memory addresses/locations

Many kinds of pointers, one for each data type, but


... Pointers57/82

Code and data is aligned and is machine dependant. For example:

Thus pointer values must be appropriate for data type, e.g.


Exercise 11: Valid Pointers58/82

Which of the following are likely to be valid pointers

0x00000000   0x00001000   0x00001001
0x7f000000   0x7f000001   0x7f000004

to objects of the following types

[Diagram:Pics/clang/memory-regions-small.png]


Fractions in different Bases59/82

The decimal fraction 0.75 means

Similary 0b0.11 means Similarly 0x0.C means Note: We call the . a radix point rather than a decimal point when we are dealing with other bases.


... Fractions in different Bases60/82

If we want to convert 0.75 in decimal to binary, it may be easy to look and and realise we need 0.5 + 0.25 which gives us 0b0.11. Sometimes it is not that easy and we need a systematic approach.

The algorithm to convert a decimal fraction to another base is:


... Fractions in different Bases61/82

For example if we want to convert 0.3125 to base 2

Therefore 0.3125 = 0b0.0101


Exercise 12: Fractions: Decimal → Binary62/82

Convert the following decimal values into binary


Floating Point Numbers63/82

Floating point numbers model a (tiny) subset of

C has two floating point types Literal floating point values:  3.14159,  1.0/3,  1.0e-9

printf("%10.4lf", (double)2.718281828459);
displays ⎵⎵⎵⎵2.7183
printf("%20.20lf", (double)4.0/7);
displays 0.57142857142857139685


... Floating Point Numbers64/82

IEEE 754 standard ...

Fraction part is normalised  (i.e. 1.2345×102 rather than 123.45)

In binary, exponent is represented relative to a bias value B


... Floating Point Numbers65/82

Example of normalising the fraction part in binary:

The normalised fraction part always has 1 before the decimal point.

Example of determining the exponent in binary:


... Floating Point Numbers66/82

Special cases:


... Floating Point Numbers67/82

Internal structure of floating point values

[Diagram:Pics/memory/float-rep-small.png]

More complex representation than int because 1.dddd e dd


... Floating Point Numbers68/82

Example (single-precision):

150.75 = 10010110.11
         // normalise fraction, compute exponent
       = 1.001011011 × 27
         // determine sign bit,
         // map fraction to 24 bits,
         // map exponent relative to baseline
       = 0100000110001011011000000000000000

where red is sign bit, green is exponent, blue is fraction

Note: B=127, e=27, so exponent = 127+7 = 134 = 10000110


Exercise 13: Floating point ↔ Decimal69/82

Convert the decimal numbers 1 to a floating point number in IEEE 754 single-precision format.

Convert the following floating point numbers to decimal.

Assume that they are in IEEE 754 single-precision format.

0 10000000 11000000000000000000000

1 01111110 10000000000000000000000

You can try out more examples with this Floating Point Calculator


Arrays70/82

Arrays are defined to have N elements, each of type T

Examples:

int    a[100];    // array of 10 ints
char   str[256];  // array of 256 chars
double vec[100];  // array of 100 doubles

Elements are laid out adjacent in memory

[Diagram:Pics/memory/array-in-mem-small.png]


... Arrays71/82

Assuming an array declaration like  Type v[N ] ...

Strings are just arrays of char with a '\0' terminator


... Arrays72/82

When arrays are "passed" to a function, actually pass &a[0]

[Diagram:Pics/memory/string-param-small.png]


... Arrays73/82

Arrays can be created automatically or via malloc()

int main(void)
{
    char str1[9] = "a string";
    char *str2;  // no array object yet

    str2 = malloc(20*sizeof(char));
    strcpy(str2, str1);
    printf("&str1=%p, %s\n", &str1, str1);
    printf("&str2=%p, %s\n", &str2, str2);
    printf("str1=%p, str2=%p\n",str1,str2);
    free(str2);
    return 0;
}

Two separate arrays (different &'s), but have same contents

(except for the unitialised parts of the arrays)


Structs74/82

Structs are defined to have a number of components

Example:

[Diagram:Pics/memory/struct-student-small.png]


... Structs75/82

To ensure alignment for the fields and for the struct itself, internal "padding" may be needed

[Diagram:Pics/memory/struct-pad-small.png]

Padding wastes space; You can try to re-order fields to minimise waste.


Unions76/82

A union is a special data type available in C that allows storing different data types in the same memory location.

The size of a union is equal to the size of its largest member (plus any padding).

An example of declaring a union

union MyUnion {
    unsigned long long value;
    char   s[8];
};

This union can store either an unsigned long long value, or a string of size 8 (including the '\0' terminator).


... Unions77/82

Example usage

union MyUnion u;
printf("%d\n",sizeof(union MyUnion)); prints out 8 
u.value = 999999;
printf("%llu\n",u.value); prints out 999999 
strcpy(u.s,"hello");
printf("%s\n",u.s);       prints out hello 
printf("%llu\n",u.value); Does NOT print out 999999 
                          as it has been (partly) overwritten 


... Unions78/82

Common use of union types: "generic" variables

#define IS_INT   1
#define IS_FLOAT 2
#define IS_STR   3

struct generic {
   int   vartype;
   union { int ival; char *sval; float fval; };
} myVar;

// treat myVar as an integer
myVar.vartype = IS_INT;
myVar.ival    = 42;
printf("%d\n", myVar.ival);
// now treat myVar as a float
myVar.vartype = IS_FLOAT;
myVar.fval    = 3.14159;
printf("%0.5f\n", myVar.fval);


Exercise 14: Union79/82

What would be output if we did the following:

struct generic myVar;

// treat myVar as ???
myVar.vartype = IS_INT;
myVar.fval    = 3.14159;
printf("%d\n", myVar.ival);

// treat myVar as ???
myVar.vartype = IS_FLOAT;
myVar.ival    = 42;
printf("%0.5f\n", myVar.fval);


Bit Fields80/82

We can specify size (in bits) of structure and union members.

struct date{
   //day has value between 1 and 31
   //so 5 bits are sufficient
   unsigned int day: 5;
   //month has value between 1 and 12, 
   //so 4 bits are sufficient 
   unsigned int month: 4;
   unsigned int year;
};

int main(void) {
   printf("%lu bytes\n", sizeof(struct date));
   struct date d = {31, 12, 2014};
   printf("%d/%d/%d", d.day, d.month, d.year);
   return 0;
}


... Bit Fields81/82

Warnings when using bit-fields:

Eg The following won't compile

int main(void) {
   printf("%p\n", &(d.day));
   return 0;
}


Exercise 15: Bit-fields82/82

What do you think the following will do? How can we fix it?

struct time{
    int hours: 5;
    int mins: 6;
    int secs: 6;
};
int main(void){
    struct time t = {22,12,20};
    printf("%d:%d:%d\n",t.hours,t.mins,t.secs);
    return 0;
}


Produced: 26 Apr 2019