Pre Course Lectures
Pre Course Lectures
language
Wes Armour
www.oerc.ox.ac.uk
Learning outcomes
https://godbolt.org/
The components of a C program
The C programing language has a rich set of tools that you can use in your code to
make it portable and easer to write.
Using these tools (contained in the C standard library) will save you time, effort and
make your code more understandable to others.
https://xkcd.com/927/
Standard header files
Name Description
char x;
Eight bits combine to produce a byte. If each bit
in a byte can be either a zero or a one then a return(0);
byte can represent:
}
2 x 2 x 2 x 2 x 2 x 2 x 2 x 2 = 28 = 256
To ensure that your code is easy to read and understand it’s important to write your code in a
readable and maintainable way.
Adding comments to your code will help others understand your code when they come to work on it.
A comment is text that you or other programmers can read but is ignored by the compiler. A good
code has more comments than source code.
https://xkcd.com/979/
Naming
return(0);
}
Expressions
variable = expression;
#include <stdio.h>
However statements can become
complicated very quickly. Consider #define PI 3.14
summing the area of three circles: int main() {
#include <stdio.h>
return(0);
• A function may or may not have a return
value. }
#include <stdio.h>
Looking at our example in a little more detail.
#define PI 3.14
float area_of_circle(float radius);
float area_of_circle(float radius);
#include <stdio.h>
Our previous example code could be come very
cumbersome and difficult to maintain if we #define PI 3.14
C has arrays to help with this. An array is a float radius[3] = {5.0, 10.0, 15.0};
indexed group of data storage, all of the same float area[3];
type. float total_area;
area[0] = area_of_circle(radius[0]);
C arrays are indexed from 0 to n-1, where n is area[1] = area_of_circle(radius[1]);
the number of elements in the array. area[2] = area_of_circle(radius[2]);
Here we define an array called radius that has 3 float area_of_circle(float radius) {
To solve a particular problem your C program might need to take different execution
paths. These might depend on different inputs.
For example we could construct a program that calculates the area or circumference of
a circle depending on what the user requests.
C has various statements that give the programmer control over the flow of execution in
their program. However it’s important to use these sensibly and not create
unmaintainable “spaghetti” code.
https://xkcd.com/292/
Relational operators
#include <stdio.h>
}
Program control – if statement
#include <stdio.h>
#define PI 3.14
The for statement (often called the for loop)
executes a block of statements a certain float area_of_circle(float radius);
number of defined times. It has the following
int main() {
structure:
int i;
for(start point; relational expression; increment) float radius[3] = {5.0, 10.0, 15.0};
float area[3];
statement; float total_area;
#include <stdio.h>
#define PI 3.14
The while statement (often called the while
loop) executes a block of statements while a float area_of_circle(float radius);
certain condition is true. It has the following
int main() {
structure:
int i;
while(relational expression) float radius[3] = {5.0, 10.0, 15.0};
float area[3];
statement; float total_area;
while(A) A
B;
To format input and output C uses conversion specifiers and escape sequences.
The use of these will become clear in the following slides.
Conversion
Type converted Description
specifier
%c char char single character
%d int signed integer
%ld long int long signed integer
%f float, double float or double signed decimal
%s char[] sequence of characters
%u int unsigned integer
%lu long int long unsigned integer
Inputs and Outputs - printf
#include <stdio.h>
#define PI 3.14
int main() {
The keen eyed amongst you will
int i;
have noticed an issue with our
float radius[3] = {5.0, 10.0, 15.0};
example program. Although we float area[3];
calculate a sum of areas of circles, float total_area;
we never actually get the result out
i=0;
of our program. while(i<3) {
area[i] = area_of_circle(radius[i]);
We can do this using the printf() i++;
}
statement. In our example program
on the right you can see that we’ve total_area = 0.0;
for(i=0; i<3; i++) {
added a printf() statement.
total_area += area[i];
}
It works as follows… printf(“\nTotal area is:\t%f\n”, total_area);
return(0);
}
#include <stdio.h>
#define PI 3.14
int i;
We start a new line using the “\n” escape float radius[3] = {5.0, 10.0, 15.0};
sequence. float area[3];
float total_area;
#include <stdio.h>
#define PI 3.14
int main() {
int i=0;
You will have also noticed that we float radius[3];
hardcode the radii into our code float area[3];
{5.0,10.0,15.0} float total_area;
total_area = 0.0;
We can use the scanf() statement for(i=0; i<3; i++) {
to read three different values, total_area += area[i];
}
meaning our code will work for any printf(“\nTotal area is:\t%f\n”, total_area);
combination of radii. return(0);
}
#include <stdio.h>
#define PI 3.14
http://www.learn-c.org/
https://www.cprogramming.com/tutorial/c-tutorial.html
https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html
A deeper dive into C programming
Wes Armour
www.oerc.ox.ac.uk
Learning Outcomes
• Multidimensional arrays.
• An introduction to pointers.
• Variable scope.
In lecture two we used an array called radius to hold different values for the radius of a
circle. This can be represented schematically below.
array[0] holds the integer value 5, array[1] holds the integer value 10, up to
array[n-1] which holds the integer value 11.
int array[n]
5 10 15 … 11
array[0] array[n-1]
Using Arrays – multidimensional
int identity[3][3]
C uses row-major ordering. What does By Cmglee [CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)
this mean? or GFDL (http://www.gnu.org/copyleft/fdl.html)], from Wikimedia Commons
Using Arrays – multidimensional
Row-major ordering means that consecutive elements within a row reside next to each other
in memory. Rows are then stored (in order) in memory consecutively.
Lets return to our example of the 3x3 identity matrix (below). We see the first element of the
first row is stored first, followed by the second element of the first row, and so on. Then the
first element of the second row is stored, followed by the second element of the second row
etc.
int array[3][3]
1 0 0 0 1 0 0 0 1
This can be generalised. The example below is the initialisation for a 3D array which
has dimensions p x q x r (I’ve omitted actual values in an attempt to make this clearer).
#include <stdio.h>
#include <stdlib.h>
int main() {
Finally how do we used these arrays
in our code? The code on the right const int els_in_row = 3;
gives an example of how we initialise const int els_in_col = 3;
a matrix with random numbers.
int random[els_in_col][els_in_row];
To do this we use the random
number generator called rand(). for(int col=0; col<els_in_col; col++) {
for(int row=0; row<els_in_row; row++) {
This is a function that returns a random[col][row]=rand();
random integer between 0 and }
RAND_MAX (where RAND_MAX is }
at least 32767). It has the function
prototype: printf(“\n\n”);
for(int col=0; col<els_in_col; col++) {
for(int row=0; row<els_in_row; row++) {
int rand(void); printf(“%d\t\t”, random[col][row]);
}
which is included in stdlib.h printf(“\n”);
}
return(0);
}
Understanding pointers - memory locations
Schematically this is
represented on the right.
Understanding pointers – creating a pointer
int array[5]
As before we declare 5 … 11
our integer array, let’s
declare space for 5 Address 98 99 100 … 100+(5-1)
integers (so n=5)
array[0] array[4]
Understanding pointers – creating a pointer
int array[5]
Next we declare a variable
(called ptr_array) that ? 5 … 11
happens to live at address
98. Address 98 99 100 … 100+(5-1)
At this point it is uninitialized, ptr_array array[0] array[4]
so its value is undetermined.
Understanding pointers – creating a pointer
int array[5]
Now we store the address
of array[0] in the variable
ptr_array 100 5 … 11
Address 98 99 100 … 100+(5-1)
Because ptr_array
contains the address of
array[0] it points to where ptr_array array[0] array[4]
array is stored in memory.
#include <stdio.h>
area = 0;
The code on the right shows how this
ptr_radius = &radius;
works and tests the results of using a area = PI * (*ptr_radius) * (*ptr_radius);
pointer. printf(“\nArea with pointer:\t%f”, area);
return(0);
}
Understanding pointers – using pointers
We can use arithmetic on pointers, just like we can any other numbers.
A schematic demonstrating this is given below.
5 10 15 … 11
https://xkcd.com/138/
Break
Characters and Strings
#include <stdio.h>
C uses the char variable to store
characters and strings. int main() {
https://en.wikipedia.org/wiki/ASCII
Characters and Strings
Characters and Strings
#include <stdio.h>
int main() {
You can also allocate storage
char one[] = {‘H’,’a’,’r’,’d’};
space for your string at compile
char *two = “Easy”;
time. To do this use one of the two
ways demonstrated in the code on printf(“\nString one:\t%s”, one);
the right. printf(“\nString two:\t%s”, two);
return(0);
}
Characters and Strings
#include <stdio.h>
For a user to interact with your
program they need to be able to int main() {
pass it input. C has two methods to
read strings from the keyboard. char one[256];
char two[256];
char three[256];
The first is the gets() function. This
simply reads all input to the int count;
keyboard until a user presses the
Enter key. printf(“\nType some text and press Enter:\n”);
gets(one);
printf(“\nYou typed:\t%s”, one);
The second is the scanf() function,
this requires the programmer to printf(“\nType two words and press Enter:\n”);
specify the format of the input count = scanf(“%s %s”, &two, &three);
using conversion specifiers. printf(“\nYou entered %d words.”, count);
printf(“\nYour words are: %s and %s”, two, three);
Both methods are demonstrated in
return(0);
the code on the right.
}
Variable scope – Global variables
#include <stdio.h>
#include <stdio.h>
int main() {
The switch statement is useful
when you want to compare a int choice;
value against a list of known printf(“\nEnter a choice: 1,2 or 3 to exit: “);
values. An example might be scanf(“%d”, &choice);
allowable responses for input.
switch(choice) {
More general comparisons are case 1:
done by if as we saw printf(“You entered 1”);
previously. break;
case 2:
An example of how the switch printf(“You entered 2”);
break;
statement can be used is given
case 3:
on the right. printf(“You entered 3. Exiting.”);
exit(0);
}
return(0);
}
Using files
C provides a number of
different tools for interacting
with files stored on your
computer.
fopen()
fclose()
fprintf()
fscanf()
https://xkcd.com/1360/
Using files – fopen and fclose
#include <stdio.h>
The code on the right gives examples
int main() {
of using the fprintf()and
fscanf() functions (it’s a bit FILE *fp;
squashed). char filename[200], mode[4];
int index[10], square[10];
We open a file as before (note the bad
printf(“\nEnter your file: “), gets(filename);
coding practice, the code fails silently if
printf(“\nEnter a file mode: “), gets(mode);
fopen() fails). if((fp=fopen(filename, mode)) == NULL) exit(1);
https://en.wikipedia.org/wiki/C_dynamic_memory_allocation
Working with memory
#include <stdio.h>
Lets return to our example code from #include <stdlib.h>
#define PI 3.14
Lecture two. We can modify this code so
that is asks the user how many areas they float area_of_circle(float radius);
want to calculate and then dynamically int main() {
allocates memory for them. int i=0, number_of_circles=0;
float *radius;
float total_area=0;
We start by declaring a pointer:
printf(“\nEnter the number of circles to calculate:\t”);
scanf(“%d”, &number_of_circles);
float *radius;
radius=(float *)malloc(number_of_circles*sizeof(float));
i=0;
We then use malloc() to allocate the while(i<number_of_circles) {
total_area += area_of_circle(radius[i]);
memory that we need. i++;
}
printf(“\nTotal area is:\t%f\n”, total_area);
After this we use scanf() to get the radii
from the user. free(radius);
return(0);
}
This time we accumulate directly to
total_area float area_of_circle(float radius) {
float area = PI * radius * radius;
return area;
Finally we free()our allocated memory. }
Working with memory – malloc()
int ** identity;
int num_rows = 3;
int num_cols = 3;
Finally lets return to our identity matrix.
// To allocate
int identity[3][3]
identity = (int **)malloc(num_rows * sizeof(int*));
for(int i=0; i<num_rows; i++) {
How can we allocate memory for this identity[i] = (int *)malloc(num_cols * sizeof(int));
using malloc()? }
identity
**int
Schematically this can be
represented by the diagram
on the right.
We begin by allocating a
identity[0] Identity[1] Identity[2]
*int
double pointer to type int**
This in turn points to an array
of pointers of type int*
identity[0][0] identity[1][0] identity[2][0]
http://www.learn-c.org/
https://www.cprogramming.com/tutorial/c-tutorial.html
https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html