0% found this document useful (0 votes)
10 views78 pages

Pre Course Lectures

This document serves as an introduction to the C programming language, covering its basic components, data storage, and fundamental programming concepts. It discusses high-level languages, the structure of C programs, data types, variables, constants, statements, expressions, operators, functions, and arrays. The document emphasizes the importance of readability and maintainability in code through comments and meaningful naming conventions.

Uploaded by

Iwunoh micheal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views78 pages

Pre Course Lectures

This document serves as an introduction to the C programming language, covering its basic components, data storage, and fundamental programming concepts. It discusses high-level languages, the structure of C programs, data types, variables, constants, statements, expressions, operators, functions, and arrays. The document emphasizes the importance of readability and maintainability in code through comments and meaningful naming conventions.

Uploaded by

Iwunoh micheal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 78

Introduction to the C programming

language

Wes Armour

Oxford e-Research Centre,


Department of Engineering Science

www.oerc.ox.ac.uk
Learning outcomes

In this lecture you will learn about:

• High level computer languages.

• The basic components of a C computer program.

• How data is stored on a computer.

• The difference between statements and expressions.

• What operators and functions are.

• How to control basic input and output.

• Finally how to write a basic C program.


A brief introduction

The C programming language was


devised by Dennis Ritchie at Bell labs in
1972 (yes, it’s predecessor was B!).

C is a high-level programming language,


meaning that it is possible to express
several pages of machine code in just a
few lines of C code.

Other examples of high-level languages


are BASIC, C++, Fortran and Pascal.
They are so called because they are
closer to human language than machine
languages.
A brief introduction

int square(int num) {


Such high-level languages allow HLL
a programmer to write programs return num * num;
that are independent of particular
}
types of computer. This is called
portability.
push rbp #2.21
Portability can be aided by using mov rbp, rsp #2.21 Assembly
an agreed standard when writing sub rsp, 16 #2.21
your program (such as C99). mov DWORD PTR [-16+rbp], edi #2.21 Code
mov eax, DWORD PTR [-16+rbp] #3.18
imul eax, DWORD PTR [-16+rbp] #3.18
A compiler (such as gcc or icc) is leave #3.18
used to convert your high-level ret #3.18
language program into machine
code that can be executed on a Machine
computer. 01110000011010001010100100100101001 Code
00011111111111101010111100111001010
10101010011111111111101010100101010
10101010101010101001010101010100101

https://godbolt.org/
The components of a C program

All C programs have some common elements.

• The #include directive tells the compiler


to include other files stored on your HDD
into your C program. These files will include
information that does not change between
programs that your program can use. On
the right we include the standard input and #include <stdio.h>
output library.
int main() {
• The main() function is the only return(0);
component that has to be included in every
C program. It is followed by a pair of }
braces: { }

• The return() statement returns values


from a function. Within the main()
function return()can be used to tell the
operating system (in our case Linux)
whether our code completed successfully.
The C standard library

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

Contains the assert macro, used to assist with


<assert.h> detecting logical errors and other types of bug in
debugging versions of a program.
The list on the right describes some
of the more common header files. A set of functions for manipulating complex
<complex.h>
numbers.
These are included into your code in
<errno.h> For testing error codes reported by library functions.
the same way as we included the
standard input/output header, using <math.h> Defines common mathematical functions.
the #include directive.
<stdbool.h> Defines a boolean data type.
<stdio.h> Defines core input and output functions
These tools can save you lots of
time, for example you could use the
random number generator included Defines numeric conversion functions, pseudo-
in <stdlib.h> rather than writing <stdlib.h> random numbers generation functions, memory
allocation, process control functions
your own.

<string.h> Defines string handling functions.

<time.h> Defines date and time handling functions


Storing data

Our previous example program didn’t do


anything though. To make this more useful we
need to store and manipulate data.

A computer stores data as strings of zeros and


ones: 100101111001…

The smallest element of data storage on a #include <stdio.h>


computer is a bit, it can be two things, a zero or
a one. int main() {

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

unique “things”. For example these “things”


could be the numbers from -128 to 127.

In the C language this would be called a char


variable.
Variables and Constants

The C language has different numeric data


types that you can use in your program Data Type Keyword Minimum Minimum Range
depending on what type of number you need to Bytes
store or manipulate. Required
Character char 1 –128 to 127
This is useful for many reasons. For example if Short integer short 2 –32767 to 32767
our program only operates on small numbers Integer int 4 –2,147,483,647 to
(-128 to 127) then we can use a char which 2,147,438,647
takes up a single byte of memory rather than Long integer long 4 –2,147,483,647 to
using a long integer that might take 4 bytes or 2,147,438,647
8 bytes of memory space. Unsigned character unsigned 1 0 to 255
char
Unsigned short unsigned 2 0 to 65535
Along with this we might want to work on integer short
decimal numbers. In C, real numbers are Unsigned integer unsigned int 4 0 to 4,294,967,295
stored as floating point variables, these have a Unsigned long unsigned 4 0 to 4,294,967,295
fractional part. Integers are stored as integer integer long
numbers and have no fractional part. Single-precision float 4 1.2E–38 to 3.4E38
floating-point
Variables with more bytes require more Double-precision double 8 2.2E–308 to
floating-point 1.8E308
computational operations to manipulate them,
so this will slow down program execution.
Variables and Constants

The amount of bytes needed to store any particular


numeric data type in memory can vary between
computer architectures.
C provides a useful function sizeof() to
determine this.
#include <stdio.h>
Numeric data types can either be a variable or a
#define PI 3.14
constant in your C program.
int main() {
Variables and literal constants can change during
your program execution, symbolic constants cannot. float radius = 10.0;
float area;
Literal constants are defined by assigning a number
area = PI * radius * radius;
to the constant:
return(0);
float radius = 10;
}
A symbolic constant can be defined using two
methods. The first is by using #define, the other by
using the const keyword.

const float radius = 10;


Statements

A statement in C is a command that instructs the


computer to do something.

An example of a statement is:


area = PI * radius * radius;
area = PI * radius * radius;

This tells the computer to multiply PI by radius and


the multiply the results of this by radius again.
The result of the multiplications is then assigned to
Is equivalent to
the variable area

All statements in C are terminated with a semi-colon


and all white space (tabs, spaces and blank lines) area =
are ignored by the compiler (apart from within a PI *
string – more later). radius
* radius
;
This allows for lots of freedom in formatting your C
program, however to ensure that your code is easy
to work with and reusable by others it is important to
ensure that it is easy to read and understand.
Comments

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.

// starts a single line comment


/* Starts a multiline comments and is ended by */

https://xkcd.com/979/
Naming

/* This is a C program that


calculates the area of a circle.
Written by Wes
wes.armour@eng.ox.ac.uk
06/05/18
Using variable names that are meaningful is */
a useful thing to do, for example:
//Include standard IO library
area = PI * radius * radius; #include <stdio.h>

is easy to understand, whereas // Define a symbolic constant, pi.


#define PI 3.14
one = c * two * two; // The main body of the program
int main() {
tells us less about what you want your code
to achieve. Take time to name variables in a // Define variables
meaningful and intuitive way. float radius = 10.0;
float area;

// Calculate the area


area = PI * radius * radius;

return(0);
}
Expressions

An expression in C is any statement


that evaluates to a numeric value. In
the most basic form this could just be
our previous example of using a
symbolic constant, PI

We use the assignment operator (=)


to assign the result of our expression
to a variable:

variable = expression;

https://www.freepik.com/free-photos-vectors/people Created by Ddraw - Freepik.com


Expressions

#include <stdio.h>
However statements can become
complicated very quickly. Consider #define PI 3.14
summing the area of three circles: int main() {

a = p*a*a+p*b*b+p*c*c; float radius_one = 5.0;


float radius_two = 10.0;
float radius_three = 15.0;
When trying to evaluate the above float area_one, area_two, area_three;
where does the computer start with float total_area;
such an expression? What stops the area_one = PI * radius_one * radius_one;
computer evaluating a+p first? area_two = PI * radius_two * radius_two;
area_three = PI * radius_three * radius_three;
Several things can help us ensure that total_area = area_one + area_two + area_three;
we get the computer to do the right
thing, separating our expression, using return(0);
brackets and operator precedence. }
Operators

An operator in C is a symbol that instructs Precedence Operator Description Associativity


the computer to perform an operation on an ++ Postfix increment Left-to-right
operand. 1 -- Postfix decrement
() Function call
++ Prefix increment Right-to-left
Precedence tells the computer which
2 -- Prefix decrement
operation should be performed first in an
sizeof Size-of
expression. Returning to our example of * Multiplication Left-to-right
calculating the area of a circle: 3 / Division
% Modulo (remainder)
area = PI * radius * radius; + Addition Left-to-right
4
- Subtraction
We see that multiply (*) has a precedence < Less than Left-to-right
of 3, whereas assignment (=) has a lower <= Less than or equal to
5
precedence of 7, meaning multiplication is > Greater than
carried out then the assignment. >= Greater than or equal to
== Equal to Left-to-right
6
!= Not equal to
The order in which the multiplication is = Direct assignment Right-to-left
carried out is determined by the += Assignment by sum
Associativity of multiplication. We see that 7 -= Assignment by difference
this is Left-to-right. So the order would be: *= Assignment by product
/= Assignment by quotient
(PI * radius) * radius
Functions

#include <stdio.h>

A function is an independent piece of C code #define PI 3.14


that performs a specific task. The function may
float area_of_circle(float radius);
or may not return a value to the calling code.
For example it might calculate the area of a int main() {
circle, or it might print a message to the screen.
float radius_one = 5.0;
float radius_two = 10.0;
• A function has a unique name float radius_three = 15.0;
float area_one, area_two, area_three;
float total_area;
• A function is independent of other parts of
your code, so it is self contained. area_one = area_of_circle(radius_one);
area_two = area_of_circle(radius_two);
area_three = area_of_circle(radius_three);
• A function performs a specific task in your
code. total_area = area_one + area_two + area_three;

return(0);
• A function may or may not have a return
value. }

float area_of_circle(float radius) {


On the right we see how we can use a function
in our example code that calculates and sums float area = PI * radius * radius;
the area of three circles.
return area;
}
Functions

#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);

Is the function prototype this tells the compiler int main() {


that a function called area_of_circle will be
float radius_one = 5.0;
used later in the code and it will take a float as float radius_two = 10.0;
an argument and return a float. float radius_three = 15.0;
float area_one, area_two, area_three;
float total_area;
We then see the function being called to
calculate area_one, area_two and area_one = area_of_circle(radius_one);
area_three. area_two = area_of_circle(radius_two);
area_three = area_of_circle(radius_three);

Finally outside of the main() { } function we total_area = area_one + area_two + area_three;


see the function definition. The first line is the
return(0);
function header and this is exactly the same as
the function prototype (without the semicolon). It }
defines the functions name, the parameter list
float area_of_circle(float radius) {
(variables and their types that are passed to the
function by the calling code), whether it returns float area = PI * radius * radius;
a value (in this case the return type is a float)
return area;
and then what it does. }
Arrays

#include <stdio.h>
Our previous example code could be come very
cumbersome and difficult to maintain if we #define PI 3.14

wanted to calculate the area of thousands of float area_of_circle(float radius);


circles.
int main() {

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]);

total_area = area[0] + area[1] + area[2];


An array is defined in the following way:
return(0);
float radius[3]; }

Here we define an array called radius that has 3 float area_of_circle(float radius) {

elements. It can be initialised using braces. float area = PI * radius * radius;

float radius[3] = {5, 10, 15}; return area;


}
Break
Program control

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

The C language has a set of relational


operators that are used to compare
expressions.
Precedence Operator Description Associativity
For example we could check to see if < Less than Left-to-right
our radius is less than or equal to 10: <= Less than or equal to
5
> Greater than
radius <= 10.0 >= Greater than or equal to
== Equal to Left-to-right
6
If our radius is less than or equal to 10 != Not equal to
the above expression evaluates to true
(represented by 1).
If our radius is greater than 10 then the
above expression evaluates to false
(represented by 0).
Logical operators

What if we need to compare more than


one expression at the same time?
C has logical operators to help with this.

Logical operators allow us to combine Precedence Operator Description Associativity


two or more relational expressions into 2 ! Logical NOT Right-to-left
one single expression. 11 && Logical AND Left-to-right
12 || Logical OR Left-to-right
For example we could check to see if
our radius is greater than 5 and less
than or equal to 10:

radius > 5.0 && radius <= 10.0


Program control – if statement

#include <stdio.h>

The if statement evaluates an #define PI 3.14


expression, if the expression
evaluates to true (1) then the code int main() {
following the if statement
float radius = 10.0;
executes.
float area;

The example on the left checks to if(radius <= 10.0) {


see if we have a radius less than or area = PI * radius * radius;
equal to 10, if we do then it }
calculates the area of a circle.
return(0);

}
Program control – if statement

The if statement also has an else


clause. The else clause executes when
the expression evaluated by the if
#include <stdio.h>
statement evaluates to false.
#define PI 3.14
An example of this is given on the right.
Here we see that when we have a int main() {
radius less than or equal to 10.0 our
code calculates the area of a circle. For float radius = 11.0;
float area;
a radius greater than 10 the code
float circumference;
calculates the circumference. In this
example radius = 11.0, so the code if(radius <= 10.0) {
would calculate the circumference. area = PI * radius * radius;
} else {
The example on the right is binary in circumference = 2.0 * PI * radius;
}
the sense that the result gives two
different outcomes dependent on return(0);
whether our expression is true or false. }
This can be expanded to give many
different outcomes by using else if
(see practical 1).
Program control – for 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;

Our example on the right executes as follows: for(i=0; i<3; i++) {


area[i] = area_of_circle(radius[i]);
}
1. start point is an integer index that is set to
zero (i = 0). total_area = 0.0;
for(i=0; i<3; i++) {
2. The relational expression is evaluated, our total_area += area[i];
index i is less than 3 (i<3) so the }
condition is true (if this evaluates to false
return(0);
the for loop terminates). }
3. Statement then executes (e.g. the area of a
circle is calculated). float area_of_circle(float radius) {
4. Next the index i is incremented by one float area = PI * radius * radius;
(i++) and execution returns to step 2.
return area;
}
Program control – for statement

The diagram on the right represents the for Start


loop as a flow diagram.

Consider the following for loop: A


for(A; B; C)
D;
B
The initial starting point A is evaluated.

Then the relational condition B is evaluated. True


D
If B evaluates to true then statement D is
executed. If B evaluates to false then the for False
loop ends. C
Once statement D has executed, the increment
C is evaluated.

The loop then returns to evaluating the End


relational condition B.
Program control – while statement

#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;

Our example on the right executes as follows: i=0;


while(i<3) {
area[i] = area_of_circle(radius[i]);
1. We fist set our integer index i to zero (i = i++;
0). }
2. The relational expression is evaluated, our total_area = 0.0;
index i is less than 3 (i<3) so the for(i=0; i<3; i++) {
condition is true (if this evaluates to false total_area += area[i];
}
the while loop terminates).
3. Statement then executes (e.g. the area of a return(0);
circle is calculate). }
4. Next the index i is incremented by one float area_of_circle(float radius) {
(i++) and execution returns to step 2. float area = PI * radius * radius;
return area;
}
Program control – while statement

The diagram on the right represents the


while loop as a flow diagram. Start

Consider the following while loop:

while(A) A
B;

The relational condition A is evaluated. True


B False
If A evaluates to true then statement B is
executed. If A evaluates to false then the
while loop ends.

Once statement B has executed, the loop End


evaluates relational condition A again.
Inputs and Outputs

Many years ago computers were


programmed using a series of punch
cards (right)!

Fortunately the C language has many


functions to help with input and output.

In this lecture we will look at two


commonly used functions.

To print information out to the screen we


will use printf()

To read information from the keyboard we


will use scanf()

These are contained in the stdio.h library.

Arnold Reinhold https://commons.wikimedia.org/wiki/File:FortranCardPROJ039.agr.jpg


Inputs and Outputs

To format input and output C uses conversion specifiers and escape sequences.
The use of these will become clear in the following slides.

Escape sequence Description


\b Backspace
\n Newline
\t Horizontal Tab
\v Vertical Tab
\\ Backslash
\' Single quotation mark
\" Double quotation mark
\? Question mark

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

float area_of_circle(float radius);

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);
}

float area_of_circle(float radius) {


float area = PI * radius * radius;
return area;
}
Inputs and Outputs - printf

#include <stdio.h>

#define PI 3.14

float area_of_circle(float radius);


We tell printf to print a string to the monitor:
“\nTotal area is:\t%f\n” int main() {

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;

We the print the characters “Total area is:” i=0;


while(i<3) {
area[i] = area_of_circle(radius[i]);
We use a tab to neatly separate our words i++;
from our output number using the “\t” }
escape sequence.
total_area = 0.0;
for(i=0; i<3; i++) {
We use the conversation specifier for a float total_area += area[i];
“%f” to output a float. }
printf(“\nTotal area is:\t%f\n”, total_area);
return(0);
We tell printf that the float to output is }
total_area
float area_of_circle(float radius) {
float area = PI * radius * radius;
return area;
}
Inputs and Outputs - scanf

#include <stdio.h>
#define PI 3.14

float area_of_circle(float radius);

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;

printf(“\nEnter three radii:\t”);


So if we wanted to use different radii scanf(“%f %f %f”, &radius[0], &radius[1], &radius[2]);
we would need to change these
while(i<3) {
values in our source code and then area[i] = area_of_circle(radius[i]);
recompile. That’s not very efficient or i++;
portable. }

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);
}

float area_of_circle(float radius) {


float area = PI * radius * radius;
return area;
}
Inputs and Outputs - scanf

#include <stdio.h>
#define PI 3.14

float area_of_circle(float radius);

The example code on the right uses the int main() {


int i=0;
scanf() statement to read three floats float radius[3];
into our code and then uses these float area[3];
values to calculate our total area as float total_area;
before. printf(“\nEnter three radii:\t”);
scanf(“%f %f %f”, &radius[0], &radius[1], &radius[2])
Once we execute our code it will print
while(i<3) {
the message “Enter three radii:” to the area[i] = area_of_circle(radius[i]);
monitor and then wait at the scanf() i++;
statement for the user to enter three }
values and press return. total_area = 0.0;
for(i=0; i<3; i++) {
scanf() reads the three values entered total_area += area[i];
}
by the user and stores them in printf(“\nTotal area is:\t%f\n”, total_area);
radius[0], radius[1] and radius[2] return(0);
respectively. }

float area_of_circle(float radius) {


float area = PI * radius * radius;
return area;
}
What have we learnt?

In this lecture you have learnt about the


basic building blocks of a C program.

You have learnt about standard


libraries, expressions and statements.

We have covered how data is stored on


a computer and how it is represented in
C.

You have learnt about functions,


operators, both logical and relational
and program control.

Finally we covered the basics of input


and output.

You should now be in a position to write


your own C program.
Further reading

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

Oxford e-Research Centre,


Department of Engineering Science

www.oerc.ox.ac.uk
Learning Outcomes

In this lecture you will learn about:

• More about arrays.

• Multidimensional arrays.

• An introduction to pointers.

• Characters and strings.

• Variable scope.

• Advanced program control.

• How to work with files.

• Dynamic memory allocation.


Using Arrays

In lecture two we presented the concept of


arrays and how to use them. Now let’s look
a them in some more detail. What
characterises an array?

• An array is a collection of data storage


locations.
• An array holds data all of the same type.
• Each storage location is called an array
element.
• C arrays are indexed from 0 to n-1,
where n is the number of elements in
the array.
• Arrays can be initialised using braces:

int array[3] = {1,3,5};

As demonstrated in our second lecture


arrays are a useful way to organise
variables in your program.
https://xkcd.com/163/
Using Arrays – single dimensional

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.

We have a contiguous collection of array elements, starting at zero, increasing to n-1,


all holding a single value.

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

We can see that arrays can be very useful


in helping us create compact and easy to
read code. But what if we want to store 1 0 0
something that has more than a single I= 0 1 0
dimension?
0 0 1
Fortunately C has the concept of
multidimensional arrays. Using these we
can store an entity that has any dimension.

Lets consider the 3x3 identity matrix (right).


We can store this as a 2D array which we
declare as:

int identity[3][3]

In C this would tell the compiler to create a


linear contiguous area in memory to hold
3x3 = 9 ints.

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

array[0][0]… array[1][1]… array[2][2]

array[0][m] array[1][m] array[2][m]


Using Arrays – initialising multidimensional arrays

How do we initialise these arrays in our code?

Previously we showed that a one dimensional array can be initialised as follows:

int array[3] = {1,3,5};

We can initialise a 2D array in the following way:

int identity[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).

int array[p][q][r] = {{{},{},{}},


{{},{},{}},
{{},{},{}}};
Using Arrays – using multidimensional arrays

#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

Our previous example of arrays depicted


a computers memory (RAM) as a
sequence of linear, contiguous storage
elements. We looked at how data items
are stored in each element.

But how does a computer know in which


element the data it wants to access is
stored? For example how does it know
where identity[0][0] is located?

This problem is overcome just as it would


be in the real world. Each memory
location is given a unique address. This
is a simplified view of the actual
mechanics of how memory is addressed,
however it is sufficient for our needs.
Understanding pointers – addressing memory

Lets go back to our previous


description of an integer array.
int array[n]
But now lets add an address.
I’ve chosen to add an integer
address where the beginning 5 10 15 … 11
element our array (which is
array[0]) has address 100 Address 100 101 102 … 100+(n-1)
(again this is simplified, but its
sufficient to understand array[0] array[n-1]
pointers).

Schematically this is
represented on the right.
Understanding pointers – creating a pointer

The next thing to note is that each


address is a number and so can be
treated like any other number in C.

If we know the address of


array[0] then we could create
another variable to store this
address.

Lets work through the process for


doing this over the next few slides.
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.

Hence ptr_array is a pointer to array


Understanding pointers – using pointers

To work with pointers we need to know


about two operators. These are:
#include <stdio.h>
The indirection operator *
#define PI 3.14
The address-of operator &
int main() {

To understand these operators lets return float radius = 10.0;


to our simple example of calculating the float area;
area of a circle.
float *ptr_radius;
We declare a pointer to type float by: area = PI * radius * radius;

float *ptr_radius; return(0);

The indirection operator tells the compiler }


that ptr_radius is a pointer to type
float and not a variable of type float.
Understanding pointers – using pointers

#include <stdio.h>

To initialise the pointer (set it to point #define PI 3.14


to something) we use the address-of
operator: int main() {

ptr_radius = &radius; float radius = 10.0;


float *ptr_radius;
float area;
This tells the compiler to take the
address of the variable radius and area = PI * radius * radius;
store it in the pointer ptr_radius printf(“\nArea:\t%f”, area);

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.

We also have increment: ptr_radius++;

and decrement operators: ptr_radius--;

Finally we can pass pointers as arguments to functions:

float area_of_circle(float *ptr_radius);

5 10 15 … 11

array[0] array[1] array[2] array[n-1]


*(array+0)*(array+1) *(array+2) *(array+n-1)
Understanding pointers

Pointers are (arguably) the most


difficult concept to understand in C.
However they are a powerful tool that
can be used to write versatile and
concise code. They also provide a
flexible method for data manipulation.

In “Practical examples using the C


programming language” we will look
at the uses of pointers in more detail.

https://xkcd.com/138/
Break
Characters and Strings

#include <stdio.h>
C uses the char variable to store
characters and strings. int main() {

C uses ASCII encoding to turn char one = 70;


integer numbers into characters. char two = ‘q’;
An example of this is given on printf(“\nOne as a character:\t%c”, one);
the right. printf(“\nOne as a number:\t%d”, one);

C decides whether a char holds printf(“\nTwo as a character:\t%c”, two);


a character or a number printf(“\nTwo as a number:\t%d”, two);
depending on the context of its
return(0);
use. }

https://en.wikipedia.org/wiki/ASCII
Characters and Strings
Characters and Strings

C uses arrays of char variables to


store strings.

Stings are terminated with the null #include <stdio.h>


character which is represented by
\0 int main() {

char one[5] = {‘H’,’a’,’r’,’d’};


So a string that has seven
char two[5] = “Easy”;
characters needs an array of eight
elements to store it. printf(“\nString one:\t%s”, one);
printf(“\nString two:\t%s”, two);
The code on the right gives an
example of this and demonstrates return(0);
}
two ways to initialise a character
array with a string.

Recall the conversion specifier for


a string is %s
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

Variable scope refers to the extent to #include <stdio.h>


which different parts of your C program
can “see” a variable that you declare. void print_number(void);

The concept of scope allows a float one = 3.0;


programmer to truly separate out
int main() {
(structure) their code into independent self
contained routines or functions. extern float one;

Doing this helps reduce bugs in code and print_number(void);


makes for more reusable code. For
return(0);
example if a variable can only be seen by
}
the function that is operating on it, another
function cannot mistakenly corrupt its void print_number(void) {
value.
extern float one;
In some instances it is desirable to share
printf(“\nYour number is:\t%f\n”, one);
a variable amongst the whole code. This
}
can be done with the extern keyword.
Variable scope – Local variables

External variables are sometimes called


global variables. Their scope is the whole
program, so main() and any other #include <stdio.h>
functions() that you define.
void print_number(int x);
This is opposite to local variables. A local
int main() {
variable is defined within a function. As
such its scope is within the function for(int x=0; x<3; x++) {
(remember main() is a function and so print_number(x);
we can have local variables in main()). }
return(0);
Local variables are automatic, meaning }
they are created when the function is void print_number(int x) {
called and destroyed when it exits. So an
automatic variable doesn’t retain its value static int y = 0;
in between function calls. printf(“\nx,y are:\t%d %d\n”, x, y);
y--;
To remember the value of a variable }
between function calls we can use the
static keyword.
Variable scope – Local variables
Advanced program control

#include <stdio.h>

void print_number(int x);


C provides some additional tools for
int main() {
advanced program control.
for(int x=0; x<5; x++) {
Three of the most useful are: print_number(x);
if(x == 2) break;
• break }
• continue
int x = 0;
• switch
while(1) {
if(x == 3) {
Both break and continue provide break;
additional control within loops. They are }
used within the body of a for() or print_number(x);
while() loop, additionally break can x++;
}
be used in a switch statement. return(0);
}
The switch statement takes an
argument and then executes code void print_number(int x) {
based on this. static int y = 0;
printf(“\nx,y are:\t%d %df\n”, x, y);
y--;
}
Advanced program control

#include <stdio.h>

void print_number(int x);

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.

In this lecture we’ll cover


four basic functions for this.

fopen()
fclose()
fprintf()
fscanf()
https://xkcd.com/1360/
Using files – fopen and fclose

The code on the right declares a


pointer to type FILE. Then two char #include <stdio.h>
arrays are declared to hold a file
name and a mode. int main() {

The file name is the name of the file FILE *fp;


char filename[200], mode[4];
you want to use and the path to it.
printf(“\nEnter your file: “);
We use gets() to read strings gets(filename);
from the keyboard and store these printf(“\nEnter a file mode: “);
in our char arrays. gets(mode);

if((fp=fopen(filename, mode)) != NULL) {


We then use fopen() to try and
printf(“\nOpened %s in mode %s”, filename, mode);
open the requested file. If the file } else {
opens successfully then the pointer printf(“\nERROR: File not recognised”);
fp is initialised. If not the pointer is }
set to NULL and we print an error. fclose(fp);
return(0);
}
Finally we close the file using the
fclose() statement.
Using files – fopen and fclose

Return value from fopen if the file:


Mode Meaning
Exists Doesn’t exist
r Reading – NULL

w Writing Overwrite if file exists Create new file


In the previous slide we
introduce the concept of New data is appended
opening a file in different a Append Create new file
at the end of file
modes.
New data is written at
The table on the right Reading +
r+ the beginning of the file Create new file
outlines different modes Writing
overwriting existing data
and the and the associated
return value of fopen()
Reading +
w+ Overwrite if file exists Create new file
Writing

Reading + New data is appended


a+ Create new file
Appending at the end of file
Using files – fprintf and fscanf

#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);

We use fprintf() to print 10 for(int i=0; i<10; i++) {


numbers and their squares to a file. fprintf(fp, “%d %d”, i, i*i);
Note how fprintf() works like }
printf()
fclose(fp);
fp=fopen(filename, mode);
We close the file and reopen it.
for(int i=0; i<10; i++) {
We then use fscanf() to read in our fscanf(fp, “%d %d”, &index[i], &square[i]);
outputted numbers. }
for(int i=0; i<10; i++) {
fprintf(fp, “%d %d”, i-index[i],(i*i)-squared[i]);
Finally we perform a difference of these }
and print out to screen. return(0);
}
Working with memory

Up until now all of our example codes


have allocated static memory.
By this we mean that when we write our
code we declare how much memory we
need for particular variables or arrays.

However there might be instances


where we don’t know how much
memory we need until we run the code.
For example we might want to read a
file into our code that is updated on a
daily basis, the files length might be
different on different days.

C provides a process for allocating


memory at runtime. This is called
dynamic memory allocation.

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));

We then use scanf() to get the number printf(“\nEnter the radii:\n”);


of circles the user wants to work with. for(i=0; i<number_of_circles; i++) scanf(“%f”, &radius[i]);

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()

Lets take a closer look at malloc()

Both malloc()and free() are defined


in stdlib.h, so this must be included
into your code to use them. memory
The function prototype for malloc() is:

void *malloc(size_t num);

size_t is an unsigned integral type and


malloc(size)
is used to represent the size of an object
in bytes. The return type of the
sizeof() operator is size_t allocation
malloc() will return NULL if num bytes
cannot be allocated (for example the
computer doesn’t have enough memory
space left)
Working with memory – free()

Once we’ve finished working with the


memory that we’ve allocated using
malloc() we should free it so that it
can be used again.

This is done using the free() function.


Its function prototype is:

void free(void *ptr);

Calling free() releases the memory


that is pointed to by ptr
Working with memory – multidimensional arrays

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()? }

The code snippet on the right shows // To free


how to do this and then free the for(int i=0; i<num_rows; i++) {
allocated memory using free() free(identity[i]);
}
free(identity);
Working with memory – multidimensional arrays

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]

We then point each of these


at a single dimensional array
of type int.
identity[0][1] identity[1][1] identity[2][1]
int
identity[0][2] identity[1][2] identity[2][2]
What have we learnt?

In this lecture you have learnt about


some of the advanced features of the C
programing language.

We have covered multidimensional


arrays, pointers, characters and strings.

You have learnt about variable scope,


some of the functions that provide
advanced program control and how to
work with files.

Finally we have covered the basics of


dynamic memory allocation.

You should now be in a position to write


your own advanced C program.
Further reading

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

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy