Week 02
Stacks, Queues, Priority Queues |
Stacks, Queues, PriorityQs | 2/82 |
Computer systems frequently make use of:
... Stacks, Queues, PriorityQs | 3/82 |
All of these data structures have
Example Stack/Queue/PriorityQ Usage | 4/82 |
Starting from data containing one item, insert 3
... Example Stack/Queue/PriorityQ Usage | 5/82 |
Then insert 6
... Example Stack/Queue/PriorityQ Usage | 6/82 |
Then insert 2
... Example Stack/Queue/PriorityQ Usage | 7/82 |
Then remove item
... Example Stack/Queue/PriorityQ Usage | 8/82 |
Then insert 4
... Example Stack/Queue/PriorityQ Usage | 9/82 |
Then remove item
... Example Stack/Queue/PriorityQ Usage | 10/82 |
Then remove item
Priority Queue Data Structure | 11/82 |
Priority Queue: Highest-priority-out protocol
enter
enqueue()
leave
dequeue()
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 Client | 12/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 Queue | 13/82 |
Bit Manipulation |
Bits in Bytes in Words | 15/82 |
Values that we normally treat as atomic can be viewed as bits, e.g.
char
'a'
01100001
short
42
0000000000101010
int
42
0000000000
0000101010
double
sizeof(int)
sizeof(double)
C provides a set of operators that act bit-by-bit on pairs of bytes.
E.g. (10101010 & 11110000)
10100000
C bitwise operators: & | ^ ~ << >>
Binary Constants | 16/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 AND | 17/82 |
The &
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 numbers | 18/82 |
One obvious way to check for odd numbers in C
int isOdd(int n) { return (n%2) == 1; }
Could we use &
Aside: an alternative to the above
#define isOdd(n) ((n)%2) == 1)
What's the difference between the function and the macro?
Bitwise OR | 19/82 |
The |
00100111 OR | 0 1 | 11100011 ----|----- -------- 0 | 0 1 11100111 1 | 1 1
Used for e.g. ensuring that a bit is set
Bitwise NEG | 20/82 |
The ~
~ 00100111 NEG | 0 1 -------- ----|----- 11011000 | 1 0
Used for e.g. creating useful bit patterns
Exercise 3: Bit-manipulation | 21/82 |
Assuming 8-bit quantities and writing answers as 8-bit bit-strings ...
What are the values of the following:
25
65
~0
~1
0xFF
~0xFF
(01010101 & 10101010)
(01010101 | 10101010)
(x & ~x)
(x | ~x)
Bitwise XOR | 22/82 |
The ^
00100111 XOR | 0 1 ^ 11100011 ----|----- -------- 0 | 0 1 11000100 1 | 1 0
Used in e.g. generating random numbers, building adder circuits
Left Shift | 23/82 |
The <<
00100111 << 2 00100111 << 8 -------- -------- 10011100 00000000
** undefined and not recommended for signed
Right Shift | 24/82 |
The >>
00100111 >> 2 00100111 >> 8 -------- -------- 00001001 00000000
** if signed
Exercise 4: Bitwise Operations | 25/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:
(x & y)
(x ^ y)
(x << 1)
(y << 2)
(x >> 1)
(y >> 1)
(x << 3)
(y << 9)
(x >> 8)
Exercise 5: Bit Shifting Exercises | 26/82 |
Memory |
The C View of Data | 28/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 Data | 29/82 |
Variables are examples of computational objects
Each computational object has
&
malloc()
sizeof
... The C View of Data | 30/82 |
Memory regions during C program execution ...
... The C View of Data | 31/82 |
Example of runtime stack during call to h()
Exercise 6: Properties of Variables | 32/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 Data | 33/82 |
Memory = indexed array of bytes
Indexes are "memory addresses" (a.k.a. pointers)
Memory | 34/82 |
Properties of physical memory
... Memory | 35/82 |
Memories can be categorised as big-endian or little-endian
Exercise 7: Endianness | 36/82 |
Write code to print out an int, byte by byte. Is your system big or little endian?
Data Representation |
Data Representation | 38/82 |
Ultimately, memory allows you to
Need to interpret this bit-string as a meaningful value
Data representations provide a way of assigning meaning to bit-strings
Character Data | 39/82 |
Character data has several possible representations (encodings)
The two most common:
(e.g. √ ∑ ∀ ∃ or )
ASCII Character Encoding | 40/82 |
Uses values in the range 0x00
0x7F
Characters partitioned into sequential groups
'\0'
'\n'
'0'
'9'
'A'
'Z'
'a'
'z'
Sequential nature of groups allows for things like (ch - '0')
See man 7 ascii
Unicode | 41/82 |
Basically, a 32-bit representation of a wide range of symbols
UTF-8 Character Encoding | 42/82 |
UTF-8 uses a variable-length encoding as follows
#bytes | #bits | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
1 | 7 | 0xxxxxxx | - | - | - |
2 | 11 | 110xxxxx | 10xxxxxx | - | - |
3 | 16 | 1110xxxx | 10xxxxxx | 10xxxxxx | - |
4 | 21 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
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 Encoding | 43/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 Encoding | 44/82 |
For each of the following symbols, with their Unicode value
U+00026
U+000B5
U+02713
U+02665
Numeric Data | 45/82 |
Numeric data comes in two major forms
Integer Constants | 46/82 |
Four ways to write integer constants in C
42
0
9
0x2A
0
F
052
0
7
0b1010
0
1
123U
unsigned int
123LL
long long int
123S
short int
4294967296
-1U
666666S
078
0b234
Unsigned integers | 47/82 |
The unsigned int
... Unsigned integers | 48/82 |
Value interpreted as binary number
E.g. consider an 8-bit unsigned int
01001101
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 ↔ Decimal | 49/82 |
When working at the machine level
00001001
00001101
00101010
00110011
11001100
00001001
00001101
00101010
00110011
11001100
Signed integers | 50/82 |
The int
... Signed integers | 51/82 |
Several possible representations for negative values
Examples: representations of (8-bit) -5 (where 5 is 00000101
10000101
11111010
11111011
... Signed integers | 52/82 |
Signed magnitude: Easy to form -X from X ... XOR in high-order bit
A problem (using 8-bit int
00000000
10000000
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 integers | 53/82 |
Ones complement: Easy to form -X from X ... NEG all bits
A problem (using 8-bit int
00000000
11111111
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 integers | 54/82 |
Twos complement: to form -X from X ... NEG all bits, then add 1
Now have only one representation for zero (00000000
-0
~00000000+1
11111111+1
00000000
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 Conversion | 55/82 |
Convert these decimal numbers to 8-bit 2's complement binary:
11111111
11010110
10111101
Pointers | 56/82 |
Pointers represent memory addresses/locations
sizeof(int *)
sizeof(char *)
sizeof(double *)
sizeof(struct X *)
... Pointers | 57/82 |
Code and data is aligned and is machine dependant. For example:
char
int
%4 == 0
double
%8 == 0
(char *)
(int *)
%4 == 0
(double *)
%8 == 0
Exercise 11: Valid Pointers | 58/82 |
Which of the following are likely to be valid pointers
0x00000000 0x00001000 0x00001001 0x7f000000 0x7f000001 0x7f000004
to objects of the following types
Fractions in different Bases | 59/82 |
The decimal fraction 0.75 means
.
... Fractions in different Bases | 60/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 Bases | 61/82 |
For example if we want to convert 0.3125 to base 2
Exercise 12: Fractions: Decimal → Binary | 62/82 |
Convert the following decimal values into binary
Floating Point Numbers | 63/82 |
Floating point numbers model a (tiny) subset of ℝ
float
double
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 Numbers | 64/82 |
IEEE 754 standard ...
INFINITY
NAN
In binary, exponent is represented relative to a bias value B
... Floating Point Numbers | 65/82 |
Example of normalising the fraction part in binary:
1010.1011
1.0101011×2011
1010.1011
1.0101011×2011
1
Example of determining the exponent in binary:
00000001
11111110
... Floating Point Numbers | 66/82 |
Special cases:
0
infinity
NaN
... Floating Point Numbers | 67/82 |
Internal structure of floating point values
More complex representation than int
... Floating Point Numbers | 68/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 ↔ Decimal | 69/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
Arrays | 70/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
... Arrays | 71/82 |
Assuming an array declaration like Type v[
]
sizeof(
)
v
&v[0]
v[i]
*(v+i)
char
'\0'
'\0'
'\0'
... Arrays | 72/82 |
When arrays are "passed" to a function, actually pass &a[0]
... Arrays | 73/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)
Structs | 74/82 |
Structs are defined to have a number of components
... Structs | 75/82 |
To ensure alignment for the fields and for the struct itself, internal "padding" may be needed
Padding wastes space; You can try to re-order fields to minimise waste.
Unions | 76/82 |
A union
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).
... Unions | 77/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
... Unions | 78/82 |
Common use of union
#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: Union | 79/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 Fields | 80/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 Fields | 81/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-fields | 82/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