0% found this document useful (0 votes)
11 views

178 Data Structures Notes

Uploaded by

Ryan Wilder
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views

178 Data Structures Notes

Uploaded by

Ryan Wilder
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 41

178 – Data Structures and Algorithms

Week 1

Lect 1 - 08/01/24
N/A

Lect 2 – 10/01/24

Components of a Computer
- Processor
o Device to fetch, decode and execute instructions.
o Possibly fetching and storing data as part of execution.
o Store
 The processor puts out the data to store at that address and asserts a
“WRITE” command on the control line. Details of signal timing is ignored.
o Load
 The processor puts out an address and asserts a “READ” command on the
control line. Now it must wait for some device to recognize the address
and put data on the data lines.
o Designed to work with larger units of info (2, 4, 8, 16 bytes per unit) called words.
o Is the part of the computers that does the work / follows the instructions.
o Has tiny a set of memory called registers.
 NI (next instruction): keeps track of where the next instruction is in
memory.
o Diff kinds of processors will have different numbers of registers
 Some can be used for anything and some (like NI) have special purpose
o A device that does the following unit turned off :

o The compiler determined addresses for variables


o Depending on the specific language the processor uses the code could be names
that start with R refer to registers.
- I/O device
o A device that allows the entry or exit of data from the computer.
o Disk devices are sometimes considered a 5th basic piece -Storage- but they are
just another I/O device.
o Can be thought of the same way as memory.
o They have addresses and some number of internal device registers which can be
thought of as the “device memory.”
o The processor uses these registers the same way it uses memory.
 Write info to them or read info from them

- Memory
o Used to store all the instructions and data that a program can use directly
o Where instructions and data are kept as bits.
o Key details are that memory access is through Load and Store
o Store
 Assume memory recognizes the address – it copies the data into a
location within.
o Load
 Assume memory recognizes the address – it copies the data from location
within to the data lines.
o Has M locations [0 to M-1] called addresses.
o Each location can store some number of bits.
 A location that can store 8 bits is called a byte.
o Memory is given a size in bytes (KB< MB< GB<TB<PB).
 A bit can be in 1 of 2 states (0 or 1)
 Only 256 patterns that can be stored in a byte all the possible
combination of 0 or 1 for each bit.

- Interconnection Bus
o A mechanism to connect processor, memory, and I/O device.
o The path that all info travels over.
o One set of wires is the address.
 How the processor indicated which location it wants to read or write to.
o One set of wires is the data.
 Information read from memory or information being sent to memory for
storage.
 Sometimes they are the same wire; address encoded at the start and
used for data in the second part.
o Final set of wires are the control lines.
 Not coved much in this course.

Into to Arduino
- Microprocessors
o Small limited capability computers in a single chip package.
o Contains:
 Processing core
 Flash memory and RAM
 I/O peripherals
 Peripherals like clocks, timers etc.
o Used in applications that benefit from small-sized or low-cost computing; where
fill fledged computers are too expensive or overkill.
o Form the centre of an embedded system.

- Arduino
o Open-source electronics and computing platform ecosystem
o Cheap and easy to learn without formal training.
o Can simply plug into a computer via USB to start programming.
o Applications
 Electronic door lock
 Alarm clock
 3D printer
 Auto watering system
 Mobile robots
 Etc.

- Arduino Uno R3
o Uses an ATMEL ATmega328P
 Low power 8-bit micro controller up to 16 MHz clock speed
 Rough estimate of how many lines of byte code instructions it can run per
second.

- Arduino Uno R3 Memory


o Flash memory
 Where Arduino sketch (i.e., program) is stored.
 32 KB of memory, through the bootloader takes up 0.5KB of this space.
o Static Random Access Memory (SRAM)
 2KB
 Where the sketch creates and manipulates variables when it runs.
o Electrically erasable programmable read-only memory (EEPROM)
 Acts like a tiny hard drive
 Requires use of the Arduino EEPROM library
 1 KB

- Arduino R3 inputs and outputs


o 20 Digital pins (D0-D19)
 Each pin can act as an input or output using the pinModel() function in
the Arduino sketch.
o Serial (D0 (RX) and D1(TX))
 Used to receive (RX) and transmit (TX) TTL serial data.
 Allow you to send and receive simple textual data to and from the board.
 Can interface with the serial monitor in the Arduino IDE.
o External Interrupts (D2 and D3)
 These pins can be configured to trigger an interrupt on a low value, a
rising or falling edge, or a change in value.
 Function: attachInterrupt()
o PWM (D3, D5, D6, D9, D10, and D11)
 Provide 8-bit PWM output with the analogWrite() function.
o SPI (D10 (SS), D11(MOSI), D12(MISO), D13(SCK))
 Support serial peripheral interface (SPI) communication using the SPI
library.
o LED (D13)
 There is a built-in LED driven by digital pin 13.
 When the pin is HIGH value, the LED is on, when the pin is LOW value, it’s
off.
o 6 analog pins (A0-A5)
 Shared with D14-D19
 Supports 10-bit (1024 resolution) analog to digital conversion (ADC) using
the analogRead() function.
o Power and Other Pins
o VIN
 The input voltage to the Arduino board when it’s using an external power
source.
 You can supply voltage via the power jack, access it through this pin.
o 3V3
 A regulated 3.3V supply.
o 5V
 The regulated power supply used to power the micro controller and other
components on the board.
 This can come either from VIN via an on-board regulator or be supplied
by USB or another regulated 5V supply.
o GND
 Ground pins
o AREF
 Reference voltage for the analog inputs.
 Used with analogReference ()
o RESET
 Bring this LOW to reset the microcontroller.
 Typically used to add a reset button to shields which block the one on the
board.
 Alternatively, UNOs are equipped with a push button on this line that
resets the microcontroller.

- Programming an Arduino
o Based on c and c++
o Can also use object programing but not in this course.
o https://ww.arduino.cc/refernce/en/

- Essential elements
o #include <arduino.h>
 Contains all of the Arduino functions and sub-libraries.
o 2 mandatory functions: setup () and loop ()
o set up ()
 called only once as soon as the program starts.
o Void loop ()
 Called indefinitely; as soon as the loop is finished it will be called again.

- Variable initialization in Arduino


o As soon as a variable is declared Arduino will initialize it a base value
 Otherwise automatically initialized it a base value i.e., 0, 0.0, or null for
pointers.

- Automatic function prototypes (Function declaration)


o A function prototype is simply the declaration of a function that specifies
function’s name, parameters (inputs), and return type.
o Functions can be used before declaration, but it isn’t good practice.
 Declare at beginning of code.

Programming Paradigm

- Programming format
o Treat as a top-down problem decomposition
o Ex.
 Problem: want to eat at 6pm
 Solution (top level): make dinner.
 Decompose one level:
 Plan menu
 purchase food items.
 Prepare food items.
 Serve food and eat.
 Take each first level step and treat as a problem to decompose.
 This brake down may not be optimal because the steps are linked. What if
the store does not sell what you are looking for
 A loop should be used to ensure all steps can be executed.
 Do {
o Plan meal
o purchase ingredients
 } until all ingredients are purchased.
 Ensure that the first state and end state are independent / steps are de-
coupled.
o Only 2 variations of steps
 It is a sequence of smaller steps.
 It is a loop; the work you repeat and the condition under which you
repeat.
o Ex. Tree planting
 Problem statement: plant a tree.
 1at level steps
 Get tree.
 Dig hole big enough for the tree roots.
 Put tree in hole.
 Put dirt in hole.
 Water the tree
 Now we have 5 smaller steps which can be turned into code
 Consider step 2
 This looks like a loop
 While (hole is too small){
o Scoop out one shovel of dirt
 )

Building and Compiling

- Building
o C is a high-level language and C programs need to be converted into low level
machine executable code
o Building is a process that converts a C program into and Executable.
o There are 4 stages to obtaining an executable file from a C program:
 1. Preprocessing
 2. Compilation
 3. Assembly
 4. Linking

- Preprocessing
o The first phase through which source code is passed.
o Includes:
 Removal of comments
 Expansion of Macros
 Expansion of the included files
 Conditional compilation
o The preprocessed output is stored in the filename.i
o Macros structures – definitions / declarations outside of main code.
o Lines that start with # are called preprocessor directives.
o #include <filename>
 Allows processor to insert contents of filename at the location where the
#include directive is found.
 angle brackets around the filename tell the preprocessor to look for the
file in the include path list.
o “filepath” the processor searches the file in the current directory first, then the
include path.
o #define is a way of specifying a text substitution.
 The first term is the text that will be replaced, and the second term is the
replacing text.
 Can be parameterized.
 Ex. #define ZERO 0

- Compiling
o The next step is to compile filename.i and produce an intermediate compiled
output file filename.s
 This file is in assembly level instructions.

- Assembly
o Contains machine level instructions.
o Filename.s is taken as input and turned into filename.obj (binary) by assembler.

- Linking
o The final phase in which all the linking of function calls with their definitions are
done.
o The linker knows where all the functions are implemented.
- Ex. Typical first C program

- Ex. A larger C program


- Arduino Build Process
o The build process still involves a preprocessor, compiler, assembler, and linker
which have roughly the same functions.
 The processor turns a sketch into a C++ program.
 The compiler and assembler still turns human-readable code into
machine readable instructions.
 The code gets linked against standard Arduino libraries, resulting in a hex
file containing specific bytes that need to be written to flash memory
onboard the Arduino.
o This hex file is uploaded over the usb connection via the bootloader already on
the microcontroller
 The bootloader is a small program that runs when you reset or turn on an
Arduino. The main function of the bootloader is to wait for a new
program to be sent from the computer and writes it to flash memory.
 Without a bootloader, you’ll need to program the Arduino using an
external device called a programmer.

- Ex. Multiple source modules


Lect 3 12/1/24

Fundamental Dara items in C

- Recall – Processor and Memory


o Memory is used to store instructions and data.
o Basic memory unit is the byte. This is usually the smallest unit of
memory that has its own address.
o Depending on the processor, a word could be a two, four, or eight
byte grouping.

- Data – What we want to know


o Units of data – numbers, characters, Booleans, language defined,
user-defined – and how they are stored in underlying memory units
(bits, bytes, words).
o Numbers – integer/real, signed/unsigned (interpretation of bit patterns)
o Characters – essentially bit patterns that have meanings assigned (e.g. 0x32
or 5010 is interpreted as the character ‘2’)
o Booleans – convention. 0 – false, non-zero true.
o User defined variable types (typedef)
o Name type and specify set of values (an enumeration) for that type.
o Describe a collection (struct, union, etc.)

- Relation – Processor and Memory


o Processor designed to perform computations with some unit of
memory. If a computer has a 32-bit processor, it usually means the
processor has registers that are 32 bits wide and it has an Arithmetic
and Logic Unit (ALU) that does operations on 32-bit quantities – e.g.,
add two 32-bit integers to give a 32-bit result. So, the processor sets
the size of a word.
o Integer multiply/divide instruction produce a double-word output
often occupying two registers.
 Multiplying two 32-bit words gets you a 64-bit result
 Dividing gets you a one-word quotient and a one-word remainder.

- Language Data Elements vs Processor


o Language designer decides how best to map language data types to
memory units, in order to have efficient and effective computing
o So, usually, an integer will be the size that the (expected) processor
can deal with in as few steps as possible
o For example, on a 32-bit processor, expect an int to be 32 bits.
o Warning: This means that if you port code from one environment to
another, you may have to modify the types of some variables if the
environment you are porting to has a smaller word size.

- C Basic Data Items (types)


• int – holds typical integer (at least 16 [32] bits)
• char – holds one character (byte [2 bytes])
• float – floating point number – at least 32 bits [64, 128 bits]
• double – more precise floating point – usually 64 bits [128, 256 bits]
• Conversion from floating point to decimals (vice-versa)
• https://www.cs.cornell.edu/~tomf/notes/cps104/floating.html

- Strong Type Rules


o Recall that we mentioned assembly language basically only has bytes
and words as types – programmers had to figure out what kind of
data was being stored and remember to use it consistently.
o C was meant to be a replacement for assembly and make
programming easier: it didn’t interfere with anything the programmer
tries to do, i.e., it has types, but doesn’t impose type rules.
o C has evolved since then as type rules are now enforced.
o C is strongly typed in that the variable type must be specified when
declaring the variable.
 However, C can also be regarded as weakly typed because users can
convert
data types through a cast and without compiler errors.

- Container Size Awareness


o Often, you can remain unaware of the container size of your data
items.
 Especially if you do all your work with one kind of processor. This will
probably be the case when you work with your Arduino.
o However, knowledge of container size becomes important if you have
to match a container size to a device register, or match data that was
laid out using a different processor with smaller containers.

- Issues with char (1)


o In ancient times, char could be a byte:
52 symbols for upper/lower case letters
10 symbols for numbers
16 symbols for punctuation
____________________________________________________
78 symbols < 128 which means that a char could be a byte
o Encoding schemes like American Standard Code for Information
Interchange (ASCII) or Extended Binary Coded Decimal Interchange
Code (EBCDIC) used a byte for a char.

- Issues with char (2)


o Non-English European languages have accents, umlauts, etc. –
extended ASCII to 256 symbols to cover them. However, this still fits
within a byte.
o However, some languages need more – so some compilers will
support wide-characters (16 bits or 2 bytes)
o There are character formats that require more than 2 bytes.

- Specifiers for int, char


o long int – “may” be bigger than int.
o short int – “may” be smaller than int.
o unsigned int – all bits used for magnitude, none for sign.
o unsigned char – all bits used for magnitude.
 Note: When you perform arithmetic operations on characters, the C
language automatically converts them into their corresponding integer
values, which are the ASCII values of those characters. This enables
various text-processing tasks.
 If char size is a byte, a char would have integer range of -128 to +127
 unsigned char would have a range from 0 to 255.

- Insert pics from phone

- Ways of Expressing Integer Values


o Typical Integers: 2071, -945
o Hexadecimal: 0xA7F
o Octal: 0376 (no longer used)
o Binary: 0b0010111011000000
o Long Integer: 8642569923L
 Alternate: (long int) 8642569923
 Note: C language definition allows for lower case l, but please avoid as
you can get numbers that look like 613l

- Expressing char Values


‘A’, ‘b’, ‘?’
‘\n’, ‘\r’, ‘\b’, ‘\t’, ‘\f’, ‘\\’, ‘\’’, ‘\0’
‘\033’, 0xA5, 27
o \ Is an escape character which changes the meaning of the next
character
Escape sequences are used to format the output text and are not

generally
displayed on the screen.
o Remember, C is not strongly typed, so char just means “often used to
hold characters but sometimes used to hold integers with a small
range.”

Tutorial

Pointers:
Int *var1; pointer
Int **var2; pointerToPointer

Printf(“%x”, **pointerToPointer) -> what is stored in the memory address of the variable.
Printf(“%x”, *pointerToPointer) -> memory address of the first pointer.
Printf(“%x”, pointerToPointer) -> memory address of pointerToPointer (should be the same as
the address of the variable because it is assigned to it).

- When using a pointer to cast different specifier types it is easier to simply cast it later
- Ex
- //void pointers
printf("void pointers\n");
int a = 7;
float b = 7.6;

void *ptr;
ptr = &a;
printf("Integer variable is = %d\n", *( (int*) ptr) );
ptr = &b;
printf("Float variable is = %f\n", *( (float*) ptr) );
Week 2

Lect 1 12/01/24

Pointers

Fundamental Data Items in C (continue)

Type Conversions (1)


 What does the following mean?
 int i; char c;
i=c;
c=i;
 i=c: this is easy, copies bits from a smaller container to a larger one.
 Note that there is a sign extension issue: if the char value has the largest/top bit set
(=1), it is considered a negative and thus i will be negative. However, if the char was
explicitly stated as an unsigned char c, then the result will only be positive.
 c=i: this one may cause loss of bits/information. To explicitly show that
this is what you want to do, you can perform a cast:
 c = (char) i;

Type Conversions (2)

- Float and integer conversions


o float f; int i;
f=i; // simple case, int is “promoted” to float
i=trunc(f); // throw away fractional part
i=round(f); // essentially adds 0.5 and performs trunc()
- Internally, bit pattern for an int and a float representing the same
number is different. Floating point numbers use bit patterns
representing an exponent part, and a magnitude part.

Conversions (3)

- Assignment of values of one type to a variable of another type is


allowed
o int k, j;
char p, q;
p = ‘A’; q = 0x7F;
k = p; j = q; // bigger container (more bits), same
value
k = 25; j = 1023; // 1023 = 0011 1111 1111
p = k; q = j; // lose bits ( q = -1!! Why?)
- Some compilers will warn you about possible loss of data and force
you to use casts.
Conversions (4)
- C generally assumes that programmer knows what they’re doing and,
although it may warn about possible problems, it provides means to
do what programmer wants.
o int j, k, m;
int *ptr;
ptr = &j; // assign address to pointer
k = ptr; // OK in K&R; error in ANSI-C
k = (int)ptr; // OK in ANSI-C
ptr = (int *)m;

Conversions (5)
- Legitimate use of a pointer cast: you know the address of some
specialized memory or register.
o #define DEVICE_ADDRESS 0xFF00100
int *p
p = DEVICE_ADDRESS; //Error in ANSI-C
p = (int *) DEVICE_ADDRESS; // OK in ANSI-C

Sets of Data – Arrays


- Arrays are sets of identical data items stored in adjacent memory locations:
o int g[100]; // Creates an array of 100 integers g[0] to g[99]
char s[50]; // Creates an array of 50 chars s[0] to s[49]
- If an integer occupies 4 bytes, and the memory for g[] starts at memory address
1000, than g[] references the memory address 1000, g[1] starts at 1004, g[2]
starts at 10008, etc.

Strings
- A string is a sequence of character, identified by double quote marks: “hello”
- This really means six characters in a row
o ‘h’ ‘e’ ‘l’ ‘l’ ‘o’ ‘\0’
- Strings in C are stored in arrays of characters: char str [10]
o Warning: a “C-string” is not the same as a string type in C++ or most other
programming languages

Declaring Variables (1)


- int j, k, m;
char p, q, r;
float f;
short int a =5; // a is both declared and initialized
unsigned char b = ‘Z’;
float pi = 3.14159;
double abigname99 = -3.7e-4;
Declaring Variables (2)
- You cannot assume much about the location of variables in memory
- For example, int j, k, m; just means “give me three places for integers
and remember that I want to label one j, another k, and the third m”
- There are no defined relationship between the address of the three
integers (i.e., they are not guaranteed to be contiguous)
o Only containers in an array have defined address relations.

Scope and Lifetime (1)


- Scope – refers to portions of static code that a variable name has
meaning – essentially areas where a compiler says a name is valid.
- Lifetime – refers to length of time that a variable exists during the
program’s execution. Local variables only exist during time that a a
function’s code is being executed – any may occupy memory that was
used earlier for another function’s locals.

Scope and Lifetime (2)


- int g; // global scope
void main (void) {
o int lm; // local to main()
for(;;) {
 int lb; // local to the body of for-loop
o }
- } // lb “disappears” here
- int h; // also global, but cannot be referenced within main()
int fun (int a, int b) { // a and b are local to fun()
o int lf, lf2; // so are lf and lf2
...
- } // a, b, lf, and lf2 disappear here

- Variables declared within a block disappear when the block is exited.

Other Features of C

Enum – Type with “Named” Values


- Create variable type with limited set of data values
- enum colors {RED, GREEN, BLUE};
enum colors electron_gun;
electron_gun = RED;
enum errors {NOERROR, ERR_NOFILE, ERR_OTHER};
enum errors result;
result = ERR_NOFILE;
Enum – Details
- Basically, an enum is an unsigned int with the first enumerated value
mapped to 0, second value mapped to 1 and so on.
- You can explicitly define the mapping:
o enum results {
 rslt1 = 48, rslt2, rslt3, rslt4
o };
o enum item {
 fuse = 21, connector = 33, terminal, breaker = 66
o };

Typedef (1)
- You are able to add your own types to your programs to make code more
readable.
o typedef float real; // real is now the same as float
real r1, r2; // r1 and r2 are type real (float)
- You can also define multiple types at once
o typedef unsigned char byte, uchar, *pbyte;
byte b;
pbyte pb;
- byte and uchar are now alternate names for an unsigned char.
More interestingly, pbyte is a pointer to an unsigned char.

Typedef (2)
- enum colors {RED, GREEN, BLUE};
enum colors electron_gun;
electron_gun = RED;

- enum colors {RED, GREEN, BLUE};


typedef enum colors Color;
Color electron_gun;
electron_gun = RED;

- typedef enum color {RED, GREEN, BLUE} Color;


// or
typedef enum {RED, GREEN, BLUE} Color;

Operators

Arithmetic Operators
a = b + c;
a = b – c;
a = b * c;
a = b / c; // integer if both int; double
// otherwise (note conversions)
a = b % c; // mod (or remainder)
a += 4; // same as a = a + 4;
a -= 7; // same as a = a – 7;
a = -7; // Careful!! Not the same as prior line
a = ++b; // b is incremented, then value assigned to a
a = b++; // value of b assigned to a, then b incremented

// There is no exponent or power operator

Autoincrement and Autodecrement (1)

- int a, b, *c, d;
++b;
b++;
--c;
c--;
a = b++ + --d;
- Autoincrement and autodecrement adds or subtracts one unit of
value. In the case of ints and chars, this is 1. In the case of a pointer, it
depends on what the pointer points to and how the underlying
computer deals with addresses.
o This concept does not apply with floats or doubles.

Autoincrement and Autodecrement (2)


- a = ++b + c--; // Unambiguous
a = ++b + ++b; // May not work as expected.
a = ++b + --b; // Undefined behavior – do not use
a = b++ + --b; // Also undefined behavior – do not use
Everything Computes a Value

- int a, b, c, k, j, m;
a = (b = 7) + (c = 9); // returns 16
- Comma operator – isn’t used much but is a standard C language component. The
expression appears as (exp1, exp2) where exp1 is evaluated, then exp2. The
result of exp2 is returned.
o k = (j=j+1, m=4); // returns 4
o The comma operator is useful for doing several things in loops.
o int s = 0;
while(++s, s < 5) {...}
Comparison Operators
- Should be pretty straightforward:
o ==, !=, <, <=, > , >=
o flag = a > b // Flag evaluates to 0 (false) or non-zero (true).
// For most compilers, you’ll get a one if true,
// but sometimes you may get numeric difference
// between a and b.
- In this course, assume that true is equivalent to 1.
- Hint: you can use the following macros:
o #define FALSE 0
#define TRUE 1

Comparison Operators
- Warning: Never compare result of a comparison to true or false.
o #define FALSE 0
#define TRUE 1
...
if(a>b) { ... } // correct
if((a>b) == TRUE){ ... } // incorrect
- Avoid equality checks with floating point.

Logical Operators – Operate on Booleans


- Unary Operators
o Logical Not !
- Binary Operators
o Logical And &&
o Logical Or||
 (a > 5) && (b < 10)
d || (e-5)

- Logical operators always result in true (!0) or false (0) result.

Lect 2 17/01/24

Flow Contorl

Hybrid Storage in C

Storing Data
- We have looked at arrays – a way of storing a collection of
identical items.
- Now look at structures – collections of heterogeneous
(dissimilar) items
- Also look at unions – multiple ways to look at the same
“data”.

Heterogeneous Data Collections (1)

- Structures are identified in C by the keyword struct


o struct time {
unsigned char hour;
int minute;
int second;
char am_or_pm;
};
- ‘hour’, ‘minute’, ‘second’, and ‘am_or_pm’ are called the fields of the
structure.

Heterogeneous Data Collections (2)

- The sizeof operator can be used to determine how much space


one of these structures takes.
o struct time {
unsigned char hour;
int minute;
int second;
char am_or_pm;
};
...
sz= sizeof(struct time);
- Hint: The sizeof operator
works on any type – it gives the
number of bytes occupied by an
instance of that type.
o The size of a structure in C is not the sum
of the sizes of its member
o Padding bytes added by compiler

Heterogeneous Data Collections (3)

- Variables of type struct time are declared as shown:


o struct time {
unsigned char hour;
int minute;
int second;
char am_or_pm;
};
...
struct time now, later;
o struct time {
unsigned char hour;
int minute;
int second;
char am_or_pm;
} now, later;

Heterogeneous Data Collections (4)

- Array of time structures


o struct time observations [100];
- You can use typedef to create a synonym for struct time
o typedef struct time Time;
o
Time observations [100];
// the above is equivalent to:
// struct time observations [100];
- Structures can be components of other structures
o struct date_time {
 unsigned int year;
unsigned char month;
unsigned char day;
Time time;
o };

Using Structures – Accessing Fields (1)

- Declare a structure with year, month, and day:


o struct date_time {
unsigned int year;
unsigned char month;
unsigned char day;
} today, *pdate;
- Access fields in a variable known by its name:
o today.year = 2022;
today.month = ‘1’;
today.day = ’17’;
- Assign address of “today” to (pointer) pdate,
then access the fields via pointer:
o Hint: pdate->year is equivalent to *pdate.year.
‘->’ is known as the arrow operator.
o pdate = &today;
pdate->year = 2022;
pdate->month = ‘1’;
pdate->day = ‘12’;

Using Structures – Accessing Fiels (2)

- today.year
o Refers to the year field in a struct date variable called today.
- pdate->year
o Refers to the year field in a struct date variable pointed to by the
struct date pointer called pdate.
- Warning: using ‘->’ when ‘.’ should be used is a common
programming
syntax error.
Memory Allocation in C

Getting Memory for Storage (1)

- Three types of memory available for C programs: Stack, Heap, and


Static
- Stack
o Used for static memory allocation that is performed at compile-time
 While executing a program, the static memory allocation cannot be changed
o Accessing this memory is fast
o All memory allocations that we have seen so far in this course use the
stack
o Memory allocations and deallocations (garbage collection) are handled
automatically by the CPU
 E.g., A variable declared and initialized inside a code block {...} will be placed on
the
stack. Once the variables falls out of scope, the memory it occupied is
automatically
freed.

Getting Memory for Storage (2)

- Heap
o Allocations and deallocations are performed explicitly by the programmer
and at run time – you can allocate a block at any time and free a block at
any time
o Accessing this memory is slower
o Much larger than stack memory (usually only limited by the physical
memory
available)
o Requires pointers to access
- Static
o Reserved for global variables, or variables that have the static qualifier
which allows the scope of the variable to extend beyond it’s code block

Getting Memory for Storage (3)


- Sometimes, it is convenient to allocate memory for storing instances
of variables not at compile time, but at run time.
o struct date_time {
 unsigned int year;
unsigned char month;
unsigned char day;
o } today, *pdate;
o
pdate = (struct date_time *)malloc(sizeof(struct date_time));
- Here we use a function malloc() to get a chunk of memory big
enough to hold a struct date_time and assign that memory’s
address to pdate.

Getting Memory for Storage (4)

- malloc is used whenever dynamic memory allocation is needed at


run time.
o When you don’t know the amount of memory you need during compile
time; e.g., when the size of an array is passed in through user input at
run time.
o When you need to allocate objects that must exist beyond the lifetime of
execution of the current block
o When you need to allocate a large amount of memory, greater than the
memory available on the stack (>1MB)
- SUPER IMPORTANT: Dynamically allocated memory allocation
created through malloc()MUST be released explicitly using
free().

Getting Memory for Storage (5)

- Stack
// static memory allocation in
// RAM

int x = 1;
char y[100];
float z = 1.255;
- Heap
// dynamic memory allocation and
// deallocation in RAM

int* m = (int*)malloc(sizeof(int));
free(m);

Nested Structures Example


typedef struct time Time; // from previous slides

struct date_time {
unsigned int year;
unsigned char month;
unsigned char day;
Time time; // Time struct within date_time struct
} dt, *pdt;

dt.year = 2021; // assign value to field in dt


dt.time.hour = 1; // assign value to hour field in time
// field in dt
pdt = (struct date_time *) malloc (sizeof struct date_time);
// date_time struct was allocated
pdt -> time.minute = 16;

free(pdt);

Unions – Alternative views of the Same Memory (1)

- Sometimes, we need/want to be able to treat a piece of memory in


two or more different ways.
- There is one place in memory with a bunch of bits.
- We want to treat that bunch of bits as though it were one type
sometimes, and a different type other times.
Unions – Alternative views of the Same Memory (2)

- The union definition allows for storing different data types in the
same memory location.
union IP4Address {
unsigned int fulladdress;
unsigned char octets [4];
};

union IP4Address iaddr;


iaddr.octets[0] = 192;
iaddr.octets[1] = 168;
iaddr.octets[2] = 0;
iaddr.octets[3] = 1;

printf(“Full address: %d\n”, iaddr.fulladdress);

- Interpretation: An IP4Address is a block of memory big enough to hold the larger of an unsigned int or an
array of 4 unsigned characters. On a 32-bit computer, these would be the same size. It is only one block, but
if we call it fulladdress, then we can do integer operations, and if we call it octets, we can work as
though it is 4 bytes.

Unions – Alternative views of the Same Memory (3)


union int_or_pointer {
unsigned int address; // can put an int value here
unsigned int *plocation; // which can be used as though it is a
// pointer/address
};

// Let’s say the specification for the computer says there is a device at
// address 0xFF100040 that can be turned on by writing the value 1 to it.

union int_or_pointer iaddr;

// The following turns on the device at 0xFF100040

iaddr.address = 0xFF100040;
*iaddr.plocation = 1;

Flow Control

Flow Control in Programs

- Choices
o One or none
o One or the other
o One of many
- Repetition (Iteration)
o Definite iteration
o Indefinite iteration
 Pre-check
 Post-check

If: Simple Choices (1)

- Form:
if(<condition>) <statement>;

//or

if(<condition>) {
<statement>;
}
- If <condition> is true (non-zero), then the <statement> will be
executed; otherwise <statement> will be skipped.

if (a > 5) printf (“a greater than 5\n”);

if (temp > ac_setpoint)


ac_status = 1;
if (temp > ac_setpoint) {
ac_status = 1;
ac_control_register |= 0x01;
printf (“AC turned on\n”);
}

Aside: Debate on Where to put curly brackets


if (condition) {
statement;
}
if (condition)
{
statement;
}
if (condition)
{
statement;
}
- An important reason for high level languages is to make it easier for people other than
the original programmer to figure out what the program is trying to do.
- To this end, making blocks of code obvious is clearly important.
- Choose a format and stick with it; follow the organizational norm.

if-else: Two-Way Choice (1)


- Form:
if(<condition>) {
{<expression1>}
} else {
{<expresson2>}
}
- Note: if there is only one statement in each of the true/false paths,
you do not need the brackets. However, if you add statements or
additional lines later, these brackets need to be added in to define
scope of each pathway.

if-else: Two-Way Choice (2) – Example

if (temp > ac_setpoint) {


ac_status = 1;
ac_control_register |= 0x01; //set AC on (last bit =1)
printf (“AC turned on\n”);
} else {
ac_status = 0;
ac_control_register &= 0xFE; //set AC off (last bit =0)
printf (“AC turned off\n”);
}

if-else: Two-Way Choice (3) – Example

if (temp > ac_setpoint) {


// Temperature above AC setpoint
if (ac_status == 0) {
ac_status = 1;
ac_control_register |= 0x01;
printf (“AC turned on\n”);
} //endif AC needs to be turned on
} else {
// Temperature at or below AC setpoint
if (ac_status == 1) {
ac_status = 0;
ac_control_register &= 0xFE;
printf (“AC turned off\n”);
} //endif AC needs to be turned off
}

Conditional Expression (1)

- Conditional (ternary) expressions are a compressed/shorthand


method of coding if-else:
if(<condition>)
{<expression1>}
else
{<expresson2>}
- It assigns one of two values based on the value of a Boolean
condition and can be written in the format:
<condition>?<expr_if_condition_is_true>:<expr_if_condition_is_false>;

Conditional Expression (2) – Example

a = b > 50 ? 10 : 4;
- If b is greater than 50, a=10, else a=5. So depending on the value of
b, the value assigned to a is either 10 or 4.
- Equivalent to:
if (b > 50) {
a = 10;
} else {
a = 4;
}

switch: Multiple Choices

switch (ch) {
case ‘A’:
statement_a;
break;
case ‘B’:
statement_b;
break;
case ‘C’:
statement_c;
break;
default:
statement_default;
}

- Suppose the break statement in case ‘B’ was missing. The code for case B would do statement_b then
statement_default.
- Switch is based on the value of ch. If ch=‘A’, the code goes to case ‘A’
- Default statement in case ch doesn’t match any of the preceding cases.

Iteration

- Iteration or repetition covers situations where an overall goal is


accomplished by repeating some set of steps
- Definite Iteration– you know in advance how many times you will do
the steps
- Indefinite Iteration– you know how to recognize when you’re done
- So, if I ask you to move 4 boxes, you will do a definite iteration:
for (int box=1; box <= 4; box++) move box;
- But if I ask you to empty all the boxes from a truck, this is indefinite:
while (truck is not empty) unload box;

for: Defined Iteration (1)


for (int i=0; i<10; i++) {
statement1;
statement2;
statement3;
}
- Blockof statements 1 through 3 is executed 10 times, with the
counter i taking the values 0, 1, 2, ..., 7, 8, 9. When the loop
terminates the value of i is 10

for: Defined Iteration (2) – Examples

for (i=0; i<10; i++)


printf (“Value of i is: %d\n”, i);
for (i=0; i<10; i++) {
printf (“Value of i is: %d\n”, i++); //What happens here?
}
for (i=0,j=0; i<10; i++,j*=2) {
printf (“Value of i,j are: %d %d\n”, i, j);
}

for: Defined Iteration (3) – Examples

for (i=0,p=&k; i<100; i+=2,p++){}


for (i=0; i<100;){}
for (;i<100;){}
for (i=0; ;i++){}
for (;;){}

- Versions of for loops that don’t include continuance need to have


some way of terminating coded within the loop (remember break?).
Versions with no end-loop adjust need adjustment within.

for: Defined Iteration (4) – Examples

char str [100];


int i, *p;

<somehow some string gets placed in str>

for (i=0,p=str; i<100; i++, p++) {


if (*p == ‘\0’) break;
switch (*p) {
case ‘a’:
case ‘A’:
<do something for letter A>
case ‘b’:
case ‘B’:
<etc.>
} // end switch
} // end for

Do-while: Indefinite Iteration with Postcheck


Do
statement;
while (condition);
next_statement;

do {
statement1;
statement2;
} while (condition);
next_statement;
- Execute statement/block of statements. Evaluate condition. If true
(non-zero), go back and do statement/block again. If false (0), go on
to next_statement.

While(TRUE)- Infinite Loops (1)

- In embedded programming (e.g., Arduino), you’ll see code that has


infinite looping. Either one or more statements include decisions to
terminate the loop, or the program stops because the power is
turned off.
- Form:
#define TRUE 1
...
while (TRUE){
statement1;
statement2;
statement3;
statement4;
}
OR
for (;;){
statement1;
statement2;
statement3;
statement4;
}

While(TRUE)- Infinite Loops (2) – Example

- Example: Automobile Air Bag Controller


int main(void)
{
// initial code checks out system and verifies that it works
while (1) {
read accelerometers;
if (readings indicate crash) break;
}
// only way to get here is if crash detected
deploy_airbags;
// no more to do until car repaired (loop will terminate
// when ignition turned off and power removed from circuit)
while (1);
}

While(TRUE)- Exit Loop from Within


#define TRUE 1
...
while (TRUE){
statement1;
statement2;
if (condition2) break;
statement3;
statement4;
}
next_statement;
- Begin executing statements in loop body - statement1 then statement2. Evaluate
condition2. If it is false, then execution continues through statement3 and
statement4 and goes back to top to evaluate again. However, if condition2 was
true, the statement “break” will be executed, and the code will break out of the loop –
going to next_statement.

While(TRUE)- Shorten Loop Execution


#define TRUE 1
...
while (TRUE){
statement1;
statement2;
if (condition2) continue;
statement3;
statement4;
}
next_statement;
- Begin executing statements in loop body - statement1 then statement2. Evaluate
condition2. If it is false, then execution continues through statement3 and
statement4 and goes back to top to evaluate again. However, if condition2 was
true, the statement “continue” will be executed, and the code will skip statement3
and statement4, and loop back to statement1

Break and Continue

- C tries to avoid the need for a “go to” statement (considered to be the
worst way to change program flow) by providing ways to implement
the most prevalent changes in path.
{
statement1;
break;
statement2;
continue;
// continue skip the rest of the block for the current iteration
//and move on to the next iteration
statement3;
statement4;
}
// break causes program flow to continue just after the end of the
// current block

Week 1 Wrap-up: Long and Short Qualifiers and Pointers

Long and Short Qualifiers


- These qualifiers aere only suggestions to the compilers
- For example:
o “long ints” and “ints” generally have the same length of 32 bits for
Windows compilers.
o In other systems, “long ints” could be 64 bits whereas “ints” take up 32 bits
(Linux and Mac OS 64-bit).
- There is nothing in the C language specification that dictates that long always
has to be larger than int.
- It only states that:
o Sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
- It’s compiler dependent and related to the target system (the system that your
executable will run on)

Pointers
- A pointer is declared with a type like so
o Int* p;
- A pointer stores an address in memory as its value:
o Int a = 5; p =&a;
o // p = the address of a
 Once a pointer is assigned to the address of a variable, it is said to
point to that variable – e.g., p points to a
 If you take a peek at the value of p, you will see a memory address
Printf(“Value stored in variable ‘a’: %d\a”, a); // prints 5
Printf(“Address of variable ‘a’: %x\n”, &a); //prints the address of a
Printf(“Value stored in pointer ‘p’: %x\n”, p): //prints the address stored in p (in
this case the address of a)
- You are able to retrieve or change the value of whatever is stored in the memory
location pointed to by a pointer using a dereferencing operation.
o The dereferencing operator is simply an asterisk (*) placed before the
pointer name.
 Every time you see the asterisk that isn’t part of a pointer’s
declaration (the initialization of the pointer), it means ‘the value
pointed to by…’
o *p = 6; should be read as “the value pointed to by p is assigned the value
of 6” which results in being assigned the value of 6.
o Int b = *p; should be read as “b is assigned that value pointed to by p”
which results in b being assigned the value of 6

Double Pointers
- A double pointer is declared with a type like so: int **dp; or like int** dp;
- A double pointer stores the address of another pointer in memory as its value
dp= &p;
o Once a double pointer is assigned to the address of another pointer, it is
said to point to that pointer – e.g., dp points to p
o If you take a peek at the value of dp, you will see a memory address – the
memory address that the pointer p is located at
- You are able to retrieve or change the address that is stored in the pointer
pointed to by a double pointer using a single dereferencing operation
o *dp = &b; // the pointer pointed to by dp (p) nor points to b
// in other words, p now points to b
- You are able to retrieve or change the value that is stored in the memory location
pointed to by the pointer pointed to by the double pointer using a single
dereferencing operation
o **dp = 10; // b now equals 10
- When would you ever want to use double pointers?
o One reason is you want to change the value of the pointer passed to a
function as the function argument, to do this you require pointer to a
pointer
 It has to do with the way arguments are passed into a function:
pass by value
 Pass by value means you are making a copy in memory of
the actual parameter’s value that is passed in, thus the
function uses a copy of the contents of the actual parameter.
 Ex. I created a function that takes two pointers as arguments
and has them swap their values (i.e., what they’re pointing
to). A naïve implementation of this function might look
something life this:
//This code won’t swap refernces as expected

voidSwaprefs(int* ptr1, int* ptr2){


int* temp;
temp = ptr1
ptr1 = ptr2;
ptr2 = temp;
}
- However, this piece of code doesn’t swap anything at all.
o Because when this function is called elsewhere and pointers are passed in
as arguments, a duplicate set of pointers (or rather the values held by the
pointers which are addresses) are passed to the function istead of the
original pointers.
o Thus, any modifications to the pointers in the SwapRefs function applies
only to the pointer copies and not the original pointers due to the pass-by-
value characteristic.
- How can we allow changed to pointers or memory allocations to be preserved
outside the function’s scope?
o By using douple-pointers that point to pointers.
o If we use double-pointers as function arguments, due to pass-by-value,
copies of those pointers’ values will be made and passed into the function.
 However, the value held by the double pointers that refernce
addresses of the original pointer will be the same and thus any
changes to them in the function will be retained outside of the
function.
- Working version of the SwapRefs function which takes in double pointers.

Void SwapRefs(int** dptr1, int** dptr2){


Int* temp;
Temp = *dptr1;
*dptr1 = *dptr2;
*dpter2 = temp;
)

- In simple words, pass in double pointers when you want to preserve (OR retain
changes in) pointer assignments or memory allocations outside of a function’s
call.
- To gain visual understanding of how pointers and pointer-to-pointers are handled
in the C language, let’s examine one of the examples we covered during the
tutorial session:

Void example2(){
Printf(“Showing example 2: Pointers to Pointers Example\n”);
Int var = 2022;
Int* ptr:
Int** ptrToPtr;
Ptr = &var;
ptrToPtr = &ptr;
printf(“Output of var = %d\n”, var);
printf(“Output of *ptr = %d\n”, *ptr);
printf(“Output of **ptrToPter = %d\n”, **ptrToPtr);
}

- The following block diagram illustrates a sample representation of a computers


memory state.
- We can verify what pointers store in C language by removing asterisks (*) from
the prefixes of ptrToPtr and ptr. Below, we will examin the outpute of the printf
function!

1) printf(“Output of *ptr = %d\n”, *ptr);


a. Output of *ptr = 2022
The asterisk prefix dereferences the ptr pointer, essentially let print display
the content of the memory cell pointed to by ptr. In simple terms, it reveals
the content of the integer variable var.
2) printf(“Output of *ptr = %d\n”, ptr);
a. Output of ptr = 0x1230
Here, printf simply displays the content of ptr, which is the memory
address of var. Like variables, printf reveal contents of pointers when there
is no asterisk prefix in front of them.
3) printf(“Output of **ptrToPtr = %d\n”, **ptrToPtr);
a. Output of **ptrToPtr = 2022
The pointer-to-pointer is derederenced twice, allowing the compiler to
access the content of the variable whose address is pointed to by ptr,
whose address is, in turn, pointed to by ptrToPtr.
4) printf(“Output of *ptrToPtr = %d\n”, *ptrToPtr);
a. Output of *ptrToPtr = 0x1230
Similar to a single pointer, a single asterisk prefix in front of the pointer-to-
pointer derfernced itself, resulting in access of the content of the pointer
which is pointed to by the pointer-to-pointer. In this example, ptrToPtr
dereferencing gives access to the content of ptr which in turn is the
address of var.
5) printf(“Output of ptrToPtr = %d\n”, ptrToPtr);
a. Output of ptrToPtr = 0x1220
Here, the printf command displays the content of ptrToPtr, which is the
address of ptr.

Conclusion:
- The asterisk in front of pointers gives access to the content of the memory cells
they point to, a process known as dereferencing
- (*) in front of pointer gives access to the content of variables to which they are
pointing to, and these variables can be of various data types such as int, char,
float, or any other.
- (*) in front of pointer-to-pointers gives access to the content of the pointer they
are pointing to, and this content is typically the address of a memory cell.
- (**) in front of pointer-to-pointer gives access to the content of the variable
pointed to by the
pointer which in turn is pointed to by that pointe-to-pointer.

Tutoring

Structs – a way to keep track of a group of variables

Student – school, program, GPA

Syntax:
Struct name{
Int a;
Float b;
Char c;
};

Definition (goes before main function):


Struct student{
Float GPA;
String school;
String program;
};

Initialization (in main code):


Struct student Nathan;
Nathan.GPA = 4.0;
OR
It can be done on the struct syntax

Struct student{
Float GPA;
String school;
String program;
} Nathan, max;

To access primitive types of variables use name.vaible


To access pointers use name->z

Scope – the range that the variable acts over

Enum (fields of variables)

Enum colours{
RED, \\ 0
GREEN, \\ 1
BLUE \\ 2

};
\\ computer treats them as integers instead of strings

If (var==colour.RED){ \\ checks if var is 0



}
\\ able to add things in between after without harm
\\ ex add orange and yellow in-between without issue

Typedef

Syntax:
Typedef struct name newname;

Memory stack and heap:

Stack:
- First in last out
- Push (putting something in) and pop (taking something out)
- Compile time
o When a compiler goes through your program it goes through main frist
 It then looks for the first function called in main
 Then it looks for a function called within a function
 Goes back to main and so on

Heap:
- Run time memory
- Explicitly defined memory
o Memory allocated at runtime
o Ie. Malloc

Link Lists:
- Nodes (can be any data to be stored)
o Usefule because you can store anything inside of it
 Varibales
 Structs
 Functions
 enums
- Head is start
- Tail is end
- Can’t only access one thing i.e. one node at a time
- Only want to use it if you want to access everything at once

Struct node{
Int data;
Struct node*next // pointer to the next node
}

Structnode*head // head is the address that points to the first node

Unions:

Untion A{
Unsinged int addres.
s; // 4 bytes
Unsigned char octets [4]; // 4 bytes
};

Week 3

Lect 1

Lect 2

Lect 3

Tutorial 26/01/24

You might also like