You are on page 1of 60

CPS 125: Computer Science For Engineers

A Comprehensive Overview

e-jja

Ryerson University
Preface
Greetings, this is going to be a written guide from the perspective of a student. From this document you
will be able to learn the basics of the course. This was primarily done to improve my writing while trying
to create something useful. I may never use these notes again, but they are really pretty. Please follow
along with these notes, and mark any corrections as I am only human. I’ve had multiple people willing to
pay for one on one tutoring, but I don’t take money from others.
The only word I can describe CPS 125 is useless.
As a course, it moves slowly and there are still major components of C not covered. As a programmer, it
can barely be called an introduction to computer science as it does not include any study of algorithms.
This course is similar to a free online 7 week courses. The assignments are outdated, the twitter account
is annoying as they should simply push D2L notifications, and the online web page feels like they are
mocking students. Every assignment is confusing to read as they make you write code, then expect you
to erase it and add on more, instead of saying what is required from the start. In other words, the course
should be an online credit.

Disclaimer: I am not responsible for any plagiarism committed by the presence of this document, this
only serves as a guide. Any plagiarism will not be tolerated by Ryerson University, nor is this guide
intended to be an answer sheet. This guide will help only if you apply yourself by programming.

1
Contents
1 Definitions and Conversions 4

2 The C Language 9

3 Learning The Basics 11


3.1 Hard Rules for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2 Soft Rules for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.3 Placeholders for IO Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.4 Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.5 Arrays and Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.5.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.5.2 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.5.3 String Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.5.4 Two Dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.6 Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6.1 Evaluating Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6.2 Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.6.3 Shortcut Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.6.4 Increment and Decrement Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.6.5 Comparison Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.6.6 Logical Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.6.7 Order of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4 Libraries 22
4.1 The Math Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.2 The Standard Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.3 The Standard Input Output Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5 Modes of Operation 24
5.1 Interactive Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.2 Batch Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

6 The if Statement 26
6.1 Else if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
6.2 Nested if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

7 Switch Statement 28

8 Loops 29
8.1 While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
8.2 Do While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
8.3 for Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

9 Dealing with Files using Loops 32


9.1 Reading a Whole File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
9.2 Writing to Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

2
10 Independent Functions 35
10.1 Create Your Own Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
10.2 Multiple Return Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
10.3 Using Two Dimensional Arrays with Independent Functions . . . . . . . . . . . . . . . . . 36

11 Recursion 37

12 Dynamic Memory Allocation 39

13 Linked Lists 41

14 Labs 44
14.1 Lab 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
14.2 Lab 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
14.3 Lab 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
14.4 Lab 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
14.5 Lab 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
14.6 Lab 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
14.7 Lab 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

15 Assignment 1 55

16 Assignment 2 57

17 Ending Comments 59

3
1 Definitions and Conversions
Definition: An algorithm is a series of instructions on how to solve the problem.

• The order of execution must be correct.


Eg: To make a jam sandwich, you have to open the jar of jam before you spread the jam

• The order must be unambiguous.


The compiler must know what to do, imagine you are simply speaking to a child. The compiler takes
everything literally, so therefore in order to minimize error in translation from logic to program you
must be clear. (eg. 3*2/5-3 should be 3*(2/(5-3)) )

• There must be a result.


The sequence of steps must produce a final result. In the first point’s example, the result is the jam
sandwich

• They must stop at a finite amount if time.


You want to make sure your program pushes out a result before the sun burns out.

A computer is something we use in our everyday life, we recognize the keyboard, mouse, and display but
we rarely think about the complexity required to make such devices work seamlessly together. In computer
science, we try explain how computers work on the inside and why you should care.

Just to get definitions out of the way:

Definition: A bit is a binary digit, so it can represent a 1 or 0

Definition: A byte is a sequence of 8 bits.

Definition: A word is the width of a memory cell


In decimal, you can represent any positive integer N by the formula,

di 10i (1)
i≥0

Where di = 0,1,...,9. Recall, that every number is a place holder for a power of ten.

Eg. 70 can be represented by 7 in the tens and 0 in the ones column.


70 = 7 × 101 + 0 x 100 (2)
In binary, the place holder idea still applies. Therefore digit starting from the right hand side increases by
a factor of 2 as you move to the left. To introduce the math, any integer number N can be represented by

di 2i (3)
i≥0

Where di = 0, 1. To find 70 in binary, you add the powers of two.


70 = 1 × 26 + 0 × 25 + 0 × 24 + 0 × 23 + 1 × 22 + 1 × 21 + 0 × 20 (4)

4
Representing an integer number N in binary

1. First largest power of two smaller than or equal to N.N ≥ 2i ‘. Keep track of the power i.

2. Take N and subtract it by the number found in the previous step.

3. Repeat step 1 with your new smaller number until you get to zero.

4. In an empty space, write down the powers of two increasing from right to left. Starting at zero

5. Recall all the i’s found in step 1. Wherever that power of 2 exists, write a one above it.

6. For all other places which did not find a power. Write a zero above the power.

As an example, let us represent the number 89,


• Notice that 89 > 64 = 26 . i = 6.

• 89 − 64 = 25

• Notice that 25 > 16 = 24 . i = 4

• 25 − 16 = 9

• Notice that 9 > 8 = 23 . i = 3

• 9−8=1

• Notice that 1 = 1 = 20 . i = 0

• 1−1=0

• We found i = {6, 4, 3, 0}

1 1 1 1
6 5 4 3 2 1 0
2 2 2 2 2 2 2

• Fill in the rest with 0s. Which gives us, 1011001.


If you wanted to verify this answer,

89 = 1 × 26 + 0 × 25 + 1 × 24 + 1 × 23 + 0 × 22 + 0 × 21 + 1 × 20 (5)

Exam tip: On the multiple choice, you do not have much time. Therefore, instead of converting the
number into binary slowly. Just check if the question asks for an odd number. If this is the case, the right
most bit must exist. You can prove this yourself.

Now we move on towards 32 bit number. To represent 89 in 32 bits, you use the same result. Except this
time there are 26 zeros in front.

0000 0000 0000 0000 0000 0000 0000 0101 1001

5
Notice there is little spacing between every 4 bits. This is more visual, but it’s important for being orga-
nized. Since there are 32 bits, you expect there to be 8 chunks of 4 bits.

Up till now we have only been using positive numbers. This is because negative numbers add a small
layer of complexity. In order to make sure the computer can differentiate between a negative and positive
numbers, the left most bit is always reserved.

Currently, there are two methods for dealing with negative numbers,

1. Sign and Magnitude Method

2. Two’s Compliment

Sign Magnitude

• If the number is negative, flip the left most bit to a 1.


9 = 0000 0000 0000 0000 0000 0000 0000 1001
−9 = 1000 0000 0000 0000 0000 0000 0000 1001

Caution: This method is simple, but it is almost never used. This is due to fact adding and subtracting
are very difficult, and it leaves an ambiguous case for zero. You can literally have a −0 or +0.

+0 = 0000 0000 0000 0000 0000 0000 0000 0000


−0 = 1000 0000 0000 0000 0000 0000 0000 0000

Two’s Compliment

• If the number is negative, flip the left most bit to a 1.

• flip every bit starting from the left to the right to a 1 until the least significant bit.
You do not flip the least significant bit, nor do you flip the bits after
9 = 0000 0000 0000 0000 0000 0000 0000 1001
−9 = 1111 1111 1111 1111 1111 1111 1111 0111

This method is used as adding and subtracting are works normally, has a defined case for zero. The term
least significant is the right most bit of your number if it was positive. In the example below, the least
significant bit(LSB) is marked as L.

12 =0000 0000 0000 0000 0000 0000 0000 1100


Position of the LSB =0000 0000 0000 0000 0000 0000 0000 0L00
−12 =1111 1111 1111 1111 1111 1111 1111 0100

Binary and decimal are not the only two numbering systems. We also have hexadecimal system, remember
hex means 16 so the maximum number this system can represent is 15. But since we only have numbers
up to 9, we have to switch to alphabets.

0123456789ABCDEF

6
This is technically a more efficient system for numbering as it uses less digits for larger numbers. Google
uses a similar method for numbering the amount of Youtube videos. As a reference, I created a table from
everything I have talked about.

Decimal Octal Hexadecimal Binary


0 0 0 0000
1 1 1 0001
2 2 2 0010
3 3 3 0011
4 4 4 0100
5 5 5 0101
6 6 6 0110
7 7 7 0111
8 8 10 1000
9 9 11 1001
10 A 12 1010
11 B 13 1011
12 C 14 1100
13 D 15 1101
14 E 16 1110
15 F 17 1111

Binary to Hexadecimal

When converting numbers from binary to decimal, simply take each block of 4 bits and convert them
individually,

−12 =1111 1111 1111 1111 1111 1111 1111 0100


−12 = F F F F F F F 4

Binary to Octal

When converting numbers from binary to octal, simply take each block of 4 bits re-bunch them starting
from the left in groups of 3. Then convert each bunch of 3 directly into octal.

−12 =1111 1111 1111 1111 1111 1111 1111 0100


−12 =011 111 111 111 111 111 111 111 111 110 100
−12 = 3 7 7 7 7 7 7 7 7 6 4

Now we increase the difficulty,

7
Floating Point Numbers in Binary (Watch the Two Videos)

Floating point numbers are simply real numbers. Numbers such as 21.23. The problem is, how do we get
decimals into binary, we can’t really do 1001.1101.

Luckily, the common standard we learn in class is IEEE 754.

It contains an sign, exponent, and mantissa.

Left most bit is allocated for sign, followed by 8 bits for the exponent, and 23 for the mantissa. That’s
right, we will be using 32 bits for this method. For more visual learners, the format follows as,

[sign][exp(8)][mantissa(23)]

Steps for converting a real number R to IEEE 754 Single Precision Binary

1. Determine the sign. If the number is positive, the sign is 0, if it is negative the sign is 1.

2. Find the greatest power of 2 that’s less than R.

3. To determine exponent, take the greatest power of 2 that’s less than R(we shall call it i). Plugging
i into the formula,

exp = 127 + i

We get the exponent in decimal, represent this number in binary plug into allocated exp place.

4. Take the numbers after decimal, if any, and multiply it by 2.

5. If the number is now greater than or equal to one make note of it, subtract one. Continue to the
next step if the number is now zero or you find a pattern, else go back to step 4.

6. Take the ones which you noted multiplying by 2, and write them in a row. This is your binary value
for the mantissa. Rewrite the value as follows.
Eg: (floor(N ))2 .[Mantissa]

7. Move the decimal i places to the left.

8. Copy everything after the decimal into the space for your mantissa.

9. Fill any blank spots with zero.

You can see how long this method is, on the exam you will need to do this fast

I would love to say I am going to do an example, however words are never going to explain it better than
these two videos:

Decimal to Binary
Binary to Decimal

Exam Tip: For the multiple choice, instead of validating the mantissa, check if the sign and exp match.

8
2 The C Language
Using English, we are able to use multiple letters to form words, then use these words to form sentences,
and sentences to form proper arguments. However, computers speak in binary, this is due to the fact that
an electrical signal can either be on or off. People didn’t want to look at a screen filled with ones and zeros,
so instead they created Assembly language. Assembly language utilized human readable hexadecimal val-
ues instead of ones and zeros, and mnemonic codes to perform tasks which made it simpler than machine
language. Now, we get to the type of language which we know today. So called “high level languages” such
as: C, Fortran, Java, and Python. This made programming available to the masses as you could write a
program and never care about the registers or memory allocation.

C is one of the most widely known programming languages around. But why is this the case? C is portable,
efficient, easy to learn, and modular. It teaches proper memory management, and forces the programmer to
care about system resources and clean code. In this class we use C to program solutions to various problems,

Steps for Solving Problems


1. Define the Problem
2. Analyse the Problem
3. Design a solution
4. Implement solution
5. Test the Program
6. Update and maintain the program
Steps for Implementations
1. Write the program
2. Compile the program
3. Build the program
4. Run the program
You will not be tested for remembering any of the steps above, but project cycles, maintenance, and design
are important aspects of software engineering.

Definition: A preprocessor is a system program that modifies the program prior to compilation
Definition: A preprocessor directive is instructions for the preprocessor, beginning with #
Definition: A library is a collection of functions, symbols, and values.
In C there are two types of preprocessor directives
# include
and
# define
Each command is terminated with a semi-colon(;). The compiler has no sense of tabs or line numbers, but
it is case sensitive.

9
Definition: A comment is a line or lines of code which is ignored by the complier. To declare a comment,
we generally use “// Single Line Comment” or “/* Multi line Comment */”
We have talked about functions in math. It can be thought of as some black box that takes some input
and produces some output. In programming, it’s the same as math, except we call inputs as function
arguments.

f (x, y, z) (6)

Where f is some function, x, y, z are some inputs. In the same you can see how programming is almost
identical,
function_name ( argument_1 , argument_2 , argument_3 ) ;

Just like f is a function, now you have a function with the name “function_name”, and it takes in 3
arguments just like x,y,z. This will provide an output based on the arguments.

10
3 Learning The Basics
The general skeleton of a program is,
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 // This is your main function
6 return (0) ;
7 }

By the time you finish this course, you should be able to blindly type this code. Let us dissect this template,
# include < stdio .h >

This statement informs the preprocessor that some functions in our program may be defined in the stdio.h
library. The stdio.h library is simply short for “Standard Input Output Library” and contains a vast
majority of the functions we will learn about.
int main ( void )

In C, when the file is being compiled. The compiler searches for a function called main. Since main is a
function it will take inputs, but as you can see the arguments are “void”. In the very beginning of this line
you see “int” this is to inform the compiler, this function will return an integer value.
return (0) ;

This line is simply following suit as we are supposed to be outputting an integer value from this function.
Therefore, we simply return the integer 0.

In C, there are 4 main variable types we care about

1. integer (int) eg. 3

2. Single Precision Floating Point (float) eg. 3.14

3. Double Precision Floating Point (double) eg. 3.1415

4. Character or String (char) eg. ’c’ or “Hello”

In order to declare a variable you must write the type, followed by the name and data stored.
/*
< type > < variable name > = < stored value >;

The following below are a few examples of defining variables


*/
int a = 27;
int b = 20;
char grade = 'A '
double pi = 3.14159265

This might seem easy, but you need to know what classifies as a identifier name by the compiler.

11
3.1 Hard Rules for Identifiers
1. It can only contain underscores, letters, and numbers ( _ , ABC , 123)

2. It cannot begin with a number (3var_name)

3. It cannot be a keyword (if, else, while, not, etc)

Additionally, there are conventions in the Computer Science field for variable names. It would be correct
not to follow the rules below, but your co-workers would probably kill you

3.2 Soft Rules for Identifiers


1. Constants are all capital letters (eg. double PI = 3.14)

2. Identifiers are are all lower case (eg. int class_average = 33)

3. It cannot be another identifier unless your intention is to reassign a function or variable

1 # include < stdio .h >


2
3 int main ( void )
4 {
5 // The following are non - valid identifiers , try and find out why .
6 int 4a = 7;
7 int There - are = 7;
8 int if = 7;
9
10 // The following are valid , but not good etiquette
11 int pi = 3.1415;
12 int printf = 7;
13 /*
14 pi will never change , therefore you consider it a constant in the program .
15 However , a more common application are resolution variables : SCREEN_WIDTH , SCREEN_SIZE
16 */
17 /*
18 printf is not a keyword in C , but it is a function from the stdio . h library .
19 We can use the word printf for defining variables , but since this identifier
20 has already been used , it should not be redefined
21 */
22
23 }

12
3.3 Placeholders for IO Statements
In this course, only the following placeholders will be used,

1. %f - Floating Point (float)

2. %lf - Long Floating Point (double)

3. %d - Integers (int)

4. %c - Characters (char)

5. %s - String (char x[n])

We now consider the spacing on the output,

%N d N is the number of assigned space for an integer, it begins to fill the space from right to left
%.N d N is the number of assigned space for an integer, it begins to fill the space from left to right
This fills the place holders to the left with zeros
% w.d lf w is the total space allocated for this double
d is the number of spaces reserved after the decimal point

Note: w is the space including the integer and decimal part of a floating point number. The integer
portion is always prioritized over the decimal. Therefore, if you have an l number of digits for the integer
component, the spots left for the decimal is w − l. If w − l ≥ d then the result is exactly what you would
expect, otherwise it would truncate(round) the decimal earlier.

Caution: If you use a wrong placeholder, the number will print zero.

These place holders are used IO functions like printf, scanf, and fscanf.
printf ( " Stuff to print : %d " , integer_variable ) ;

Some people learn better by example,


1 # include < stdio .h >
2

3 int main ( void )


4 {
5 int x = 143;
6 float y = 14.4;
7 double z = 27.91920;
8

9 // You can print multiple variables as long as there exists placeholders for each value .
10 printf ( " Value of x : %4 d = %.4 d \ n " ,x , x ) ; // The \ n prints a newline
11 printf ( " Value of y: %f \n", y);
12 printf ( " Value of z : % lf = %5.3 lf = %2.9 lf = %0 lf \ n" ,z ,z , z ) ;
13
14 return 0;
15 }

Try and grab a piece of paper to determine the output. The answer is on the next page.

13
Note: The first time x is printed, the output is shifted one to the right due to the unused space allocated.

3.4 Pointers
Pointers are unbelievably useful, but it’s tricky to understand.

Imagine you had an integer x with the value,


int x = 5;

We know x is a variable, so therefore it must be stored somewhere in memory. To get the address(location)
of where it is located, you can use the & operator. We should note that the address is not the value of the
variable. Try this code,
1 # include < stdio .h >
2 int main ( void )
3 {
4 int x = 5;
5 printf ( " The value of x is % d \n " , x ) ;
6 printf ( " The address of x is %p \n " , & x ) ;
7 return (0) ;
8 }

You will notice, the second value that prints just seems like garbage number. This is in-fact the memory
address where the integer is stored. Now you may ask why would you ever want to use pointers? To which
I would reply with another question. Imagine you had a house, and you wanted to call a plumber over
to your house. How do you get a plumber to come over? I don’t think any logical person would bring
the house to the plumber. Instead, you would call up the plumber and tell them your home address. The
plumber can then come over do their job.

Similarly, if programming and you find a point where a function(plumber) needs to work on something
complex like a matrix or multi-dimensional array(house). You send over the address. Moving huge chunks
of data and recreating multi-dimensional arrays multiple times is cumbersome and simply bad code.

We have a specific type of variable that contains the address of another variable. This is what we call a
pointer.

We can define a pointer as,


< type of pointer > * < pointer name > = < address >

So now, if you wanted a variable to point to x. We can do it as follows,


1 # include < stdio .h >
2 int main ( void )
3 {
4 int x = 5;
5 int * ptr = &x ;
6
7 printf ( " The address of x is : % p = % p " , ptr , & x ) ;
8

14
9 ptr = NULL ;
10

11 return (0) ;
12 }
Major thing to notice is on line 7. I made sure my pointer was no longer pointing to x, instead it’s now
equal to null

PROPERLY CLEAR YOUR POINTER AT THE END OF YOUR CODE

The whole reason why C is such an amazing language to learn is the simplicity and how it forces good
memory management.

Now you might be wondering. If there is an address stored, shouldn’t we be able to work backwards and
see what the address contains? You are correct. This is called dereferencing a pointer. We use the
*pointer_name to dereference a pointer. In the example above, we can use
x = * p;
and that would be the same as saying,
x = x;

3.5 Arrays and Strings


3.5.1 Arrays
You can think of an array as an ordered set. You understand sets from linear algebra, Z+ = {0, 1, 2, ...}.
Likewise, you can define a set of variables by using an array. The general form of an array is shown below,
<type > < name of array >[ Number of elements ( N ) ] = { E_0 , E_2 ,... , E_ (N -1) };
Imagine you wanted to store 5 integers, you can use the following code,
int bunch_of_ints [5] = { 1 , 2 , 3 , 4 , 5}
In order to pick out an element from the list, you can use the following code
bunch_of_ints [ element number ]
Notice: In the general form, we started from E0 and went to E(N −1) . This means the numbering of
elements begins at 0 instead of 1. Therefore, if you wanted to read the last element in the array,
int last_element = bunch_of_ints [4] // last element is element # 4

3.5.2 Strings
How do arrays relate to strings? Well a string is an array of characters as shown below.
char < string_name >[ max_letters_in_string ] = " some string " ;
Notice: You do not use single quotes, instead you use double quotes for strings.

A string and an array in the simplest form is a pointer. When declaring a string or an array, it is necessary
to inform the complier the amount of memory to be allocated towards it. After the memory is allocated,
there is a pointer used to keep track and grab values from the assigned memory. This process is similar to
those old multi-disk music players from the early 90’s.

15
3.5.3 String Arrays
You may want to create an array of strings. The code for such an idea is shown below.
int * array_of_strings [5] = { " Hello " , " My " , " Name " , " is " , " Student " };

The main difference between a regular array and an array that can store strings is shown in the code above.
For an array which can store strings, you use an array pointer (which is why you use of the ∗). If ∗ was
missing, then it would be a regular character array that can hold 5 characters. With the ∗, it means it can
hold 5 strings.

3.5.4 Two Dimensional Arrays


You might feel very familiar with two dimensional arrays as act like matrices. You can visualize a two
dimensional array using the figure below,
 
a a ... a1j
 11 12 
a21 a22 ... a2j 
 
 . . . . 
 .. .. . . .. 
 
ai1 ai2 . . . aij

In order to create a two dimensional array, you would use the following code
<type > < array name >[ i ][ j]

In this class, you are being asked to use the Row-major order. Therefore, in the code above, i is the
column number and j is the row number. As an example, to create a 7 by 7 array of integers, you would
use the following code.
int matrix [7][7];

Caution: Once again remember the index of an array begins at 0. Therefore, a11 is column 0 and row 0.

16
3.6 Arithmetic
3.6.1 Evaluating Expressions
For the most part arithmetic in code is evaluated the same way as math.

1. Expressions in parentheses () must be evaluated first

2. Operators are evaluated in the order: *,/,+, -, % (More order of operations later)

3. All expressions in are evaluated from left to right

4. If there is a floating point type value in an expression, it will take precedence over integers

1 # include < stdio .h >


2
3 int main ( void )
4 {
5 int x = 9;
6 float y = 2.5;
7 double z = 3.1;
8
9 float m = x /y ;
10 int r = x* x + y* y + z *z ;
11 double u = ( x* y) *(1/ x ) *( z /( x * y ) ) ;
12
13 return 0;
14 }

The question is, what are the values of m, r, and u.

Lets begin with m,


float m = x/ y

This is an integer divided by a floating point number, so the final value is also going to be a floating point.
m = 3.6000000
int r = x *x + y *y + z * z ;

For r, we have an integer, float, and double all squared. int r = 9 ∗ 9 + (2.5 ∗ 2.5) + (3.1 ∗ 3.1). Let us go
through with the multiplication. int r = 81 + 6.25 + 9.61. Since we are adding an integer to a float and
double, our final result will be a float. int r = 96.86. But now since r can only store integers, we need
to convert a double to an integer. This is where rule below comes into play, by this logic, r = 96.

Note: If we change a float/double to an integer, it will always round down to a whole number. Addition-
ally, if we divide integers to get a decimal, it will be rounded down

double u = ( x *y ) *(1/ x) *( z /( x * y ) ) ;

We can see u = 0, because we have 1/x, which are two integers so it evaluates to zero.

17
3.6.2 Casting
Sometimes when doing arithmetic or dealing with functions of different types, it’s useful to temporarily
change the type of a variable or the return type of a function. This temporary change in type is called
casting

Take for example the code below:


1 # inlcude < stdio .h >
2
3 int main ( void )
4 {
5 int a = 2, b = 3;
6 double c = a /b ;
7
8 printf ( " The value of c is : % lf " , c ) ;
9

10 return (0) ;
11 }

If you were to compile and run the program above, you would find c = 0.000 Since we know since c is
an double, the code’s initial intention was to make c = 0.66667 = 2/3. Because a and b are integers, it
rounds down 0.6667 to 0.000. If we want to avoid such easy mistake, it’s a good idea to use casting. The
following code below shows how to cast.
( < new temp type >) variable

In order to fix the example above we would cast a double on a or b.


1 # inlcude < stdio .h >
2
3 int main ( void )
4 {
5 int a = 2 , b = 3;
6 double c = ( double )a /b ;
7
8 printf ( " The value of c is : % lf " , c ) ;
9
10 return (0) ;
11 }

3.6.3 Shortcut Operators


In programming, you sometimes need to repeat an action. Therefore for those repeated expressions, we
have shortcuts.
x = x *5;
a = a /2;
i = i +2;
i = i -2;

Are the same as


x *= 5;
a /= 2;
i += 2;
i -= 2;

18
3.6.4 Increment and Decrement Operator
Similarly, you also use counter variables which require to increase by one or decrease by one each time.
Instead of writing every time,
i = i + 1;
// or
i += 1;

we use
i ++;

Similarly, instead of using,


i = i - 1;
// or
i -= 1;

we use
i - -;

There are however two types.


i ++ /* and */ ++ i

i++ runs through the code before incrementing the value, whereas ++i increments before evaluating. We
can easily show this with an example.
# include < stdio .h >

int main ( void )


{
int a = 1;
int b = 1;
int x = a ++;
int y = ++ b;
return (0) ;
}

In the end a = b = 2. But x = 1 and b = 2, this is because ++b increments before evaluating the expression,
whereas a++ increments after.

3.6.5 Comparison Operators


This is going to be a short sub-subsection, but the most important.
a < b // is a less than b
a > b // is a greater than b
a <= b // is a less than or equal to b
a >= b // is a greater than or equal to b
a == b // does a equal to b
a != b // does a not equal to b

3.6.6 Logical Operators


By introducing logical operators you now get access to more complex comparisons. We have 3 main logical
operators which you will be using in this class.
1. The && or and operator which means both sides of and has to be true

19
2. The || or or operator which means either side of or has to be true

3. The ! or not operator flips the truth value of the argument


Imagine we are given the values,
int a = 1;
int b = 2;

what can you say about the following statements,


1 ( a < b) && (b == 2)
2 ( a > b) || (a == 1)
3 ( a != 5) && ( b == 2)
4 ( b /2 == 1) && ( a/ a == a )
5 ( a /2 == 0) or (5/ b == 2)
6 ( a /9 == 1) and (6/ b == 46)

• Line 1. (a is less than b)(TRUE) and (b is equal to 2) (TRUE) – True

• Line 2. (a is greater than b)(FALSE) or (a is equal to 1)(TRUE) – True

• Line 3. (a does not equal 5)(TRUE) and (b is equal to 2)(TRUE) – True

• Line 4. (b divided by 2 is 1)(TRUE) and (a divided by a is a)(TRUE) – True

• Line 5. (a divided by 2 is 0)(TRUE) or (5 divided by b is 2)(TRUE) – True

• Line 6. (a divided by 9 is 1)(FALSE) or (6 divided by b is 46)(FALSE) – False

Note: For line 5 and 6 remember the rounding rule for integers we discussed.

Exam Tip: For stupidly long statements, look for or or if statements


• For and, if one side is wrong, the whole statement is wrong.

• For or, if one side is true, the whole statement is true.

3.6.7 Order of Operation


Now we discuss how the complier simplifies and completes our code.
1. Function Calls

2. Unary Operations

(a) Increment (i++)


(b) Decrement (i–)
(c) Address (&a)
(d) Positive (+x)
(e) Negative (-x)
(f) Not (!(x>y))
(g) Casting (double)int

20
3. Multiplication, Division, and Modulus

4. Addition, Subtraction

5. Comparison Operators

6. and statements

7. = operator

You are expected to know how arithmetic works and what operations take precedence in a complex line
of code.

21
4 Libraries
In this course, we only use 3 different libraries. So therefore, I will introduce you to them and anything
testable.

4.1 The Math Library


To use the math library we need to inform the preprocessor that some commands used are defined in that
library.
# include < math .h >

In the math library all functions below will return floating point values no matter the input type. Some
important functions in this library are :
sqrt ( x) // Returns the squareroot of the input ( eg . sqrt (69) = 8. somthing )
floor (x ) // Returns the number rounded down ( eg . floor ( -1.2) = -2.0)
ciel ( x) // Returns the number rounded up ( eg . ciel ( -1.2) = -1.0)
log (x ) // Returns log base e of x or ln ( x ) ( eg . log ( e) = 1.0)
log10 (x ) // Returns log base 10 of x ( eg . log10 (100) = 2.0)
fabs ( x) // Returns absolute value of x ( eg . fabs ( -1.2) = 1.2)

Additionally, we have trig functions which use radians as input


sin (x ) // Returns the sine of x ( eg . sin ( PI ) = 0.0)
cos (x ) // Returns the cosine of x ( eg . cos ( PI ) = -1.0)
tan (x ) // Returns the tangent of x ( eg . tan ( PI /4) = 1.0)

Next we have a few simple but testable functions


exp (x ) // Returns e^ x ( eg . exp (1) = e )
pow (x ,y) // Returns x^ y ( eg . pow (2 ,3) = 8)
atan ( x) // Returns arc tangent in randians ( eg . atan (1) = pi /4)

4.2 The Standard Library


To use the standard library we need to inform the preprocessor that some commands used are defined in
that library.
# include < stdlib .h >

We have some testable functions,


abs (x ) // Returns absolute value of x ( eg . abs ( -1) = 1)
rand ( x) // Returns an integer between 0 and RAND_MAX

Note: RAND_MAX is a variable which can be modified by the user


Caution: abs(x) will only return an integer value (eg. abs(3.1) = 3)
Exam Note: Remember the difference between abs and fabs. Note their libraries.

4.3 The Standard Input Output Library


To use the standard input output library we need to inform the preprocessor that some commands used
are defined in that library.
# include < stdio .h >

The main important functions from this library is

22
1 int variable = 2
2 printf ( " variable % d" , variable ) // Will print " variable 2"
3 scanf ( " %d " , & variable ) // Gets an integer from user and stores it

We will go more in depth on these functions later and you will see them being utilized everywhere through-
out this course. For any programs you write, this will be the default include.

23
5 Modes of Operation
There are two modes by which a program can operate.
1. Interactive Mode - The program will print words on the screen and ask for input.
2. Batch Mode - The program will read and write to a file.

5.1 Interactive Mode


The computer will have to interact with the user by printing statements to the screen and waiting for
input. We already know how to use printf. printf is used below without any additional arguments
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 printf ( " data " );
6 return (0) ;
7 }
This program will print out: “data”.
To print out a variable, you have seen the use of placeholders and variables as additional arguments
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int luck_number = 42;
6 printf ( " my lucky number is % d" , lucky_number ) ;
7 return (0) ;
8 }
This prints out: “my lucky number is 42”.
Now we want user input. This requires the use of a function called scanf. The scanf function’s fist
argument is a string which only contains placeholders. The second or additional argument is the address
of variables to store each input. The placeholder has to match the type of variable at the address given.
1 # include < stdio .h >
2 int main ( void )
3 {
4 int x = 0;
5 scanf ( " % d" , &x ) ;
6 reutrn (0) ;
7 }
If the user was required to input more numbers, we add more placeholders and variable address. For each
place holder, the program will stall and wait for user input. Therefore, for the program below, it will stall
3 times. During each stall, an integer values must be input and the user must hit the enter key.
1 # include < stdio .h >
2 int main ( void )
3 {
4 int x ,y ,z;
5 scanf ( " % d % d %d " , &x , &y , & z ) ;
6 return (0) ;
7 }
Note: Do not use anything else but placeholders in the quotations for scanf.

24
5.2 Batch Mode
Imagine you had a file with the name “data.txt” containing the number 42. In order to read from the file,
we use the following code.
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int data_from_file ;
6

7 FILE * data = fopen ( " data . txt " , " r" ) ;


8
9 fscanf ( data , "% d " , & data_from_file ) ;
10
11 fclose ( data ) ;
12 reutrn (0) ;
13 }

Let us begin to analyze this code,


FILE * data_file

We first create a file pointer called data.


fopen ( " data . txt " , "r " )

To load a file, we use the function fopen. fopen takes two arguments. The first argument is a string of
the file name, and the second is the status. We set the status to “r” which means read only.
fscanf ( data_file , "% d " , & data_from_file ) ;

fscanf is exactly like scanf, except the first argument is now the file pointer. Each value we read, the file
pointer moves to the next value inside the file.
fclose ( data )

This deallocates the file pointer. We elaborate more on files after we cover loops.

Now imagine you wanted to write the number 42 to a named “data.txt”. The code is almost exactly the
same as reading, except now we use fprintf.
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int data_to_store = 42;
6

7 FILE * data = fopen ( " data . txt " , "w " ); // This time the mode is w. This is to write .
8
9 fprintf ( data , " %d " , data_from_file ) ;
10
11 fclose ( data );
12 reutrn (0) ;
13 }

You can notice the similarity between printf and fprintf. It’s almost identical but the first argument
for fprintf is the file pointer

25
6 The if Statement
If you want to implement conditional logic into your code, then you should consider the if statement. The
general form of the if statement is:
if ( condition )
{
// do something
}
else
{
// do something
}

Recall: the condition is statements or statements combined with logical operators. We saw how state-
ments and logical operators work in section 3.6.6.

If the condition is true, the code inside the parenthesis will run. In other words, the condition is checked
before running any code inside the parenthesis. The code inside the else statement will only run if the
condition is false

1 # include < stdio .h >


2
3 int main ( void )
4 {
5 int x = 2;
6 int y = 3;
7
8 if (x > 2 || y == 3) // Notice the first condition is false , second is true
9 {
10 printf ( " Hello World " ); // Due to the " or " statement , the condition is true
11 // So this code will run
12 }
13
14 return (0) ;
15 }

6.1 Else if
If you know there are many outcomes, but only one of them is true. You can use an else if statement.
An else if statement is used directly after an if statement or another else if statement. It optimizes
your code, because if the initial if statement or any other else if statement is true, then any else if
or else statements below are skipped.

You will find the structure of an else if statement identical to that of a regular if statement. An example
of using else if is shown below,
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int x = 0;
6
7 printf ( " Please input a number : " ) ;

26
8 scanf ( " % d" , &x ) ;
9

10 if (x == 0)
11 {
12 printf ( " Zero is such a boring number " );
13 }
14 else if ( x > 0) // Notice the position ( between if and else )
15 {
16 printf ( " Ohh a positive integer " ); // Notice the identical structure to if
17 }
18 else
19 {
20 printf ( " Ohh a negative integer " );
21 }
22
23 return (0) ;
24 }

6.2 Nested if
For more complex scenarios, you may find yourself trying to use an if statement inside another existing
if statement. This “if inside another if” is called a nested if statement. Below is an example to show the
structure of a nested if statement.
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int a = 2; int b = 3; int x = 4; int z = 9;
6 if ( a < b) // This is the main if statement
7 {
8 x - -;
9 if ((z -5) == x ) // This is nested if statement . It is inside another if
10 {
11 printf ( " Hello World " );
12 }
13 else
14 {
15 printf ( "I sleep 14 hours a day " ) ;
16 }
17 }
18 }

The code above will print “I sleep 14 hours a day”. You should note that if the main if statement is false,
then the nested if statement’s condition is never checked. Because the code inside your main if statement
is skipped.

27
7 Switch Statement
A switch statement is like an if statement, and is used when you have one condition and you know the
possible output values. The general form of a switch statement is,
switch ( variable )
{
case value1 :
\\ code here // Notice no parenethesis are being used .
break ; // the " break " keyword is used to end each case
case value2 :
\\ code here
break ;
case value3 :
\\ code here
break ;
...
default :
\\ code here
break ;
}

The break keyword in C is used to forcefully end loops. In the general form, we can only end each case
with a break. As an additional note, you must avoid ending while or for loops using break, as it is typically
considered as “bad code”. The example below shows one such use for using
1 # include < stdio .h >
2

3 int main ( void )


4 {
5
6 char RAMSS_grade = 'F '; // The grade has been set to F
7
8 switch ( RAMSS_grade )
9 {
10 case 'A ':
11 printf ( " You passed "); // This code will be skipped
12 break ;
13 case 'F ':
14 printf ( " Failing is a part of life " ) ; // This code run
15 break ;
16 default :
17 printf ( " Unknown Input Value " ) ;
18 break ;
19 }
20 return (0) ;
21 }

If we run the code above, it will output the following statement: “Failing is a part of life”. This is because
the RAMSS_grade is an ’F’, and the code has instructions to run in that specific case. Note, when none of
the cases match the variable, then the default case runs.

28
8 Loops
A loop is just something that repeats itself. if you notice you are copying and pasting something multiple
times, chances are you need a loop.

In C there are 3 kinds of loops

1. while loop

2. do while loop

3. for loop

Again for the following section, I encourage you to pull up and compile the code yourself.

8.1 While
A while loop will literally do something while a condition is true. The general form of a while loop is as
follows,
while ( condition )
{
// Code here
}

The condition is only checked before any of the code inside the parenthesis is run.

If the condition is true,

1. The code inside the parenthesis is run

2. At the end of the parenthesis, the condition is checked again

3. If the condition is still true, return to step 1.

If the condition is false, the code inside parenthesis is skipped.

A very simple application of the loop is shown below,


1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int a = 2;
6 int b = 1;
7 while (b != a )
8 {
9 b ++;
10 }
11 return (0) ;
12 }

29
8.2 Do While
Do while is exactly the same as a while loop. However, initially the code inside do is run before the
condition is checked. It has a general form,
do
{
// Some code here
} while ( condition ) ;
This type of loop is generally used for input validation, as it can be very simple to apply. The code below
is an example of input validation using this loop.
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int number = 0;
6 do
7 {
8 printf ( " Please input an integer number between 0 and 25: ") ;
9 scanf ( "% d " , & number );
10
11 } while ( number <= 0 || number >= 25) ;
12

13 printf ( " \ nThank you ") ;


14
15 return (0) ;
16 }
Notice: There is a semi-colon after the while statement.

8.3 for Loop


A for loop should only when you know the amount of loops to perform before hand. The general form of
such a loop is as follows,
for ( initial_code ; condition ; update_instruction )
{
// Do something
}
The loop follows the following steps,
1. initial_code is run when the loop initializes
2. Checks if the condition is true, if yes proceed to next step, if not exit the loop.
3. Run the code inside the parenthesis
4. The update_instruction is run
5. Return to step 2
The reason why this loop is so useful, is because of the inbuilt initial_code and update_instruction.
These two pieces of code are typically used to setup and run a counter for the loop. The condition is
used to end the loop.

An example of a for statement in use is,

30
1 # include < stdio .h >
2

3 int main ( void )


4 {
5 for ( int i = 0; i < 20; i ++)
6 {
7 if ( i %2 != 0) // The percent means remainder after i is divided by 2
8 {
9 i ++;
10 }
11 printf ( " The value of i is : % d " , i ) ;
12 }
13 }

Try and find out what the code above will print.

31
9 Dealing with Files using Loops
9.1 Reading a Whole File
Imagine we have a file called “data.dat”. Inside this file, we have the following values tabulated.
1 92
2 88
3 97
4 90
5 94

Since I am an Electrical Engineering student, for the second semester I only take 5 courses. The numbers
indicate each of my course averages. Your job is to take these 5 courses and find the TGPA.

We require 5 fscanfs. Try to think of a way to do this. The answer is below.


1 # include < stdio .h >
2
3 int main ( void )
4 {
5 FILE * data = fopen ( " data . dat " , " r" ) ;
6
7 float temp ;
8 float tgpa = 0;
9

10 // Extract all the course data and change to gpa


11 for ( int i = 0; i < 5; i ++)
12 {
13 fscanf ( data , " %d " , & temp ) ;
14
15 if ( temp >= 90)
16 {
17 tgpa += 4.33;
18 }
19 else if ( 90 > temp && temp >= 85 ) // Same as just another if statement
20 {
21 tgpa += 4.00;
22 }
23 else if ( 85 > temp && temp >= 80 )
24 {
25 tgpa += 3.67;
26 }
27 // You can fill the rest below , but this should cover all the avergaes in the file
28 }
29 tgpa /= 5;
30
31 printf ( " My TGPA = %.3 f " , tgpa ) ;
32
33 fclose ( data ) ;
34
35 return (0) ;
36
37 }

32
9.2 Writing to Files
Time to step it up a notch.

Imagine you created a game. Every time your friend and you play, it always starts a fight because no one
kept track of score.

Requirements from your code:

• You will write code to store data for one game

• There are 5 rounds per game, you will ask who won each round

• You should expect input of “1” or “2” as the winner of each round. Input Validation!

• Every round should be stored on a new line in the data file

• At the end of each game, you must include the phrase “END–” on a newline followed by two additional
newlines.

• You must declare who won at the end

The answer is below.


1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int curr_win ;
6 int decide_win ;
7 FILE * data = fopen ( " data . dat " , " a" ) ; // " a" mode appends existing file
8
9 for ( int i = 0; i < 5; i ++)
10 {
11 do
12 {
13 printf ( " Please enter valid results from round %d : " ,i +1 ) ;
14 scanf ( " %d " , curr_win ) ;
15

16 } while ( curr_win != 1 && curr_win != 2) ;


17
18 if ( curr_win == 1)
19 {
20 decide_win ++;
21 }
22
23 fprintf ( data , "% d \n " , curr_win ) ;
24 }
25 fprintf ( data , " END - -\ n \ n" );
26
27 if ( decide_win >= 3)
28 {
29 printf ( " Player 1 wins " );
30 }
31 else
32 {

33
33 printf ( " Player 2 wins " );
34 }
35
36 fclose ( data ) ;
37
38 return (0) ;
39 }

34
10 Independent Functions
10.1 Create Your Own Functions
Independent functions are functions other than main(void). We discussed this earlier when studying the
basics, but now we are going to be creating functions. In order to declare a new function we can follow
the outline below.
< return_type > < function_name > ( < arguments >)
{
// Code to run
}
Your function needs to be above main unless we use forward declaration. The following is an example of
how to use a function.
1 # include < stdio .h >
2
3 int print_number ( int number )
4 {
5 printf ( " Number you want to print is %d " , number ) ;
6 }
7

8 int main ( void )


9 {
10 print_number (42) ;
11 return (0) ;
12 }
We mentioned the use of forward declaration. It is done by leaving a snippet the undefined function above
the main. We can then properly define the function after the main later. The undefined snippet looks as
follows
< return_type > < function_name > ( < arguments >) ;
As an example, we can rewrite the code above as,
1 # include < stdio .h >
2
3 int print_number ( int number ) ;
4
5 int main ( void )
6 {
7 print_number (42) ;
8 return (0) ;
9 }
10
11 int print_number ( int number )
12 {
13 printf ( " Number you want to print is %d " , number ) ;
14 }
The code looks more cleaner for larger programs. The snippet informs the compiler not freak out when it
sees this function as it’s defined later in the file. More examples are available in the section on recursion.

10.2 Multiple Return Values


Sometimes you may want a function to return more than one value. To deal with this, you are required to
use pointers (or address). If you send a variable address as an argument to a function, then the function

35
can manipulate the stored value at that address.
1 # include < stdio .h >
2 # inlcude < math .h >
3
4 int split_decimal ( double num , double * fractional_part ) ; // Expects a double and a
double address
5
6 int main ( void )
7 {
8 double fractional_part ;
9 int integer_part ;
10
11 // & integer_part is the address of integer_part
12 integer_part = split_decimal (3.14 , & integer_part ) ;
13
14 printf ( " The integer part is : % d\ n " , integer_part ) ;
15 printf ( " The fractional part is % lf " , fractional_part ) ;
16
17 return (0) ;
18 }
19
20 int split_decimal ( double num , double * fractional_part )
21 {
22 * fractional_part = num - floor ( num ) ; // Dereferencing the address allows me you to
23 // change the variable directly .
24 return ( floor ( num ) ) ;
25 }

10.3 Using Two Dimensional Arrays with Independent Functions


In order to send two dimensional arrays into functions as arguments, the function argument needs to
contain the maximum row value expected. As an example, a function expecting a two dimensional array
of 200 row elements maximum would be
< return_type > < function_name >( < array_type > < array_name >[][200])

If the function’s name was “find_border” with a return type double, expecting a two dimensional double
array named “mama_mia” of size 200. Then follow the code below.
double find_border ( double mama_mia [][200])

36
11 Recursion
Recursion is a bit finicky but useful. Especially if you want to backtrack in programs. Recursion in the
simplest terms is a process which calls itself to solve a smaller problems of itself (weird). In this course we
have simple applications such as finding a factorial or subtracting a number constantly.

As an example, to find a factorial of any number input you can use the following code.
1 # include < stdio .h >
2

3 int find_factorial ( int c )


4 {
5 if ( c == 0)
6 {
7 return 1; // we know we can solve 0! = 1
8 }
9 else
10 {
11 c *= find_factorial (c -1) ; // Solves a smaller version of the problem using itself
12 }
13 }
14

15 int main ( void )


16 {
17 int c , r;
18
19 printf ( " Please enter a number : " ) ;
20 scanf ( " % d" , &c ) ;
21
22 r = find_factorial ( c ) ;
23 printf ( " The factorial is : % d " , r ) ;
24
25 return (0) ;
26 }

The example below is another use of recursion. We find the greatest common factor.
1 # include < stdio .h >
2
3 int gcd ( int x , int y )
4 {
5 if ( y == 0 )
6 {
7 return x;
8 }
9 else
10 {
11 gcd (y , x %y );
12 }
13 }
14
15 int main ( void )
16 {
17 int g ,r , ans ;
18

19 printf ( " Please enter two numbers : " )


20 scanf ( " % d % d" , &g , & r) ;

37
21
22 ans = gcd (g , r) ;
23 printf ( " The greatest common demoninator is : %d " , ans ) ;
24
25 return (0) ;
26 }

To make sure you do not get lost when deal with these recursive functions. Always develop a mathematical
way of looking at your recursive function. For example, in the code above we have the following function
for gcd.
{
x if y = 0
gcd(x, y) = (7)
gcd(x, remainder(x, y)) if y > 0

You can compute the output without rereading the code multiple times. For more complex examples, try
solving the “N Queens Problem”.

38
12 Dynamic Memory Allocation
For dynamic memory allocation we will be using functions from the standard library.
# include < stdlib .h >

Dynamic memory allocation is used when we don’t know how much memory we are going to be using.
Remember when I said that an array could be thought of as a pointer. Now we are going to create an
array using a pointer.

In order to create a dynamic one dimensional array of integers,


1 # include < stdio .h >
2 # include < stdlib .h >
3

4 int main ( void )


5 {
6 int amount ;
7
8 printf ( " Please enter the number of integers in your array " )
9 scanf ( " % d" , & amount ) ;
10
11 int * array = ( int *) malloc ( amount * sizeof ( int )) ;
12 if ( array != NULL )
13 {
14 for ( int i = 0; i < amount ; i ++)
15 {
16 printf ( " Please enter integer %d : " , i +1) ;
17 scanf ( " %d " , & array [ i ]) ;
18 }
19 printf ( " All integers stored " );
20 }
21 free ( array ) ;
22 return (0) ;
23 }

This may look confusing. But let me help digest this new code.
int * array = ( int *) malloc ( amount * sizeof ( int ) ) ;

This line is basically the only “new” code.


malloc ( size_in_bytes )

malloc allows us to allocate memory in bytes


sizeof ( int )

returns the size of an integer in bytes


amount * sizeof ( int )

amount is the number to be stored in an array. So the total size the array will use is amount multiplied by
the size of an integer.
( int *) malloc ( amount * sizeof ( int ) ) ;

using (int *) is technically not needed but to be verbose we need to change the type to something a
pointer can hold.
int * array = ( int *) malloc ( amount * sizeof ( int ) ) ;

39
So therefore, this piece of code assigns the total amount of memory which is going to be accessed through
this pointer. Notice how the use of accessing data is still the same notation used for arrays
array [ element ]

We need to make sure the memory allocated to the array is deallocated and free to use by the system again
free ( array );

In C we can use two commands to allocate space

1. malloc

2. calloc

malloc only uses one argument which is the total space to allocate.
malloc ( amount * sizeof ( int ))

But calloc takes two arguments which number of them to allocate and the type respectively
calloc ( amount , sizeof ( int ))

In the previous example, we can replace line 11 with the following


int * array = ( int *) calloc ( amount , sizeof ( int )) ;

The main advantage of using calloc over malloc is that it allocates space, but also zeros the data spaces
allocated. In doing so, malloc is effectively slower than calloc. In our previous example, we don’t care if
the initialized array is all zeroed.

Note: Two dimensional dynamic arrays are not important for this course. However, you should under-
stand how to use arrays and two dimensional array with functions.

Exam Tip: You have to know how to create a dynamic array; as shown above; by hand.

40
13 Linked Lists
Do you remember those detective shows? There is always one chiché scene in the detectives house where
a wall is filled with red string attached to tacks holding up paper clippings. Each tack contains data, but
it also holds the string to the next tack.

Well, it’s almost like a linked list. In programming terms, each tack would be called a node, and the string
would be the links. Each node is located at some address, and it is joined to other nodes by links. Each
node contains data and the location of the next node. Currently, we can’t represent a node by a variable,
because we can store two data types in one. This means we have to create a composite data type. The
general form for creating a new data type is as follows
typedef struct NODE
{
struct NODE * ptr ;
int data ;
} node ;
Let’s explain some of this code in easier context.

typedef is used to rename data types.


typedef int peewee ; // we just renamed the keyword int to peewee
peewee a = 2;
struct <data_name> {} is used to create a composite data type. What’s annoying is every time you
create a new structure. You have to use struct <data_name> to refer to it.
struct int_and_float // New composite data type to hold an int and float
{
int a;
float b;
}
struct int_and_float var ; // Decalring new variable to be type int_and_float
So we can rename struct int_and_float to something easier to use
typedef struct int_and_float // typedef is remaing the whole structure
{
int a;
float b;
} iaf ; // Notice the semi - colon . This is the line end for typedef

iaf var ; // Decalring new variable to be type int_and_float

41
Therefore, going back to our initial code,
typedef struct NODE
{
struct NODE * ptr ;
int data ;
} node ;

We have a composite data type called struct NODE which contains a pointer to other struct NODE vari-
ables and data. struct NODE is also renamed to be node for ease of use.

How do you access the data and pointer of a composite data type? You use the dot operator!
< composite data type variable >. < value to access >

An example of using the dot operator is shown below.


typedef struct int_and_float
{
int integer ;
float floating_point ;
} iaf ;

iaf var ; // var now is a composite data type

var . integer = 3; // Accessing the values stored in var


var . floating_point = 3.1415;

We can only use the dot operator when you are accessing the data using the variable. When accessing the
data using a pointer, you use the arrow operator
iaf * t = & var ;

In the code above, note that t -> data is the same as var.data. The difference is because you are using
a pointer to access the data, whereas simply using the variable. This is very useful as shown below.

The first node in a linked list is called a head. The address of the head should be carefully used or you
risk losing the list. The last value in the list should always have it’s pointer set to null.
1 # include < stdio .h >
2
3 typedef struct NODE
4 {
5 struct NODE * ptr ;
6 int data ;
7 } node ;
8
9 int main ( void )
10 {
11 node head ;
12 node node_a ;
13 node node_b ;
14
15 head . ptr = & node_a ;
16 node_a . ptr = & node_b ;
17 node_b . ptr = NULL ;
18
19 head . data = 1;
20 node_a . data = 2;

42
21 node_b . data = 3;
22

23 for ( node * t = & head ; t != NULL ; t -> ptr ) // Transverse the list using a pointer
24 {
25 printf ( " % d " , t -> data ) ;
26 }
27
28 return (0) ;
29 }

This code should output: “1 2 3 ”. Using linked lists can be very tricky. This is a topic where a lack of
practice leads to a lack of understanding. I encourage you to make a linked list on your own. Maybe even
try doubly linked lists (you keep track of the previous element and the next element using their addresses).

Additionally, please play around and see if you can create functions to add links, delete links, and find
specific values in the linked list.

Exam Tip: Study the example above

And we are finally done the main portion of this guide. I wrote this in 3 days so please excuse any mistakes.

43
14 Labs
14.1 Lab 1
1 # include < stdio .h >
2
3 int main ()
4 {
5 // You should use the function printf ("") . Anything inside the quotations will be
printed .
6 printf ( " This \ nIs \ nMy \ Second \ nC \ nProgram \n " )
7 // the "\ n " makes the printf function print a newline on the screen .
8 printf ( " X O X \ nO O X\ nO X O " );
9

10 return 0;
11 }

44
14.2 Lab 2
For this lab, you have to understand syntax and arithmetic. Additionally, you should also try to compute
each the value of c.
1 # include < stdio .h >
2
3 int main ( void )
4 {
5 int a = 3, b = 4 , e ;
6 double f = 4 , c , d ;
7
8 d = a + b;
9 c = a/ f - b;
10 printf ( " The value of d is % lf \n " , d ) ;
11 return (0) ;
12 }

45
14.3 Lab 3
1 # include < stdio .h >
2 # include < math .h >
3
4 int main ( void )
5 {
6 int num1 , num2 , num3 , num4 , sumTotal , sumDifference , sumSquares ;
7 double sumSqrt ;
8
9 printf ( " Please input values for num 1 -4: \ n" );
10 scanf ( " % d % d %d %d " , & num1 , & num2 , & num3 , & num4 ) ;
11
12 sumTotal = num1 + num2 + num3 + num4 ;
13 sumDifference = ( num1 + num2 ) -( num3 + num4 ) ;
14
15 // Instead of using the math function to square variables ,
16 // it would be easier to multiply them by themselves .
17 sumSquares = num1 * num1 + num2 * num2 + num3 * num3 + num4 * num4 ;
18
19 // sumSqrt should be a double value , therefore you should cast any ints into doubles .
20 // This is not needed because integer and floats always convert to floats
21 // But you should try and stay consistent
22 sumSqrt = sqrt ( sumSquares ) /( double ) ( sumTotal ) ;
23
24 // Printing values using placeholders such as % d and % lf
25 printf ( " The sum total is : % d \n " , sumTotal ) ;
26 printf ( " The difference between the first two and last two integers %d \ n" , sumDifference )
;
27 printf ( " The sum of the squares is : % d\ n " , sumSquares ) ;
28 printf ( " The square root of the sum of squares divided by the sum totoal is : %.2 lf \n " ,
sumSqrt ) ;
29
30 return 0;
31 }

46
For the code below, you will notice the use of loops and arrays. This is a faster and more simpler method,
but not expected for you to do. Instead of using a loop, you would have to input each value. In the section
covering loops, we noted that whenever you have to repeat an action numerous times it’s simpler to use a
loop.
1 # include < stdio .h >
2
3 int main ()
4 {
5 // Creating required variables .
6 double convertedYards , convertedMiles , toConvert ;
7 // Using an arrat to store values of the data
8 int values [4] = {100 ,200 ,400 ,800};
9
10 printf ( " --- - -- - -- -- -- -- -- -- - - - - - - - - - - - - - - - -\ n" ) ;
11 printf ( " | Meters | Yards | Miles |\ n" ) ;
12 printf ( " --- - -- - -- -- -- -- -- -- - - - - - - - - - - - - - - - -\ n" ) ;
13

14 // Loop to print each value converted to yards and miles


15 for ( int i = 0; i < 4; i ++)
16 {
17 // Convert and save the values to temp variables
18 convertedYards = ( double ) values [ i ] * 1.094;
19 convertedMiles = ( double ) values [ i ] * 0.0006215;
20 // Print temp variables
21 printf ( " | %6 d | %9 lf | %9 lf |\ n " , values [ i ] , convertedYards , convertedMiles );
22 }
23
24 // ask for input from the user
25 printf ( " Please enter a value in meters to convert : " ) ;
26
27 // Remember & var means the address of var
28 // for scanf statements you have to store input at the address
29 scanf ( " % lf " , & toConvert ) ;
30
31 // Convert input into yards and miles
32 convertedYards = toConvert * 1.094;
33 convertedMiles = toConvert * 0.0006215;
34
35 // finally print the converted yards and miles
36 printf ( " Yards : %9 lf \ nMiles : %9 lf " , convertedYards , convertedMiles ) ;
37

38 return 0;
39 }

47
14.4 Lab 4
1 # include < stdio .h >
2
3 int main ()
4 {
5 int a1 , a2 , t1 , t2 , sum ;
6 double avg ;
7 char grade ;
8
9
10 printf ( " Input 2 assignment marks : " ) ;
11
12 // This is yet another method of input validation
13 while ( scanf ( " %d %d " , & a1 ,& a2 ) == EOF || a1 > 100 || a2 > 100)
14 {
15 printf ( " \ nPlease enter valid integer values \ n ") ;
16 printf ( " Input 2 assignment marks : ") ;
17 }
18 printf ( " Input 2 test marks : ") ;
19
20 // Another use of input validation
21 while ( scanf ( " %d %d " , & t1 ,& t2 ) == EOF || t1 > 100 || t2 > 100)
22 {
23 printf ( " Please enter valid integer values \ n" ) ;
24 printf ( " Input 2 test marks : ") ;
25 }
26
27 sum = ( a1 + a2 + t1 + t2 ) ;
28

29 avg = ( double ) sum /4;


30
31 printf ( " The average of the marks input : %.2 lf " , avg ) ;
32
33 // Try and use " else if " or switch statements
34 if ( avg >= 80)
35 grade = 'A ';
36 else
37 if ( avg >= 70)
38 grade = 'B ';
39 else
40 if ( avg >= 60)
41 grade = 'C ';
42 else
43 if ( avg >= 50)
44 grade = 'D ';
45 else
46 grade = 'F ';
47
48 printf ( " \ nBased of the mark average above , you currently have a mark : %c " , grade );
49
50 return 0;
51 }

48
14.5 Lab 5
1 # include < stdio .h >
2
3 int main ()
4 {
5 // Set up variables and file pointer
6 int num_samples , beach_num , num_orgs_per_100 , total ;
7 FILE * data = fopen ( " data . txt " , " r" ) ;
8 double sample_avg = 0;
9
10 // Make sure the file exists
11 if ( data )
12 {
13
14 printf ( " Beach | Sample Num | Samples | Samples Average | Safe or Not Safe \n " );
15
16 // Statement used to read to the end of file
17 while (! feof ( data ) )
18 {
19 // Read the data from the file and store in vars
20 fscanf ( data , "% d % d " , & beach_num , & num_samples ) ;
21
22 printf ( " %3 d |%7 d | " , beach_num , num_samples ) ;
23
24 total = 0;
25
26 for ( int i = 0; i < num_samples ; i ++)
27 {
28

29 // Read data from the file and store in vars


30 fscanf ( data , "% d " , & num_orgs_per_100 ) ;
31 total += num_orgs_per_100 ;
32 printf ( "% d " , num_orgs_per_100 ) ;
33
34 // Find out when to print a comma ( more asthetic )
35 if (i +1 < num_samples )
36 printf ( " , " );
37 }
38 // Find average
39 sample_avg = total / num_samples ;
40

41 printf ( "| %.1 lf " , sample_avg ) ;


42
43 if ( sample_avg < 3500)
44 printf ( "| Safe \ n ");
45 else
46 printf ( "| Not Safe \ n" );
47
48 // Find the number which terminates the loop
49 if ( beach_num == -17)
50 break ;
51 }
52 // Free the file pointer
53 fclose ( data ) ;
54 }
55 // If the file was unable to open

49
56 else
57 printf ( " Unable to open file " ) ;
58 }

Note: The code feof(<file_name>) returns true if the file pointer is at the end of the file. Therefore, the
code below will simply run until it reaches the end of the file.
while (! feof ( file_name ) )
{
// code
}

50
14.6 Lab 6
1 # include < stdio .h >
2
3 // Forward Declaration of 3 functions expecting 3 integer arguments
4 int max_of_3 ( int , int , int );
5 int min_of_3 ( int , int , int );
6 int mid_of_3 ( int , int , int );
7
8
9 int main ()
10 {
11
12 // Our variables are declared here
13 int a ,b ,c;
14 int max , min , mid ;
15
16 // Ask the user for input
17 printf ( " Please enter a value for a ,b ,c : " );
18 scanf ( " % d % d %d " ,&a ,& b ,& c ) ;
19
20 // Functions are used to dertermine the max , min , and mid values
21 max = max_of_3 (a ,b , c ) ;
22 min = min_of_3 (a ,b , c ) ;
23 mid = mid_of_3 (a ,b , c ) ;
24
25 printf ( " The value from least to greatest are : % d % d % d" , min , mid , max ) ;
26
27 return 0;
28 }
29
30
31 int max_of_3 ( int a , int b , int c)
32 {
33 // Check if a is bigger than the rest
34 if ( a > b && a > c )
35 {
36 return a; // Return a as it is bigger than the rest
37 }
38 // Check if b is bigger than the rest
39 else if (b > a && b > c )
40 {
41 return b;
42 }
43 // Therefore , if a and b are not the biggest . The biggest will therefore be c
44 else
45 {
46 return c;
47 }
48 }
49
50 int min_of_3 ( int a , int b , int c )
51 {
52 if ( a < b && a < c ) // Find the smallest using comparitors
53 return a;
54 else if (b < a && b < c )
55 return b;

51
56 else
57 return c;
58 }
59
60
61 int mid_of_3 ( int a , int b , int c )
62 {
63 int max , min ; // Check what number has not been used from the 3
64 max = max_of_3 (a ,b , c ) ;
65 min = min_of_3 (a ,b , c ) ;
66
67 if ( a != max && a != min )
68 return a ;
69 else if (b != max && b != min )
70 return b ;
71 else
72 return c ;
73 }

Note: The second part of Lab 6 is an exercise left to the reader.

52
14.7 Lab 8
This was a more dynamic approach to the problem. The way you would be expected to setup your array
and pass it through functions is covered in section 3.5.4 and 10.3. Other than the set up of the array,
everything is the same. If you have time, try and understand the setup of the dynamic two dimensional
array below.
1 # include < stdio .h >
2 # include < stdlib .h >
3
4 void print_matrix ( int ** a , int matrix_size ) ;
5
6 int main ()
7 {
8

9 FILE * data_file ; // File pointer


10 char * data [] = { " dat1 . dat " }; // Ability to use multiple files
11 int num_of_data_files = sizeof ( data ) / sizeof ( data [0]) ; // Find how many files are there
12
13 // For every file
14 for ( int i = 0; i < num_of_data_files ; i ++)
15 {
16 data_file = fopen ( data [ i ] , " r" ) ; // Open each data file
17
18 int matrix_size = 0;
19 fscanf ( data_file , "% d " , & matrix_size ) ; // Gets the size of the matrix
20

21 int ** a = malloc ( matrix_size * sizeof ( int )) ; // Dynamic array based on the size
22 for ( int j = 0; j < matrix_size ; j ++)
23 {
24 a[j ] = malloc ( matrix_size * sizeof ( int ) ) ;
25 }
26

27 for ( int x = 0; x < matrix_size ; x ++)


28 {
29 for ( int y = 0; y < matrix_size ; y ++)
30 {
31 fscanf ( data_file , " % d" , & a [ x ][ y ]) ; // Store file data inside matrix
32 }
33 }
34
35 print_matrix (a , matrix_size ) ; // Print the stored matrix
36 printf ( " Now printing diagonal and cross diagonal \n " );
37
38

39 for ( int t = 0; t < matrix_size ; t ++) // Print the diagonal


40 {
41 printf ( "% d\ t " , a [ t ][ t ]) ;
42 }
43 printf ( " \ n" );
44

45 for ( int k = 0; k < matrix_size ; k ++) // Print the cross - diganonal


46 {
47 printf ( "% d\ t " , a [ k ][( matrix_size -1) -k ]) ;
48 }
49 // Deallocating and freeing is not required for a regular two dimensional array
50

53
51 for ( int d = 0; d < matrix_size ; d ++) // Empty dymanic array
52 {
53 a[d ] = NULL ;
54 }
55 free (a ); // Deallocate memory
56
57 fclose ( data_file );
58 }
59 }
60
61 // Function that prints the matrix
62 void print_matrix ( int ** a , int matrix_size )
63 {
64 for ( int x = 0; x < matrix_size ; x ++)
65 {
66 for ( int y = 0; y < matrix_size ; y ++)
67 {
68 printf ( "% d\ t " , a [ x ][ y ]) ;
69 }
70 printf ( " \ n" );
71 }
72 }

54
15 Assignment 1
1 # include < stdio .h >
2 # include < math .h >
3
4 int main ()
5 {
6 // Counters to develop the fibonacci sequence
7 int a = 0, a_1 = 1, a_2 = 0;
8
9 int amountSqrt = 0;
10
11 // Keep a counter to how many numbers were calculated
12 int amountNums = 1;
13
14 // 31 st element
15 int num31 = 0;
16
17 double goldenRatio = 0;
18
19 while (a <= sequenceMax )
20 {
21
22 printf ( " % d " , a );
23

24 if ( a_1 != 0)
25 {
26 goldenRatio = ( float )a / a_1 ;
27 printf ( " %.3 lf \ n " , goldenRatio ) ;
28 }
29 else
30 printf ( " Skip \ n ") ;
31
32 // The second last element is equal to the last element
33 a_2 = a_1 ;
34 // The last element is equal to the current element
35 a_1 = a;
36 // The new current element is a sum of the two previous elements
37 a = a_1 + a_2 ;
38
39 // check if the integer has a perfect integer squareroot
40 if ( sqrt (( float ) a) == floor ( sqrt (( float )a ) ) )
41 {
42 amountSqrt ++;
43 printf ( "\ t perfect square " )
44 }
45
46 amountNums ++;
47 // Save the 31 element in the sequence and will print it out later
48 if ( amountNums == 31)
49 {
50 num31 = a ;
51 }
52
53 }
54 printf ( " \n \ nThe amount of numbers displayed is %d " , amountNums -1) ;

55
55
56 printf ( " \n \ nThe number of perfectly sqrt root integers are : % d" , amountSqrt ) ;
57
58 if ( num31 )
59 printf ( "\ nThe 31 st number in the sequence is % d" , num31 ) ;
60 else
61 printf ( " \ nThere was not 31 elements in the fibonacci sequence up to % d " , sequenceMax );
62

63 return 0;
64 }

56
16 Assignment 2
The code below is left as snippets for the reader. Using this guide, go through each function and understand
how they work. You should redo assignment 2 a few times before going to the exam.
1 # include < stdio .h >
2
3
4 double average ( double a [][200] , int row , int col )
5 {
6 double avg = 0;
7

8 for ( int i =0 ;i < row ; i ++ )


9 {
10 for ( int k = 0; k < col ; k ++)
11 {
12 avg = avg + a [ i ][ k ];
13 }
14 }
15 avg = avg /( row * col );
16 return avg ;
17 }
18
19 double corner ( double a [][200] , int row , int col )
20 {
21 double corner_sum = a [0][0] + a [0][ col -1] + a [ row -1][0] + a [ row -1][ col -1];
22 return corner_sum ;
23 }
24
25 double row_avg ( double a [][200] , int row , int col , int r_c )
26 {
27 double row_avg = 0;
28 for ( int i =0 ;i < col ; i ++)
29 {
30 row_avg += a [ r_c -1][ i ];
31

32 }
33 row_avg /= col ;
34 }
35
36 double calc_border ( double a [][200] , int row , int col )
37 {
38 double avg = 0;
39
40 for ( int k = 0; k < col ; k ++)
41 {
42 avg = avg + a [0][ k] + a [ row -1][ k ];
43 }
44 for ( int r = 0; r < row ; r ++)
45 {
46 avg = avg + a[ r ][0] + a [ r ][ col -1];
47 }
48 avg -= corner (a , row , col ) ;
49

50 }
51
52 double diag ( double a [][200] , int row )

57
53 {
54 double avg = 0;
55
56 for ( int i =0 ;i < row ; i ++ )
57 {
58 avg += a[ i ][ i ];
59 }
60 avg /= row ;
61 return avg ;
62 }
63
64
65 int main ( void )
66 {
67 FILE * data = fopen ( " data . dat " , " r" ) ;
68 int row , col ;
69 int r_c , c_c ;
70
71 fscanf ( data , "% d % d " , & row , & col ) ;
72

73 double a [ row ][ col ];


74
75 for ( int i = 0; i < row ; i ++)
76 {
77 for ( int j = 0; j < col ; j ++)
78 {
79 fscanf ( data , "% lf " ,& a [ i ][ j ]) ;
80 }
81 }
82
83 for ( int k = 0; k < row ; k ++)
84 {
85 for ( int c = 0; c < col ; c ++)
86 {
87 printf ( "% lf \ t" , a [ k ][ c ]) ;
88 }
89 printf ( " \ n" );
90 }
91
92 fscanf ( data , " %d %d " , & r_c , & c_c ) ;
93
94 printf ( " AVERAGE : % lf " , average (a , row , col ) ) ;
95 printf ( " ROW : % lf " , row_avg (a , row , col , r_c ) ) ;
96

97 fclose ( data ) ;
98
99 }

58
17 Ending Comments
If you understand the material discussed in this overview, have ample practice by programming daily, and
can easily solve the lab problem, then there is no reason to ever show up to class. I will tell you straight
to your face: if you do not practice, then you will fail.

59

You might also like