You are on page 1of 232

ENGGEN 131

Introduction to Engineering Computation and Software Development

Course Manual
C Programming Module
Second Semester 2013

Department of Computer Science

ENGGEN 131 SC 2013

This coursebook belongs to:

Name:

ID:

Email:

Lab Session Checklist Lab Lab 7 Tutor's signature (upon completion of lab tasks)

Lab 8

Lab 9

Lab 10

Lab 11

Lab 12

EngGen 131 S2 2013

ENGGEN 131 C Programming


Second Semester
2013

Contents

Page Course Outline Lecture 13: An Introduction to C Lecture 14: Types, Operators and Standard I/O Lecture 15: Conditionals, Loops and 1D arrays Lecture 16: 2D arrays, Library Functions and Structures Lecture 17: File I/O and Random Numbers Lecture 18: Functions Lecture 19: Pointers and Strings (Part 1) Lecture 20: Project Discussion Lecture 21: Pointers And Strings (Part 2) Lecture 22: Recursion Lecture 23: Software Engineering Overview Lecture 24: Exam Revision 1 7 15 31 51 67 81 99 115 117 129 141

Lab 7: An Introduction to Visual Studio Lab 8: Conditionals, Loops, Arrays and Structures Lab 9: File I/O and Functions Lab 10: Pointers and Strings Lab 11: Project and Strings Lab 12: Recursion and Project Reviews

143 159 175 193 207 221

Course Manual (C Programming)

EngGen 131 S2 2013

Feedback Any and all feedback on this coursebook is welcome. Please send all corrections, comments and other feedback to paul@cs.auckland.ac.nz Thank you!

Paul Denny September, 2013

Course Manual (C Programming)

EngGen 131 S2 2013

ENGGEN 131 C Programming: Course Outline 2013

Contact
Paul Denny Room: Email: Phone: 465, Computer Science Building (Building 303 S) paul@cs.auckland.ac.nz 3737599 xtn. 87087

Content
Welcome to the second half of the ENGGEN 131 course for 2013. Its great to have you with us! The numbering of the Lectures and Labs in this course book is a continuation from the MATLAB coursebook (we begin with Lab 7 and Lecture 13). This part of the course is an introduction to the C programming language. It will be assumed that you are familiar with the principles of programming covered in the first section of the course using MATLAB.

Resources
Cecil All of the material for this section of the course is available on Cecil: http://cecil.auckland.ac.nz Forums We are using Piazza as a class forum this semester. This is the best place for you to ask questions and discuss the course content: https://piazza.com CloudCoder You will be using CloudCoder to practice writing short fragments of C code (practice makes perfect!) https://auckland.cloudcoder.org/cloudcoder
Course Outline

Course Manual (C Programming)

EngGen 131 S2 2013

Arop the peer review system The Arop peer review system is available at: https://aropa.ec.auckland.ac.nz You will use Arop to submit your project and to review the projects of your peers. Programming environments In the labs, we will be using Microsoft Visual Studio Professional Edition. At home, you may use any ANSI C compiler you like, but the following environments are recommended: Windows: Microsoft provides Express versions of Visual Studio for free. These will provide you with an environment similar to what you have in the labs. If you would like to download one of these versions, try not to be overwhelmed by all the different varieties. There really are just two versions you can choose from that meet the requirements of this course, depending on whether or not you are running Windows 7 or later: Visual Studio Express 2012 for Windows Desktop This is the most recent Express version, but make sure you get the version for Windows Desktop. This version supports multiple languages (including C, which is what we are using) and has a more modern look and feel than the 2010 version. However, you will need to be running either Windows 7 or Windows 8 to install this version. Visual C++ 2010 Express This version is perfectly fine for this course. Also, this is your only choice if you are running a version of Windows prior to 7. Use the following download link to obtain either version (of course you can search for them as well, but you may have to look a little harder to find the 2010 version): http://www.microsoft.com/visualstudio/eng/downloads

Linux: GNU Compiler Collection GCC comes standard with all major Linux distributions http://gcc.gnu.org/ Mac: XCode Apple provides support for GCC in the standard XCode development environment http://www.apple.com/macosx/developers/

Course Outline

Course Manual (C Programming)

EngGen 131 S2 2013

Timetable
The following timetable gives an overview of the final 6 weeks of the EngGen 131 course. Week Lecture 1
(Monday)

Lecture 2
(Tuesday)

Lab
(Tuesday - Friday)

Lecture 3
(Friday)

Week 7 (16 20th Sept)


th

Lecture 13 An Introduction to C

Lecture 14 Types, Operators and Standard I/O

Lab 7 An Introduction to Visual Studio

Discussion and Exercises

Week 8 rd (23 27th Sept)

Lecture 15 Conditionals, Loops and 1D arrays

Lecture 16 2D Arrays, Library functions and Structures

Lab 8 Conditionals, Loops, Arrays and Structures

Discussion and Exercises

Lecture 17

Week 9 File I/O and Random (30th Sept 4th Oct) Numbers

Lecture 18 Functions

Lab 9 File I/O and Functions

Discussion and Exercises

Week 10 (7th 11th Oct)


CloudCoder
Sun 6th Oct, 10:00pm

Lecture 19 Pointers and Strings (Part 1)

Lecture 20 Project Discussion

Lab 10 Pointers and Strings

Discussion and Exercises TEST (in class)

Week 11 (14th 18th Oct)

Lecture 21 Pointers and Strings (Part 2)

Lecture 22 Recursion

Lab 11 Project and Strings

Discussion and Exercises

Week 12 (21 25th Oct)


st

Project Due
Sun 20th Oct, 10:00pm

Lecture 23 Software Engineering Overview

Lecture 24 Revision

Lab 12 Recursion and Project Reviews

Discussion and Exercises Peer review


Friday 25th Oct, 10:00pm

Course Outline

Course Manual (C Programming)

EngGen 131 S2 2013

Assessment
The assessment for this section of the course (contributing 50% towards your final grade) consists of the following: Labs (worth 6%) There are 6 lab sessions supporting the material taught in this section of the course, worth a total of 6%. CloudCoder (worth 2%) We will be using a tool called CloudCoder to give you practice writing short fragments of code. The goal here is to focus on simple exercises first to increase your confidence and familiarity with C syntax, before moving on to more complex problems. Completing the exercises on CloudCoder will contribute 2% towards your final grade. More information about CloudCoder, and a link to it from Cecil, will be available once the module begins. Project (worth 10%) and Peer Review (worth 2%) The project for this section of the course contributes 10% towards your final grade. In order to get credit for this assessment, you need to complete a peer review (contributing 2% towards your final grade) which involves reviewing the code produced by other students in the class. This peer review will begin during Lab 12 (from 22nd 25th October) and will use Arop. You must finish the peer review by 10:00pm Friday 25th October. You must submit the project via Arop before 10:00pm, Sunday 20th October. Test (worth 5%) The test for this section of the course will be held on Friday 11th October in the lecture time. Please attend your scheduled lecture time on this day. The test will consist of multiple-choice questions and will contribute 5% to your final grade. Exam (worth 25%) The final exam contributes 50% to your final grade, and 50% of the exam will cover this section of the course, with an emphasis on the material in the labs and the project. The exam for the C programming section of the course will be multiple-choice.

Course Outline

Course Manual (C Programming)

EngGen 131 S2 2013

Function Summary
The following list summarises some of the ANSI C functions that you may find useful, and the corresponding header files.

<stdio.h> int printf(char *format, ...); int scanf(char *format, ...); char *gets(char *s); int putchar(int c); int getchar(void); FILE *fopen(char *filename, char *mode); int fscanf(FILE *stream, char *format, ...); int fgetc(FILE *stream); int fprintf(FILE *stream, char *format, ...); int fclose(FILE *stream);

<stdlib.h> int rand(void); void srand(unsigned int seed); void *malloc(unsigned long size); void free(void *p);

<string.h> char *strcpy(char *s, char *t); int strcmp(const char *s, char *t); unsigned long strlen(char *s); char *strcat(char *s, char *t);

<math.h> double sqrt(double x); double pow(double x, double y); double sin(double x);

<ctype.h> int toupper(int int tolower(int int isdigit(int int isalpha(int

c); c); c); c);

Course Outline

Course Manual (C Programming)

EngGen 131 S2 2013

Style Summary
The following are recommended guidelines for the programming style that you should follow for this course. You are welcome to use other styles if you wish, as long as you are consistent. Variable identifiers The first letter should be lower case, and the first letter of subsequent words should be upper case: int numberOfStudents; double taxRate; Function identifiers Identifiers for functions from the standard library are entirely in lower case. For functions that we define, the first letter should be upper case, as should the first letter of subsequent words: int SumArray(int values[]) Braces for functions The opening brace should be on a line by itself, immediately below the line which defines the functions return type, name and parameters. The closing brace should be on a line by itself and line up with opening brace: int main(void) { <statements...> } Braces for other blocks of code The opening brace should be on the same line as the statement which begins the block. The closing brace should be on a line by itself, lined up with the first character of the statement which begins the block: if (i < 10) { <statements...> } Indentation All statements between an opening and closing brace should be indented by one level using a tab character: int main(void) { int a = 10; if (a < 10) { printf("ok\n"); } return 0; }

Course Outline

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 13: An Introduction to C


By now, you already know how to program. The language that was used to introduce you to the principles of programming was MATLAB, which provides excellent support for matrix manipulation and the plotting of functions and data. You have learnt how to use variables, and how to write programs that implement algorithms requiring conditionals, loops and arrays. You have used many of the powerful functions that come standard with the MATLAB library, and you have defined your own functions to decompose a program into simple pieces. Now, we are going to learn another language the C programming language. Many of the ideas will be similar, particularly those involving basic arithmetic operators and control flow (loops, conditionals, and the mechanics of function calling), however there are a number of differences; some major and others quite subtle.

MATLAB vs. C
Your first question might be "why are we learning a new language?" Depending on the problem you are trying to solve, it makes sense to use the most appropriate tool. There are some things that we can do more effectively using MATLAB, and there are other things that we can do more effectively using C. For many simple programs, it doesn't make much difference which language we use. A few comparisons between the two languages are made below. Speed Programs written in C typically execute much more quickly than programs written in MATLAB. The reason for this is that C is a compiled language, whereas MATLAB is an interpreted language. C is a compiled language. In order to run a C program, the source code (that we write) is first entirely converted by a program called a compiler into binary code. This binary code is targeted specifically for the processor and operating system on which the program is to be run. The main advantage of using a compiled language is the performance of the resulting program. MATLAB is an interpreted language. When a MATLAB program executes, each instruction in the source code (that we write) is translated to an appropriate set of binary instructions which are then executed on the processor. This translation step takes place for each instruction as the program is running, slowing down the execution. One advantage of interpreting the code is that we can run our programs as soon as we have written them without requiring the compilation process. Another advantage is that if there are any errors in the program, we can be given very useful, specific feedback about where in the source code the error exists.

Lecture 13

Course Manual (C Programming)

EngGen 131 S2 2013

Syntax There are a number of subtle differences between the syntax of C and the syntax of MATLAB which you will pick up as you learn the language. Some of the differences are: MATLAB denoted by a starting keyword (such as if or for) followed by the ending keyword, end start at 1, and elements are accessed using parentheses, () ~, |, & a variable consists of just a name, and can be used to store any kind of data built-in support for matrices, vectors, and operations on them M-files are either scripts or functions. The name of a function file defines the existence of a function. C denoted by curly braces, {}

code blocks

array indexes logical operators types

matrix operations file organisation

start at 0, and elements are accessed using square brackets, [] !, ||, && a variable consists of a name and a type, and can be used to store only that type of data the compiler ensures that this is not violated no built-in support for matrices A source file is simply a collection of functions. The definition of a function in the source code defines the existence of the function.

Licensing MATLAB is a proprietary product of The MathWorks, and can only be used by purchasing an appropriate license. On the other hand, there are many freely available compilers and environments for developing C programs.

History C was first developed in 1972 and has influenced many modern languages including C++, C# and Java (in particular, the syntax of these languages is similar to C). MATLAB was initially developed using Fortran in the late 1970s. In the early 1980s, MATLAB was rewritten in the C programming language, and in 1984 The MathWorks was founded.

So, MATLAB or C? MATLAB is perfectly suited to solving numerical problems, particularly involving matrix or vector manipulation. Solutions to these kinds of problems can be developed rapidly in MATLAB, as it provides direct support for matrices and operations on matrices. MATLAB also provides many library functions for producing graphical output, so it is perfect for problems where solutions need to be visualised in the form of graphs or plots. C is suited to problems where speed is critical, as programs written in C can execute very quickly. C is also suitable when it is necessary to have low-level access to computer memory via machine addresses, particularly for embedded systems. Also, if it is necessary to have fine control over defining and manipulating data structures, C is an excellent choice.

Lecture 13

Course Manual (C Programming)

EngGen 131 S2 2013

ANSI C
In 1978, Dennis Ritchie and Brian Kernighan published a book, called "The C Programming Language" which described the features of the language. Although not officially a language specification, this book was used informally as a definition of what features a compiler should support. Compilers were initially developed for many different systems, but often each compiler would provide special features that suited the specific system the compiler was designed for. This meant that any code making use of these special features could not be compiled on another system. However, the ability to easily port code from one system to another is very desirable, and so it became necessary to develop a standard specification for the language, defining all the features that must be supported by a C compiler. In 1989, the American National Standards Institute (ANSI) completed a specification for the language known as ANSI C. With this specification, anyone writing source code adhering to the ANSI C standard could be sure that the code would compile correctly with any compiler that supported the ANSI C standard. In this course, we will be writing code that adheres to the ANSI C standard. ANSI C is supported by almost all of the widely used C compilers.

A first C program
Here is a very short, ANSI compliant, C program which prints out a welcome message. This is a good starting point for some of the simpler programs you will develop in this course: #include <stdio.h> /* This program displays a welcome message */ int main(void) { printf("hello world\n"); return 0; }

Comments
In MATLAB, to comment a line you would use the % symbol. This meant that all text to the right of the symbol was ignored by the MATLAB interpreter. For example, in the following MATLAB code: % set up constants g = 9.81; length = 1.0; k = g/length;

% acceleration due to gravity % length of the pendulum

all of the text to the right of the % signs is ignored when the program runs. The purpose of including comments is to help anyone that tries to read our code.

Lecture 13

Course Manual (C Programming)

EngGen 131 S2 2013

In C, comments have not only a beginning symbol (/*), but also a terminating symbol (*/). Anything between these pair of symbols is treated as a comment and ignored by the compiler. Another language, C++, introduced single line comments where everything to the right of the symbol // is ignored by the compiler. These are very similar to the MATLAB style comments. For example: #include <stdio.h> int main(void) { printf("hello world\n"); return 0; }

// prints welcome message

Although single line comments are not part of the standard ANSI C specification, they are supported by many C compilers because of their usefulness.

C and C++ are different languages. In this course, we are learning C.

The C programming language was first developed in 1972 by Dennis Ritchie and Brian Kernighan, whereas C++ was developed in 1978 by Bjarne Stroustrup. C++ is an extension of C (in fact, most C++ compilers will compile C code) that adds a number of features the most important of which is object orientation. Controlling complexity is the essence of computer programming. - Brian Kernighan

Lecture 13

10

Course Manual (C Programming)

EngGen 131 S2 2013

Compiling a C program
A C source file is simply a text file, and can be written using any text editor. C source files are given the suffix ".c". For example, consider the following program saved in a file called hello.c: File: hello.c #include <stdio.h> /* This program displays a welcome message */ int main(void) { printf("hello world\n"); return 0; }

To run this program, the first thing we have to do is compile it. Once the compiler has finished, it should produce an executable file (such as hello.exe) which can be run directly. The detailed process of what happens when you compile a C program is quite complicated, and for the most part we don't need to worry about all of the steps. The diagram below summarises this compilation process:

C source file

hello.c

Preprocessor

Compiler

assembly code

Assembler

object code

Standard C Library

Linker

Executable hello.exe file

Lecture 13

11

Course Manual (C Programming)

EngGen 131 S2 2013

The Preprocessor The C preprocessor is the first step in the process. It takes a source file as input, and prepares it for the actual compiler. This preparation involves: removing all the comments from the source code (the compiler doesn't need to see the comments, which are there only for the benefit of someone reading our code) interpreting any special preprocessor directives in the source code Preprocessor directives are denoted by the # symbol followed by a command. The two most common preprocessor directives are: #include This preprocessor directive is followed by the name of a file (which should be a header file with a .h suffix). This directive is very simple the preprocessor simply inserts all of the text from the header file into the source file. For example: #include <stdio.h> inserts the contents of the file stdio.h into the source file. #define This preprocessor directive is followed by two expressions. This directive does a very basic text search and replace it replaces all occurrences of the first expression in the source file by the second expression. For example: #define MAX_STUDENTS 500 replaces all occurrences of the text MAX_STUDENTS in the source file by the text 500. The Compiler The C compiler takes the source file that the preprocessor has prepared, and converts it to assembly code. Assembly code is a low level, yet still human-readable language, where each assembly code instruction corresponds to a particular machine language instruction. The machine language instructions are what the computer actually understands they are simply patterns of bits that make sense only to a particular type of processor and operating system. The Assembler The assembler takes the assembly code from the compiler and translates it to machine language, also called object code. On a Windows machine, object code files have a .obj suffix. The Linker The final step in the process is where the linker takes all of the object code files produced by the assembler, and combines them with each other (if the source code in one file has called a function in another file) and with the object files that store the standard library functions (if the source code has called any of the standard library functions) to form a single executable file. The Result The result of this process is an executable file that contains all the necessary instructions to run the program. On a Windows machine, executable files have a .exe suffix. It is very common to refer to this entire process (preprocessing, compiling, assembling and linking) informally as simply "compiling".

Lecture 13

12

Course Manual (C Programming)

EngGen 131 S2 2013

Running a C program
Once we have compiled our code, what happens when we run the executable? The main() function Every C program must have a function called main() defined. According to the ANSI standard, there are only two valid ways that the main() function can be defined, and in this course we will always be using the following version which does not take any parameters: int main(void) This function is the entry point for the program. When we launch the executable, the main() function is entered, and every statement in the main function is executed, in order, from top to bottom.

program begins int main(void) { statements printf("hello world\n"); executed return 0; } program exits When the statement: return 0; is reached, the program exits, and the value 0 (which corresponds to a successful program execution) is returned to the operating system. The printf() function To be useful, a program must produce some kind of output. The simplest way of producing output is to display some text on the screen. The printf() function can be used to produce text output whatever text appears in the quotation marks will be printed out. For example, when the program shown above is executed, the output would simply be: hello world

Lecture 13

13

Course Manual (C Programming)

EngGen 131 S2 2013

Syntax errors
A syntax error is an error that occurs when you make a mistake typing the characters that make up your source code. Syntax errors are very common they happen all the time. Dont worry if your code has syntax errors the most important thing is that when you have such errors you are able to locate them and correct them. Fortunately, the compiler picks up syntax errors and will issue an error message that includes the line of code on which the error has occurred. Unfortunately, sometimes the error messages can be quite cryptic and difficult to understand. Dont panic have a look at the line of code and try your best to understand the message. Lets look at a couple of examples: #include <stdio.h> int main(void) { printf("hello world\n") return 0; } #include <stdio.h> int main(void) { print("hello world\n"); return 0; } Syntax error: the semi-colon that is required at the end of the line with the printf() is missing. Compiler message: hello.c(6) : error C2143: syntax error : missing ';' before 'return'

Syntax error: the f is missing from the printf function there is no such function as print. Note: in fact, this error is picked up by the linker when it tries to link your compiled code with the standard library (and discovers that print doesnt exist). Compiler message: hello.obj : error LNK2019: unresolved external symbol _print referenced in function _main hello.exe : fatal error LNK1120: 1 unresolved externals

#include <stdio.h> int main(void) { printf('hello world\n'); return 0; }

Syntax error: strings must be surrounded by double quotation marks, not single quotation marks Compiler message: hello.c(5) : error C2015: too many characters in constant

With experience, you will get used to syntax errors and will be able to correct them quickly. Be patient!

Lecture 13

14

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 14: Types, Operators and Standard I/O


To write any useful kind of program, we need to be able to store, process and retrieve data. We also need to be able to receive input, and produce output.

Learning outcomes
After this lecture, you should be able to: declare a variable of a specific type, and be able to store a value in that variable evaluate simple equations consisting of the arithmetic operators and parentheses cast an expression of one type to another type use the printf() function, and appropriate conversion specifiers, to display text, characters and numbers use the scanf() function to read characters and numbers typed by the user at the keyboard

MATLAB Comparison
We have already seen that in MATLAB, we use variables to store our data. When we create a variable in MATLAB, we don't need to consider whether it is going to be used to store a 2D array, an integer, a floating point number or even a string. All we need to do is think of a name for our variable and then we can store anything we like in it using the assignment operator.
MATLAB

>> >> >> >>

A B C D

= = = =

[1,2,3;4,5,6;7,8,9]; 100; (F-32)*5/9; 'the quick brown fox';

In C, however, when we want to use a variable, we not only need to give the variable a name, but we must also explicitly indicate what kind of data we want the variable to store. The kind of data that a variable can store is defined by the type of the variable. The basic arithmetic operators are very similar in MATLAB and in C. MATLAB provides the disp() and input() functions for displaying output and getting input from the user respectively.
MATLAB

>> A = input('Enter a: '); >> disp('Hello');

In C, the corresponding functions are printf() and scanf().


Lecture 14

15

Course Manual (C Programming)

EngGen 131 S2 2013

Introduction
Let's start with the simple problem of converting a given duration (specified in a total number of seconds) into an equivalent number of minutes and seconds. For example, if the duration is 125 seconds, then that is equivalent to 2 minutes and 5 seconds. Let's say we want to write a program in C which solves this problem. We need to know how to get input from the user, how to perform some sort of simple arithmetic, and how to display output back to the screen.

125 seconds

C Program
input output

2 minutes and 5 seconds

The algorithm for this problem is pretty straightforward and is illustrated in the flow chart to the left. The arithmetic we want to perform is division. We want to divide the input value by 60. In the output, the number of minutes will be the "whole part" of this division and the number of seconds will be the "remainder", or what is left over. When we read the input value from the user (which in the example above was 125), we will want to store this somewhere. Just like in MATLAB, we will store this value in a variable. However, in MATLAB, you can store any kind of value in a variable. In C, we can only store certain types of values in certain types of variables.

In this particular example, all of the numbers we are working with are whole numbers we are not dealing with any numbers that consist of a fractional part. Before we can use a variable to store a value, we first must specify what type of variable we want. Fortunately, the choice is not too complicated - there are four basic types to choose from. The next few pages introduce the basic types, and explain how to work with variables. The arithmetic operators are also introduced, and we will see how to get input and display output. The completed program for the time conversion example (which includes a conversion to hours, as well as minutes and seconds) is given in the summary program section at the end of this lecture.

Lecture 14

16

Course Manual (C Programming)

EngGen 131 S2 2013

The basic types


There are only 4 basic types of variables in C: type char int float double kind of information stored can store an ASCII character can store an integer value (whole number) can store a floating point number (with single precision) can store a floating point number (with double precision)

We should keep in mind: the set of values that each type can represent is finite, and is determined by the fixed amount of storage allocated for that type (for example, the int type cannot represent all of the integers) when we want to use a variable, we need to pick the correct type for it by keeping in mind what values we need the variable to store Type modifiers In addition to these basic types, which are sometimes referred to as the primitive types, a few modifiers exist which can be applied to effectively create a few more types. The integer type (int) can be modified with either short or long, to give: short int long int and the unsigned modifier can be applied to char and any of the integer types: unsigned unsigned unsigned unsigned char short int int long int

The unsigned modifier indicates that we only want to store positive values in the variable, and therefore doubles the range of values that can be represented. The type short int is usually abbreviated to just short, and long int is usually abbreviated to long.

http://xkcd.com/
Lecture 14

17

Course Manual (C Programming)

EngGen 131 S2 2013

Variable sizes The type information tells the compiler how much space in memory to set aside for storing the variable. The ANSI standard defines the minimum number of bytes that are guaranteed to be used to store each type, but in practice, the actual number of bytes used for each type is dependent on the compiler and the operating system. The following table gives typical numbers, as well as the range of values that can be stored: type unsigned char unsigned short unsigned int unsigned long float double char short int long typical size (in bytes) 1 1 2 2 4 4 4 4 4 8 range of values that can be stored 0 to 255 -128 to 127 0 to 65535 -32,768 to 32,767 0 to 4,294,967,295 -2,147,483,648 to 2,147,483,647 0 to 4,294,967,295 -2,147,483,648 to 2,147,483,647 1.175494e-38 to 3.402823e+38 2.225074e-308 to 1.797693e+308

In general, the most common types that we will use are char, int and double. char

1 byte

int

4 bytes

double

8 bytes

Declaring a variable
We must declare variables before we can use them. A declaration tells the compiler what type we want the variable to be, and what name we want the variable to have (called an identifier), followed by a semi-colon: <type> <identifier>; For example, the following statements declare three variables: char firstInitial; int numberOfStudents; double exchangeRate;

Lecture 14

18

Course Manual (C Programming)

EngGen 131 S2 2013

The relative sizes of these variables could be visualised accurately as follows:

firstInitial

1 byte

numberOfStudents

4 bytes

exchangeRate

8 bytes

although in practice we tend not to visualise the physical sizes of the variables. Instead, we think of every variable as a box of the same size but recognise that they have the capability of storing different ranges of values. Therefore, we would visualise these three variables as follows:

firstInitial

numberOfStudents

exchangeRate

Identifier names In the above example, we chose the identifier names so that they were meaningful and gave an idea of what information the variables were going to store. It is a good idea to give meaningful names to our identifiers, because it makes it easier for other people to understand our code. Also notice that each identifier name started with a lower case letter, and the first letter of each subsequent word in the name was in upper case. This is quite a common style for naming identifiers. Identifier names are case-sensitive, so be aware that the compiler would regard number, Number and NUMBER as distinct identifier names. Where to declare We cannot declare variables just anywhere. For example, we cannot declare a variable in the middle of a block of statements. Variables must be declared at the beginning of a block of statements, such as a function definition, as illustrated below: int main(void) { <variable declarations> <statements> }
Lecture 14

19

Course Manual (C Programming)

EngGen 131 S2 2013

Constants
A constant is a value that appears in the source code for our program. There are different kinds of constants corresponding to the different variable types. Some examples are shown in the table below: character constants 'A' 'b' '\n' '\\' '\t' '\0' -45 260 3.1416 -15.0 1.6e2 "apple" "A" the upper case 'A' character the lower case 'b' character the newline character the backslash character the tab character the "null" character (totally different to '0') the numeric value "-45" the numeric value "260" can have multiple digits in the fraction can be positive or negative the numeric value 160.0 a string consisting of 5 characters a 1 character string (totally different to 'A')

integer constants floating point constants

string constants

Numbers in the source code, like 26, are of type int by default. Numbers in the source code, like 5.3, are of type double by default. A variable of type char actually stores the ASCII code (which is an integer between 0 and 127) of the character that it represents. When the compiler sees a character constant, like 'A', it converts that into the corresponding ASCII code of 65. From our point of view, it is much easier for us to let the compiler do this for us, rather than looking up the ASCII codes manually from a table:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Lecture 14

(nul) (soh) (stx) (etx) (eot) (enq) (ack) (bel) (bs) (ht) (nl) (vt) (np) (cr) (so) (si)

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

(dle) (dc1) (dc2) (dc3) (dc4) (nak) (syn) (etb) (can) (em) (sub) (esc) (fs) (gs) (rs) (us)

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

(sp) ! " # $ % & ' ( ) * + , . /

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

0 1 2 3 4 5 6 7 8 9 : ; < = > ? 20

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

@ A B C D E F G H I J K L M N O

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

P Q R S T U V W X Y Z [ \ ] ^ _

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

` a b c d e f g h i j k l m n o

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

p q r s t u v w x y z { | } ~ (del)

Course Manual (C Programming)

EngGen 131 S2 2013

Assignment operator (=)


The assignment operator in C is the '=' character. To store a value in a variable that has been declared, we specify the name of the variable on the left, and the value to store in it on the right: char firstInitial; int numberOfStudents; double exchangeRate; firstInitial = 'X'; numberOfStudents = 430; exchangeRate = 0.672; The type of the value on the right of the assignment operator should match the type of the variable on the left (that the value is being assigned to). It is an excellent idea to get into the habit of visualising variables in memory. Each variable is drawn as a box with the current value stored by the variable drawn inside the box:

firstInitial

'X'

numberOfStudents

430

exchangeRate 0.672

Although this may seem trivial now, drawing diagrams of memory is a simple but effective tool when we inevitably have to locate a bug in our program. Sometimes the contents of memory can be quite complex, and being able to visualise it clearly can make a huge difference in how long it takes us to find errors in our code. So this is a good habit to get into early. We dont need to declare and initialise a variable in separate statements it is very common to initialise a variable at the same time that we declare it:

int size = 100;

size

100

Lecture 14

21

Course Manual (C Programming)

EngGen 131 S2 2013

Lets briefly discuss how our computers memory is organised while our C program is running. When you execute your program (for example, when you run hello.exe), the operating system allocates memory for the program to use. The diagram below shows a very simplistic view of the memory allocated for our program.

Instructions The memory for our running program includes the actual binary instructions that were produced by the compiler. As the CPU executes these instructions one at a time, we say the program runs. Data Any variables that we declare outside of a function definition are called global variables. For now, we will only be declaring variables inside of function definitions these are called local variables. When we come back to look at global variables later, it will be useful for us to know that they are stored in a separate section of the memory for our running program. The Heap When a program is running, it is possible for it to request memory to use. This is known as dynamic memory allocation. In C, there are special functions such as malloc()for requesting memory. Memory allocated by these requests is available in an area known as the heap, which obviously grows every time the program requests new memory. In this course, we are not going to be dynamically allocating memory. The Stack Whenever we call a function, some memory is allocated for storing information about the function. This memory is placed on the stack. When a function finishes executing, the memory it was using on the stack is returned thus, the stack grows and shrinks as the program runs and functions begin and end. The memory allocated on the stack when a function is called is known as a stack frame. A stack frame stores the values of any variables defined inside the function definition.
Lecture 14

22

Course Manual (C Programming)

EngGen 131 S2 2013

The previous description was a little abstract. Lets now consider the following complete C program: #include <stdio.h> int main(void) { char firstInitial; int numberOfStudents; double exchangeRate; firstInitial = 'X'; numberOfStudents = 430; exchangeRate = 0.672; return 0; } We will visualise the memory allocated for this running program as follows:

NOTE: We are not going to visualise the Instructions. They are binary codes and wouldnt make any sense to us anyway. We also are not going to visualise The Heap at least not in this course, because we will not be allocating memory dynamically. There will be plenty of time to learn this later, lets understand the fundamentals really well first. In the diagram to the left, there is nothing in the Data area. That is because there are no global variables declared. We will learn about global variables later in the course. What we can see is that the main() function has been called (it appears on the stack). We know that the main() function is always called when our program starts. We visualise this call to the main() function like a box, labelled with main(), and inside the box are the variables declared inside the function definition. In this case, we see the variables firstInitial, numberOfStudents, and exchangeRate.

Lecture 14

23

Course Manual (C Programming)

EngGen 131 S2 2013

Assignment expressions The assignment operator also acts like an expression the expression evaluates to whatever value was assigned to the variable. We sometimes see this value being used as part of a larger expression, such as: a = b = 0; which is equivalent to a = (b = 0); This assigns the value 0 to b, and then assigns the result of that expression, 0, to a as well.

Arithmetic operators
The following basic mathematical operators are available: + * / % addition subtraction multiplication division (the result is an integer if both operands are integers) modulus (or remainder applies only to integers)

The operators apply to operands of the same type. The result is the same type as the operands.

Type combination If two different types are combined in an arithmetic expression, the compiler will automatically promote the smaller type to the larger type. An ordering of the basic types, from largest to smallest, is summarised below: largest: double float long int short char

smallest:

For example, consider the expression: 12 + 5.5 Here, the smaller type is the int type (the constant 12) which will be promoted to double before the addition is performed (i.e. it will be promoted to 12.0). The expression will therefore evaluate to 17.5. No information is lost in a promotion, because a smaller type is always promoted to a larger type.
Lecture 14

24

Course Manual (C Programming)

EngGen 131 S2 2013

Casting So what would happen in the following expression? int a; a = 12 + 5.5; In this case, the right hand side of the assignment evaluates to 17.5 (as the 12 is promoted to the double value 12.0 before the addition takes place). However, a double value cannot be assigned to an int variable because the fractional part of the number can not be stored. The compiler will issue a warning about this, such as: warning '=' : conversion from 'double' to 'int', possible loss of data

There are some situations where we would like to force a conversion from one type to another (such as from a larger type to a smaller type). We tell the compiler that we want the conversion to take place by using a cast. The general form of a cast is below, where <expression> can be any expression, and <type> is the type to convert the expression into: (type)<expression> For example: int a; a = (int)(12 + 5.5); would now compile without warning, and the value 17 would be assigned to a. A cast from type double to type int simply truncates the fractional part of the number no rounding is performed. Unary operators The unary ++ and -- operators increment or decrement the value in a variable. The statement: a++; has the same effect as the explicit arithmetic and assignment: a = a + 1; For example, the statements: int a = 10; int b = 10; a++; b--; printf("%d %d", a, b); would produce the output: 11 9
Lecture 14

25

Course Manual (C Programming)

EngGen 131 S2 2013

Special assignment operators For each of the mathematical operators, there is a corresponding shorthand assignment operator. For example, to add 15 to the value of variable a, we could either use: a = a + 15; or we could use the shorthand operator: a += 15; The shorthand assignment operators are: +=, -=, *=, /=, %= Operator precedence The precedence of the operators is summarised in the table below: () ++ * + = += -= / *= /= -% increasing order of precedence

For example, the statement: int a = 1 + 2 * 3 - (4 - 5); would assign the value 8 to the variable a, as the operators would be evaluated in the following order: int a = 1 + 2 * 3 - (4 - 5);

int a = 1 + 2 * 3 - -1; when an expression consists of multiple arithmetic operators of the same precedence, evaluation takes place from left to right

int a = 1 + 6 - -1;

int a = 7 - -1; int a = 8;

Lecture 14

26

Course Manual (C Programming)

EngGen 131 S2 2013

Standard Input and Output


To produce output to the screen, we use the printf() function, and to read input from the keyboard we can use the scanf() function. In order to use either of these functions, we need to have the following preprocessor directive at the top of our source file: #include <stdio.h>

printf() Formatted output is produced using the printf() function. printf(<format string>, <values>, ...); The format string contains the text to be printed out, as well as conversion specifiers which correspond to additional values that are going to be printed. Each conversion specifier has the following general form: %[flags][width][.prec]type where flags width .prec type : left justify the output + : display the sign of number minimum size of the output field the number of digits to display the type of value these attributes are optional

and where the most common values for type are: %d %u %f %s %c signed integer unsigned integer floating point number (float or double) string character

For example: int i = -75; double d = 123.456; printf("Value: %d %.2f", i, d); will print: Value: -75 123.46 We must make sure that the type of each conversion specifier matches the type of the corresponding value to be displayed. If the types do not match, the output will not be correct.

Lecture 14

27

Course Manual (C Programming)

EngGen 131 S2 2013

scanf() Input from the keyboard can be obtained using the scanf() function. scanf(<format string>, <address>, ...); As with printf(), the format string contains conversion specifiers which correspond to each of the values that are going to be input. These specifiers indicate the type of value that is to be read, with common values being: %d %u %f %lf %s %c signed integer unsigned integer float double string character note, double and float have different specifiers

The scanf() function will read values typed in at the keyboard and store them in the variables that are specified as arguments to scanf(). In order for this to work however, the scanf() function needs to know the address of each variable. This information is obtained using the address operator, &, which appears immediately to the left of the variable name. For example, consider the code: int i; double d; scanf("%d %lf", &i, &d); if the user types the following at the keyboard when the program is run: 55 6.2 then the variable i will store 55 and the variable d will store 6.2. We must make sure that the type of each conversion specifier matches the type of the corresponding variable being initialised. If the types do not match, the variable will be initialised incorrectly. Visual Studio and scanf() If we call the scanf() function in the Visual Studio environment, we get a warning message from the compiler similar to the following: warning C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. Although scanf() is an ANSI C function, it is sometimes considered to be unsafe as it is known to be vulnerable to certain kinds of security attacks known as buffer overflows. The programs that we write in this course will not be susceptible to these kinds of problems, so it is perfectly safe for us to use.

Lecture 14

28

Course Manual (C Programming)

EngGen 131 S2 2013

However, it is not nice to have the warning appear whenever we compile our code. Visual Studio provides a simple solution to this. If we add the following preprocessor directive: #define _CRT_SECURE_NO_WARNINGS as the very first line of code in our source file (before any of the #include directives) then this warning will not be issued by the compiler.

Lecture 14 Summary Program


We can now write programs that receive input from the user, process that input using the standard arithmetic operators, and then display the results to the screen. For example, the following program converts a period of time specified by the user in seconds, to hours minutes and seconds. #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #define SECONDS_PER_MINUTE 60 #define SECONDS_PER_HOUR 3600 int main(void) { int input, seconds, leftOverSeconds, minutes, hours; printf("Enter duration (secs): "); scanf("%d", &input); hours = input / SECONDS_PER_HOUR; leftOverSeconds = input % SECONDS_PER_HOUR; minutes = leftOverSeconds / SECONDS_PER_MINUTE; seconds = leftOverSeconds % SECONDS_PER_MINUTE; printf("%d seconds is %d hours, %d minutes and %d seconds.\n", input, hours, minutes, seconds); return 0; }

An example of this program running is given below (user input is in bold): Enter duration (secs): 12345 12345 seconds is 3 hours, 25 minutes and 45 seconds.

29

Lecture 14

Course Manual (C Programming)

EngGen 131 S2 2013

http://xkcd.com/

Lecture 14

30

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 15: Conditionals, Loops and 1D arrays


With the programs we have seen so far, the statements in the main() function are executed in order, from top to bottom. We can modify the flow of control of our programs by using conditionals and loops, and we can use arrays to store and process large collections of data.

Learning outcomes
After this lecture, you should be able to: evaluate boolean expressions consisting of the relational and logical operators use an if statement to conditionally execute a block of code use both while and for loops to repetitively execute blocks of code declare and initialise a one-dimensional array variable in a single statement write a loop which iterates over a one-dimensional array

MATLAB Comparison
MATLAB provides statements for performing conditional execution (IF) and loops (FOR, WHILE).
MATLAB

IF expression statements ELSEIF expression statements ELSE statements END

FOR variable = expr statement statement END

WHILE expression statements END

C has corresponding statements, but with some syntax differences. MATLAB provides built-in support for working with collections of values. Defining vectors or matrices is trivial, and the arithmetic operators are designed to work automatically with such structures. For example, two vectors can be initialised and then added as follows:
MATLAB

>> a = [1 2 3]; >> b = [4 5 6]; >> c = a + b c = 5 7 9

In C, there is no automatic support for working with anything except the basic types. If we need to store a large collection of data, we should use an array. There are no built-in operations that we can use to process the data in an array, instead we must define the code that processes them.
Lecture 15

31

Course Manual (C Programming)

EngGen 131 S2 2013

Booleans in C
C does not have an explicit boolean type. Instead, the language treats the integer value 0 as false, and any non-zero integer value (although 1 is typically chosen) as true. Any integer expression can therefore be used as a condition. Conditions can be composed of expressions combined with the relational operators: == != < <= > >= equal not equal less than less than or equal greater than greater than or equal

Expressions involving the relational operators always evaluate to either 0 (for false) or 1 (for true). Consider the following example: int value; value = (11 == 12); printf("%d\n", value); value = (11 <= 12); printf("%d\n", value); value = (11 != 12); printf("%d\n", value); The output of the code to the left would be: 0 1 1

Conditions can be combined using the logical operators: && || ! and or not

Again, the logical operators always evaluate to either 0 (for false) or 1 (for true). The first two operators, && and ||, take two operands, whereas the ! operator takes just one. Order of precedence: If parentheses are not used to enforce order of evaluation, the order of precedence of the logical operators is: ! (highest precedence) && || (lowest precedence) For example: int value; value = !(5 < 6) && (3 == (1 + 2)); printf("%d\n", value);
32

The output of the code to the left would be: 0


Course Manual (C Programming)

Lecture 15

EngGen 131 S2 2013

Selection statements
C uses curly braces { } to denote blocks of code. The basic if statement has the following syntax: if ( <expression> ) <statement> First, the <expression> is evaluated. If it is true, then the <statement> is executed, otherwise the <statement> is not executed. As an example: int value = 9; if ((value >= 20) || (value < 10)) printf("Value = %d", value); would print: Value = 9 Whereas: int value = 9; if ((value >= 20) && (value < 10)) printf("Value = %d", value); would not produce any output. Multiple statements If there are multiple statements in the block, then curly braces are mandatory: if ( <expression> ) { <statement> <statement> <statement> } A common mistake is to forget to include the curly braces when there is more than one statement in the block. Be aware that indentation is ignored by the compiler its purpose is to make the logical organisation of the code easier for us to read and understand. So the following code: if (1 == 2) printf("Never printed"); printf("Always printed"); would always print "Always printed" despite the fact the condition is obviously false.

To avoid such problems, it is a good idea to get into the habit of always including the braces around blocks of code, even if they only consist of a single line.
Lecture 15

33

Course Manual (C Programming)

EngGen 131 S2 2013

An else clause can also be included, and will be executed if the condition is false: if ( <expression> ) { <statement> <statement> } else { <statement> <statement> } Again, if there is only a single statement in the block, then the curly braces are not necessary: if (1 == 2) printf("Never printed"); else printf("Always printed"); However, be careful the following code will not compile because the second printf() statement appears outside of the if clause, and hence when the compiler encounters the else statement there is no corresponding if. In this case, curly braces are necessary to denote the blocks. if (1 == 2) printf("Never printed"); printf("This code won't compile"); else printf("Always printed");

won't compile

To avoid these kinds of problems, it is a good idea to always include braces explicitly around blocks of code even if the block consists of only a single statement. else if A common combination of if and else occurs when there are a number of conditions to be tested, and only one will be true. if ( <expression-1> ) { <statement> <statement> } else if (<expression-2>) { <statement> <statement> } else if (<expression-3>) { <statement> <statement> } else { <statement> <statement> } With if, else if, else statements, only one of the blocks of statements will be executed. To the right is an example, where only the statements in the else clause will be executed.
Lecture 15

if (1 == 2) { printf("Not printed"); printf("Not printed"); } else if (2 == 3) { printf("Not printed"); } else { printf("1 does not equal 2"); printf("and 2 does not equal 3"); } 34
Course Manual (C Programming)

EngGen 131 S2 2013

Consider the following program which asks the user to enter an exam mark, and then displays whether or not the exam mark is valid. For the purposes of this program, an exam mark is valid if it is between 0 and 100 inclusive: File: exam.c #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main(void) { int examMark; printf("Enter exam mark: "); scanf("%d", &examMark); if ((examMark >= 0) && (examMark <= 100)) printf("%d is valid", examMark); else printf("the exam mark must be between 0 and 100"); return 0; }

Notice that the conditional test: if ((examMark >= 0) && (examMark <= 100)) determines whether or not the exam mark lies between 0 and 100 inclusive by testing two separate conditions: "examMark is greater than or equal to zero AND examMark is less than or equal to 100" Warning be careful Consider what would happen if the conditional test in the previous program was written as: if (0 <= examMark <= 100) which looks correct from a mathematical point of view. In this case, because the relational operators will be evaluated from left to right, the compiler interprets the expression as: (0 <= examMark) <= 100 In this case, the condition (0 <= examMark) will always evaluate to either 0 or 1 (depending on the value of examMark), but this result is always going to be less than 100, so the expression: 0 <= examMark <= 100 will always evaluate to true, regardless of the value of examMark.
Lecture 15

35

Course Manual (C Programming)

EngGen 131 S2 2013

Conditional operator (NOT EXAMINABLE) C provides a special conditional operator which can be used in place of simple if/else statements. The general form is below: <expression1> ? <expression2> : <expression3> First, expression1 is evaluated. If it is true, then expression2 is evaluated and becomes the result of the statement, otherwise expression3 is evaluated and becomes the result of the statement. For example, consider the following code which prints out the value stored in the variable apples which is of type int. It also prints out the word "apple" and uses an if else statement to decide whether or not to add an "s": if (apples == 1) printf("1 apple\n"); else printf("%d apples\n", apples); An equivalent way of doing this would involve using the conditional operator as follows: printf("%d apple%s\n", apples, (apples == 1) ? "" : "s"); switch statement (NOT EXAMINABLE) A switch statement can often be clearer than a series of if/else statements. The general form is: switch ( <expression> ) { case value1: statements; break; case value2: statements; break; ... default: statements; break; } The controlling expression must be an integer expression The case values must be unique and they must be constants Once a case is matched, all following statements are executed until a break is encountered The default statements are always executed, unless a break has been encountered

Lecture 15

36

Course Manual (C Programming)

EngGen 131 S2 2013

For example, consider the following code: char grade; printf("Enter your grade: "); scanf("%c", &grade); switch( grade ) { case 'A': printf( break; case 'B': printf( break; case 'C': printf( case 'D': printf( break; default: printf( break; }

"excellent" );

"good" );

"ok but " ); "you must do better" );

"are you sure??" );

Output from this code segment is below (user input is in bold): Enter your grade: A excellent Enter your grade: B good Enter your grade: C ok but you must do better Enter your grade: D you must do better Enter your grade: X are you sure??

Notice that when the input was 'C', two printf() statements were executed because there is no break statement in the case 'C' clause.

Lecture 15

37

Course Manual (C Programming)

EngGen 131 S2 2013

Loops
C provides syntax for three different kinds of loops (for, while and do). while A while loop has the following syntax: < initialisation > while ( < condition > ) { < statement >; < statement >; < increment >; } for A for loop combines the standard initialisation, condition and increment into its syntax: for ( <initialisation> ; < condition > ; <increment> ) { <statement>; <statement>; } do A do loop is very similar to a while loop, except the condition is tested at the end of the loop rather than at the start (this is useful if the body of the loop needs to be executed once before the condition is tested): < initialisation > do { < statement >; < statement >; < increment >; } while ( < condition > );

Of the three different kinds of loops, for and while loops are the most commonly used. The following table shows a comparison of the three loop styles. for
int i; for (i = 0; i < 5; i++) { printf("%d ", i); } int i; i = 0; while (i < 5) { printf("%d ", i); i++; }

while
int i; i = 0;

do

do { printf("%d ", i); i++; } while (i < 5);

These loops are equivalent in each case the output is a single line displaying the values: 0 1 2 3 4
Lecture 15

38

Course Manual (C Programming)

EngGen 131 S2 2013

break and continue Sometimes, when the body of a loop is executing, we might encounter a situation where we would like to either jump out of the loop completely, or stop executing the remaining statements in the body of the loop and simply jump back to the top of the loop and test the condition again. The keywords break and continue, when used inside the body of a loop, allow us to do these things as shown in the diagram below.

while (...) { ... break; ... }

while (...) { ... continue; ... }

Consider the following loop, which determines whether the value stored in the variable value is a prime number, by testing to see if it is divisible by any number less than itself (starting from 2): i = 2; while ((i < value) && (value % i != 0)) { i++; } if (i == value) printf("%d is a prime number\n", value); else printf("%d is not a prime number\n", value); Another way this code could be written would be to use a break statement in the body of the loop. This has advantages and disadvantages: while it complicates the body of the loop, it simplifies the loop condition. i = 2; while (i < value) { if (value % i == 0) break; i++; } if (i == value) printf("%d is a prime number\n", value); else printf("%d is not a prime number\n", value);
Lecture 15

39

Course Manual (C Programming)

EngGen 131 S2 2013

One-dimensional arrays
Arrays store a collection of variables of the same type. An array has an identifier, and the individual elements can be accessed through the identifier and a subscript. In order to use an array in your program, like any other type of variable, it must first be declared. Declaring an array The general form of declaring an array is as follows:

<type> <identifier>[<size>];

The value of <size> must be a constant integer value. For example, we could declare an array called numbers consisting of 8 elements using a constant as follows: int main(void) { int numbers[8]; ... ... return 0; }

Or, perhaps a better idea in terms of code style, we could use a #define preprocessor directive: #define MAX_INPUTS 8 int main(void) { int numbers[MAX_INPUTS]; ... ... return 0; }

In either case, this will allocate a contiguous block of memory that is made up of individual int variables. We can visualise this array in memory as follows:

numbers
0 1 2 3 4 5 6 7

Lecture 15

40

Course Manual (C Programming)

EngGen 131 S2 2013

You cannot assume that the values initially stored in the array elements are 0. These values may, in fact, be random and so you must initialise the elements in the array before you use them (initialisation is introduced shortly).

numbers

? 0

? 1

? 2

? 3

? 4

? 5

? 6

? 7

The memory for the array is allocated when the main() function is called. Like any other variable that is declared inside the definition of the main() function, we would visualise it as part of the memory allocated for the main() function, as in the diagram on the right. Also, because the size of the array is specified in the array declaration, it cannot be resized or made larger. Therefore we need to carefully decide what the most appropriate size for the array is at the time we declare it in our code. Due to this limitation, it is quite common for programmers to create arrays dynamically (using, for example, malloc() in which case the memory is allocated on the heap rather than the stack), so that their size can be determined and modified while the program is running. However, in this course we will not be considering dynamic memory allocation. Note that in the array declaration, the size of the array must be a constant. In particular, it cannot be a variable as in the incorrect example below: int main(void) { int x = 100; int values[x]; ... return 0; } Although it is obvious to us that in the above example the value stored in x is 100, the compiler does not verify this and wont allow the code to compile. Warning: variable length arrays An update to the original ANSI C standard, known as C99, introduced a feature known as variable length arrays, which does allow code like the example above where the size of the array is determined at run-time based on the value stored in a variable. Some compilers (such as gcc) do support this feature, however it is not supported by all compilers, notably the Microsoft compiler (cl) used in this course. For this reason, in this course do not write code that uses variable length arrays.
Lecture 15

The compiler will issue an error: error: expected constant expression

41

Course Manual (C Programming)

EngGen 131 S2 2013

Accessing array elements To access an individual element in an array, we use square brackets and we specify the index, or subscript, of the element we want.

numbers
0 1 2 3 4 5 6 7

numbers[1]

Each element of this array is simply a variable of type int. We can therefore refer to an element of this array in any situation where it makes sense to have a variable of type int. For example, the following expression is perfectly valid: numbers[0] = numbers[1] + numbers[2];

as this sets the value of the first element of the array (an int variable) to be the sum of the next two elements (both int variables). Consider the following array:

numbers

3 0

1 1

6 2

2 3

5 4

9 5

7 6

8 7

After the expression: numbers[0] = numbers[1] + numbers[2];

is executed, the sum 7 would be stored in index position 0:

numbers

7 0

1 1

6 2

2 3

5 4

9 5

7 6

8 7

Lecture 15

42

Course Manual (C Programming)

EngGen 131 S2 2013

Initialising an array The elements of an array can be initialised as part of the declaration, by surrounding the values in curly braces {} and separating them with commas. int numbers[8] = {6, 3, 9, 0, 0, 0, 0, 1};

The resulting array would be visualised as follows:

numbers

6
0

3
1

9
2

0
3

0
4

0
5

0
6

1
7

NOTE: In this example, it is not actually necessary to include the length of the array in the square brackets because the compiler can work it out based on the length of the initialisation sequence. Therefore, the above array could also be initialised with the following statement: int numbers[] = {6, 3, 9, 0, 0, 0, 0, 1}; However, for clarity, it is a good idea to always include the array length in the square brackets.

If the size is specified, and the initialisation sequence is shorter than the length of the array, then the remaining values are filled in with zero. For example: int numbers[20] = {7};

sets aside space for 20 ints in the numbers array which are all initialised to zero:

numbers
7
0

0
1

0
2

0
3

0
4

0
5

0
6

0
7

0
8

9 10 11 12 13 14 15 16 17 18 19

Warning be careful We must be very careful not to attempt to access a non-existent element in an array. For example, assume we tried to access the element at index 20 in the above array: numbers[20] = 2;
Lecture 15

43

Course Manual (C Programming)

EngGen 131 S2 2013

This will overwrite some memory outside of the array bounds.

numbers
0
0

0
1

0
2

0
3

0
4

0
5

0
6

0
7

0
8

9 10 11 12 13 14 15 16 17 18 19

numbers[20] = 2;

This kind of error may or may not cause our program to crash immediately. If it does crash straight away, then we are lucky because we will know something is wrong. If not, then the error may be very difficult for us to find because we may not discover it until we try to access the overwritten value and realise it is not what we expected.

As an example, consider the following poorly written code: int numbers[20] = {0}; printf("%d\n", numbers[19]); printf("%d\n", numbers[20]); The valid index values for this array range from 0 to 19 inclusive. 20 is not a valid index value, and therefore if we attempt to access it, the value that is stored in the memory location is unpredictable. Here is the output produced when the above code was executed: 0 1245120 Notice that the code compiled successfully and there was no indication of any error when the program was run, other than the value printed out being unusual. If this value was used in some equation or expression rather than being printed out, the program may behave unpredictably. You need to be very careful not to access elements outside of the valid bounds of an array.

Lecture 15

44

Course Manual (C Programming)

EngGen 131 S2 2013

Processing arrays If we want to access all of the elements in an array, for example maybe we want to set each element equal to some value, we must do so one by one. There are no shortcut operations that will process an entire array at once. The most convenient way of accessing each element in an array is to use a loop. For example, consider the following loop which sets each element of the array numbers equal to 10: int numbers[20]; int i; for (i = 0; i < 20; i++) { numbers[i] = 10; }

Sometimes it is difficult to predict exactly how many elements are going to be required to store information when the program executes. It is therefore quite common to allocate more space than is expected to be required, and use a separate variable to store how many of the initial elements of the array are actually being used to store useful information. For example, consider the following variable declarations: int numbers[20] = {0}; int numElements = 0;

We could visualise these variables as follows:

numbers
0
0

0
1

0
2

0
3

0
4

0
5

0
6

0
7

0
8

9 10 11 12 13 14 15 16 17 18 19

numElements

Each new data value is stored in the array at the index specified by numElements, and then the value of numElements is incremented. For example, consider the array after four values 7, 3, 1 and 8 have been added:

numbers
7
0

3
1

1
2

8
3

0
4

0
5

0
6

0
7

0
8

9 10 11 12 13 14 15 16 17 18 19

numElements

Lecture 15

45

Course Manual (C Programming)

EngGen 131 S2 2013

If we were to print out each of these data values, we would use the value of the variable numElements in the stopping condition to the loop, as follows: for (i = 0; i < numElements; i++) { printf("%d ", numbers[i]); }

If we wanted to store another data value in the array, the value of numElements indicates at which index the new value should be stored. After the new value is stored, we would also have to increment numElements. For example, to store the value 99: numbers[numElements] = 99; numElements++;

after which, the array and the variable numElements would be visualised as:

numbers
7
0

3
1

1
2

8 99
3 4

0
5

0
6

0
7

0
8

9 10 11 12 13 14 15 16 17 18 19

numElements

This approach is very common when we don't know beforehand exactly how many elements we need to store, and hence cannot allocate exactly the right amount of space at the time the array is declared.

Lecture 15

46

Course Manual (C Programming)

EngGen 131 S2 2013

Summing the elements Assume we have an array of integers called numbers, and the variable numElements stores the number of elements in the array that store useful data. We might want to calculate the sum of all of the values in this array. An algorithm for the basic steps required is: start with the value 0 add numbers[0] to this add numbers[1] to this add numbers[2] to this ... add numbers[numElements-1] to this

We can easily convert the basic steps listed above into source code, by defining a variable called sum to keep a track of the current sum as we iterate through the elements of the array: int sum = 0; int i; for (i = 0; i < numElements; i++) { sum += numbers[i]; }

Finding the minimum value Again, assume we have an array of integers called numbers, and the variable numElements stores the number of elements in the array that store useful data. We might want to calculate the value of the smallest element in the array. We can start by recording the smallest element we have seen so far, and then updating this as we progress through the array. Initially, the first element of the array must be the smallest value we have seen so far: int smallestSoFar = numbers[0];

The next value we need to consider is the second element in the array. So the loop variable will start at 1: int i; for (i = 1; i < numElements; i++) { if (numbers[i] < smallestSoFar) { smallestSoFar = numbers[i]; } } When the loop finishes, the value stored in the variable smallestSoFar will be the smallest value in the array.

Lecture 15

47

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 15 Summary Program


We can now write programs that use conditionals to make decisions and use loops for repetition. For example, the following program determines whether or not the number entered by the user is a prime number. #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main(void) { int value, i; printf("Enter a number: "); scanf("%d", &value); i = 2; while ((i < value) && (value % i != 0)) { i++; } if (i == value) printf("%d is a prime number\n", value); else printf("%d is not a prime number\n", value); return 0; }

An example of this program running is given below (user input is in bold): Enter a number: 133 133 is not a prime number

Lecture 15

48

Course Manual (C Programming)

EngGen 131 S2 2013

We can also use arrays to store and process collections of values. Below is a more complicated example. It extends the previous program, by allowing the user to enter any number (up to MAX_INPUTS) of input values which are then stored in an array. The user enters the value -1 to indicate no more input. The program then tests each of the numbers in the array for primality similar to the previous program. #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #define MAX_INPUTS 100 int main(void) { int inputs[MAX_INPUTS]; int numInputs = 0; int value, testIndex, i; printf("Enter numbers to test for primality.\n"); printf("Enter -1 to stop input and begin testing.\n\n"); value = 0; while (value != -1) { printf("Enter number %d: ", numInputs); scanf("%d", &value); if (value != -1) { inputs[numInputs] = value; numInputs++; } } printf("\nNow testing for prime numbers...\n\n"); for (testIndex = 0; testIndex < numInputs; testIndex++) { value = inputs[testIndex]; i = 2; while ((i < value) && (value % i != 0)) { i++; } if (i == value) printf("%d is a prime number\n", value); else printf("%d is not a prime number\n", value); } return 0; }

Lecture 15

49

Course Manual (C Programming)

EngGen 131 S2 2013

An example of this program running is shown below (user input is in bold): Enter numbers to test for primality. Enter -1 to stop input and begin testing. Enter Enter Enter Enter Enter Enter Enter Enter number number number number number number number number 0: 1: 2: 3: 4: 5: 6: 7: 100 37 15 31 94537207 7850 7853 -1

Now testing for prime numbers... 100 is not a prime number 37 is a prime number 15 is not a prime number 31 is a prime number 94537207 is a prime number 7850 is not a prime number 7853 is a prime number

http://xkcd.com/

Lecture 15

50

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 16: 2D arrays, Library Functions and Structures


A two-dimensional array is a natural extension of a one-dimensional array, where the elements are arranged into rows and columns, and two index values are used to access the data. Many useful tasks can be performed by calling functions that are defined in libraries that are either a standard part of the language, or programmer-defined. The basic C data types can be combined together to form more complex types which may be better suited to the problems we are working on.

Learning outcomes
After this lecture, you should be able to: declare a two-dimensional array, and use a nested loop to iterate over its elements write code that includes the header file for an ANSI C standard library or an external library, and makes calls to the functions defined in that library define and use a structure consisting of several fields declare and use an array where the elements of the array are a structure type

MATLAB Comparison
In MATLAB, a two-dimensional array (or matrix) can be defined by specifying the comma-separated values one row at a time, with each row terminated by a semi-colon:
MATLAB

>> A = [1, 0, 2; 0, 2, 3; 0, 4, 5; 1, 0, 0];

To work with two-dimensional arrays in C, we must declare the appropriate type of variable and access the data using two index values one for the row and one for the column. There are a large number of useful functions that are available to us in MATLAB. To use any of the standard MATLAB functions, we need to know the name of the function, what sort of input arguments to pass to it, and what is the output returned by the function. For example, to use the sqrt() function, we could pass a vector to it and it would return a new vector containing the square root of the original elements:
MATLAB

>> A = [50,60,70]; >> B = sqrt(A);

Lecture 16

51

Course Manual (C Programming)

EngGen 131 S2 2013

In C, there are also a number of standard library functions we can call (although the standard C library is much smaller than the MATLAB library). We need to know the name of the function, what values to pass to it, and what value it returns. Unlike MATLAB, in C there is no automatic support for working with anything except the basic types. If we need more complex types we can define our own data types, called structures, to specifically suit our needs. To perform operations with structures, we need to explicitly define the code that processes them.

Two-dimensional arrays
Sometimes the data we are working with is most naturally organised as a table, arranged into rows and columns. A good example is when we are manipulating the pixels of an image. A two-dimensional array has two index values, or subscripts: one to denote the row and the other to denote the column of the element. A two-dimensional array is declared in a similar way to a one-dimensional array, except both the number of rows and the number of columns must be specified. For example, a 4 row, 3 column array can be declared with the following statement: int numbers[4][3]; Although the memory allocated for this array would be a single contiguous block (in this case consisting of enough space to store 12 ints), it is more convenient to visualise the array as follows, organised into 4 rows and 3 columns:
0 0 1 2

numbers
2 3

With two-dimensional arrays, we typically use a nested loop to access each element systematically. For example, given the array as declared previously: int numbers[4][3]; we might want to initialise this so that each element stores the sum of its row and column position. To do this, we would use a nested loop: for (i = 0; i < 4; i++) { for (j = 0; j < 3; j++) { numbers[i][j] = i + j; } }

Lecture 16

52

Course Manual (C Programming)

EngGen 131 S2 2013

And, to display the contents of this two-dimensional array (formatted into rows and columns) we could use the following code: for (i = 0; i < 4; i++) { for (j = 0; j < 3; j++) { printf("%d ", numbers[i][j]); } printf("\n"); } which would produce the following output: 0 1 2 3 1 2 3 4 2 3 4 5

Rows then columns When accessing an element of a two-dimensional array, such as: numbers[i][j] it is conventional for the first index, i, to represent the row index and the second index, j, to represent the column index. It is a good idea to stick with this convention all the time, to avoid confusion.

Initialising a two-dimensional array The elements of a two-dimensional array can be initialised as part of the declaration. The values on each row of the array should be surrounded in curly braces {} and separated with commas, as should the rows themselves. For example, the initialisation below creates the same array that was initialised using a nested loop in the previous example: int numbers[4][3] = { {0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {3, 4, 5} };

We would visualise this array as follows:

0 0

0 1 2 3

1 2 3 4

2 3 4 5

numbers
2 3

Lecture 16

53

Course Manual (C Programming)

EngGen 131 S2 2013

ANSI C standard library


The functions available in the ANSI C standard library are declared in a set of 15 header files. Each header file contains the declarations for a number of functions which have been categorised as performing similar types of tasks. To use a function that is declared in a particular header file, the preprocessor directive: #include <name of standard library header file> should be included at the top of the source file. For example, we have seen that in order to use the printf() or scanf() functions, we must include the <stdio.h> header file. The list of header files that comprises the standard library is given below: <assert.h> : <ctype.h> : <errno.h> : <float.h> : <limits.h> : <locale.h> : <math.h> : <setjmp.h> : <signal.h> : <stdarg.h> : <stddef.h> : <stdio.h> : <stdlib.h> : <string.h> : <time.h> : Diagnostics Character Class Tests Error Codes Reported by (Some) Library Functions Implementation-defined Floating-Point Limits Implementation-defined Limits Locale-specific Information Mathematical Functions Non-local Jumps Signals Variable Argument Lists Definitions of General Use Input and Output Utility functions String functions Time and Date functions

Mathematical functions
To use the math functions from the standard library, our program must include: #include <math.h> Some of the useful math functions, which take double values as arguments and return double values, include: sqrt(x) computes the square root of x pow(x, y) computes x to the power of y sin(x) computes the sine of x, where x is in radians For example, the following code: double values = pow(2, 16); printf("%f", values); would produce the output: 65536.000000
Lecture 16

54

Course Manual (C Programming)

EngGen 131 S2 2013

Character functions
The standard library contains a number of functions for input and output of character data, as well as processing character data. Character based input and output can be achieved with the following pair of functions (defined in <stdio.h>): putchar(c) getchar() outputs the specified character to standard output (the screen) reads a character as input from standard input (the keyboard)

The getchar() function returns an int value, that will store the ASCII code of the character entered. Note, the int type is large enough to represent all possible characters. For example, the following code: int c = getchar(); printf("You typed %c", c); will store the character that is entered at the keyboard in the variable c, and print this out to the screen. A number of character conversion and comparison functions are defined in the standard library. To use these, our program must include: #include <ctype.h> A few of these useful functions are listed below: toupper(ch) tolower(ch) isdigit(ch) isalpha(ch) returns the corresponding upper case letter returns the corresponding lower case letter returns non-zero if ch is a digit, 0 otherwise returns non-zero if ch is an uppercase or lowercase letter, 0 otherwise

Although processing characters, these functions take int values as arguments and return int values. This is fine because the int type is larger than the char type, and so is able to represent all possible character values. EXAMPLE Lets look at an example that highlights a potential problem when dealing with character input. Assume we would like to prompt the user to enter either y or n, for example to indicate yes or no in response to some question. If the user enters y or n then we will display their choice back to them, otherwise if they enter any other character, we will prompt them again. Here are several examples of the program running:
Play again? (y/n): n Thanks - you entered n Play again? (y/n): a Play again? (y/n): N Thanks - you entered n Play again? (y/n): q Play again? (y/n): w Play again? (y/n): e Play again? (y/n): r Play again? (y/n): t Play again? (y/n): y Thanks - you entered y

Notice (in the middle example above) that the program converts upper case input characters into lower case. This allows the user to enter either n, N, y or Y.
Lecture 16

55

Course Manual (C Programming)

EngGen 131 S2 2013

Below is one implementation of a program for handling user input in this way: #include <stdio.h> #include <ctype.h> int main(void) { int c = ' '; int validInput = 0; while (!validInput) { printf("Play again? (y/n): "); c = getchar(); if (c == '\n') { c = getchar(); } c = tolower(c); if ((c == 'n') || (c == 'y')) { validInput = 1; } } printf("Thanks - you entered %c\n", c); return 0; } You can see that the while loop continues to execute until the value stored in the variable validInput becomes 1. This only occurs when the lower case version of the input character is either n or y. When the user enters a character at the command line, there are in fact two characters that get stored in the input buffer. The input buffer is a block of memory where input values are stored until they are needed by the program. For example, if the user enters the character a, both the a and the new line character (representing the ENTER key) will be stored in the input buffer. This can cause a problem: the first time through the loop, when we call getchar(), we will receive the a character. However, the new line character will still be in the buffer. Therefore, if we only called getchar() once each time around the loop, the new line character would be returned on the next iteration. If we dont take care to ignore this character, it may be confusing for the user. We need to ignore the new line character from the input buffer each time through the loop. The solution that has been used in the example above is to test if the character from the input buffer is the new line character: if (c == '\n') { c = getchar(); } If it is, then we simply ignore it by calling the getchar() function again to get the next character the user enters.

Lecture 16

56

Course Manual (C Programming)

EngGen 131 S2 2013

External library functions


The functions defined in the ANSI C standard library are guaranteed to be available on any system that is ANSI C compliant. This means that programs relying on functions from the standard library can be transferred (or ported) from one system to another and still compile and work correctly. There may be functions defined in libraries that are not part of the standard library which we would like to use in our programs. In such cases, we would typically have access to both the header file and the source file for the library. The header file contains declarations (essentially just the names and parameter types) of all of the functions in the library, and the source file contains the actual definitions of these functions. To use the functions defined in the library, we would include the header file using the #include preprocessor directive. When the #include directive is used to include a header file that is not part of the standard library, the name of the header file is surrounded with quotation marks, rather than the angle brackets that are reserved for header files from the standard library:

#include "name of programmer defined header file"

For example, assume that we would like to make use of functions defined in an external library distributed as the header file library.h and the source file library.c. We would add the statement: #include "library.h" and make sure the library.h and library.c files were available to the compiler.

library.h

main.c #include "library.h" int main(void) { ... ... ... ... } our program library.c

external library

We could now call functions defined in the library from within the main.c source file.

Lecture 16

57

Course Manual (C Programming)

EngGen 131 S2 2013

Example: LibBMP
The Windows Bitmap, or BMP, file format is a fairly well known (and relatively simple) file format for storing digital images. Given a file in this format, say image.bmp, we can manipulate the image in many possible ways as long as we can perform two simple operations: obtain the colour value of a pixel at a given row and column of the image change the colour value of a pixel at a given row and column of the image to a new value

To provide this functionality, in this course we will use a library called LibBMP.

LibBMP.h

images.c #include "LibBMP.h" int main(void) { ... ... ... ... } our program LibBMP.c

external library

The LibBMP library consists of a header file: LibBMP.h, and a source file: LibBMP.c. You can download both of these files from Cecil. This library contains functions that allow you to read the bitmap data from an image file stored in the BMP (Windows Bitmap) file format (only uncompressed 24-bit images are supported). To use this library from Visual Studio, copy these two files into your project folder, and then add LibBMP.c and LibBMP.h to your project. To use this library from the command prompt, simply copy these two files into the folder for your program. You will need to add: #include "LibBMP.h"

to the top of the source file that is going to use the library.

Lecture 16

58

Course Manual (C Programming)

EngGen 131 S2 2013

The LibBMP library provides the following functions that you may find useful. LoadBMPFile("fi.bmp", &width, &height) This loads an image file stored on disk in BMP format into memory. Parameters: "fi.bmp": is the name of the input image file this will already exist on disk in the project folder &width: the address of the int variable that will be initialised to the width of the image once it is read from disk &height: the address of the int variable that will be initialised to the height of the image once it is read from disk GetPixelValue(row, col, channel) Once an image file has been loaded into memory, this function returns an integer value (between 0 and 255) representing the intensity of the specified colour channel of a pixel in the image. Parameters: row: the row, or vertical position, of the pixel col: the column, or horizontal position, of the pixel channel: the colour channel (0 = red, 1 = green, 2 = blue) SetPixelValue(value, row, col, channel) Once an image file has been loaded into memory, this function sets the intensity value (between 0 and 255) of the specified channel of a pixel in the image. Parameters: value: the intensity value (between 0 and 255) to be stored in the specified channel of the pixel row: the row, or vertical position, of the pixel col: the column, or horizontal position, of the pixel channel: the colour channel (0 = red, 1 = green, 2 = blue) SaveBMPFile("fo.bmp", width, height) This saves the image in memory out to a file on disk in BMP format. Parameters: "fo.bmp": is the name of the output image file this will be created as output by the program and appear in the project folder width: the width of the image height: the height of the image

Lecture 16

59

Course Manual (C Programming)

EngGen 131 S2 2013

Image manipulation example The program below uses the LibBMP library to fold a copy of the top half of an image down onto the bottom half. #include <stdio.h> #include "LibBMP.h" int main(void) { int width, height; int row, col; int colour; LoadBMPFile("clown.bmp", &width, &height); for (row = 0; row < height / 2; row++) { for (col = 0; col < width; col++) { colour = GetPixelValue(row, col, 0); SetPixelValue(colour, height - 1 - row, col, 0); colour = GetPixelValue(row, col, 1); SetPixelValue(colour, height - 1 - row, col, 1); colour = GetPixelValue(row, col, 2); SetPixelValue(colour, height - 1 - row, col, 2); } } SaveBMPFile("output.bmp", width, height); return 0; }

clown.bmp

output.bmp

Lecture 16

60

Course Manual (C Programming)

EngGen 131 S2 2013

Structures
It can be convenient to compose new types of data by combining the basic data types. This allows us to work with variables which more closely match the basic kinds of data that we are manipulating in our programs. For example, we might want to write a program that processes 2-dimensional points, each of which consists of an x and a y value. There is no predefined data type that allows us to store a point, so we can define our own data type for this. In C, the basic user-defined data type is the structure (sometimes referred to as a record). A structure consists of a collection of fields (also called components or members) which can be of different types, grouped together under a single name. We can define a new structure type using the following syntax: typedef struct { <type> <fieldName1>; <type> <fieldName2>; ... } <name>; For example, to define a structure to store our point data, we could declare the following structure: typedef struct { int x; int y; } Point; This structure definition does not allocate space for any variables. All that it does is define a new type called Point. Before we can store anything using this structure, we need to declare a variable of this new type.

#include <stdio.h> typedef struct { double x; double y; } Point; int main(void) { Point a, b; int same; a.x = 10; a.y = 20; b.x = 10; b.y = 40;

The definitions of structures, or typedefs, usually occur just after the #include and #define statements (if there are any) at the top of the source file. For example, the program on the left defines and uses Point structures.

same = ((a.x == b.x) && (a.y == b.y)); if (same) { printf("The points are the same"); } else { printf("The points are different"); } return 0; }
Lecture 16

61

Course Manual (C Programming)

EngGen 131 S2 2013

Declaring a structure variable Declaring a variable of a structure type is done in the same way as declaring a variable of one of the basic types. We simply specify the type of the variable and give it a name. For example, we could declare two variables called p1 and p2 of type Point as follows: Point p1, p2; We would visualise these variables as follows:

x p1 y p2

x y

The amount of memory that is allocated to store a structure is just the sum of the amount of memory required to store each of the fields. So in this case, both p1 and p2 would require 8 bytes of storage (two variables of type int, which each require 4 bytes) Accessing fields To access the fields of a structure we use the structure selection operator, which is a ".": Point p1; p1.x = 100; p1.y = 150; p1 y 150

x 100

Structure assignment The assignment operator copies all of the fields from one structure to another: Point p1, p2; p1.x = 100; p1.y = 150; p2 = p1; This assignment statement copies the values stored in each field of variable p1 into the fields of p2. x 100 p1 y 150 p2 y 150 x 100

Lecture 16

62

Course Manual (C Programming)

EngGen 131 S2 2013

Arrays of structures
Consider an array, where each element is a structure type. For example, given the following structure definition: typedef struct { int x; int y; } Point;

We can declare an array to store 100 points: Point locations[100];

We would visualise this array as follows:

x locations y
0

x y
1

x .... y
2

x y
99

Each element of this array, for example locations[2], can be treated in exactly the same way that we would treat an individual variable of type Point. For example, the statements: locations[2].x = 547; locations[2].y = 126;

would assign 547 and 126 to the fields of the third element of the locations array:

x locations y
0

x y
1

x 547 .... y 126


2

x y
99

Lecture 16

63

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 16 Summary Program


We can now write programs that work with arrays and structures. For example, the following program calculates some basic statistics for a small set of student marks, as entered by the user. #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #define NUM_RESULTS 5 /* A program which reads in a series of results. These results represent student results in a course and consist of an ID number and a final mark. It then echoes all of the results out to the screen and calculates and displays the average mark, as well as printing a * next to the result with the highest mark. */ /* A result will consist of an ID number and a mark */ typedef struct { int id; double mark; } Result; int main(void) { int i; double average; int topIndex; /* Create an array to store the results */ Result results[NUM_RESULTS]; /* Allow the user to input the results */ for (i = 0; i < NUM_RESULTS; i++) { printf("Please enter result %d [id mark]: ", i); scanf("%d %lf", &results[i].id, &results[i].mark); } /* Calculate the average and highest mark */ average = 0.0; topIndex = 0; for (i = 0; i < NUM_RESULTS; i++) { average += results[i].mark; if (results[topIndex].mark < results[i].mark) topIndex = i; } average = average / NUM_RESULTS;

Lecture 16

64

Course Manual (C Programming)

EngGen 131 S2 2013

/* Print the results */ printf("\nSummary:\n"); for (i = 0; i < 5; i++) { printf("ID: %d MARK: %.1f", results[i].id, results[i].mark); if (i == topIndex) { printf(" *"); } printf("\n"); } printf("\nAverage mark: %f", average); return 0; }

The output from this program, given a small input of 5 results is shown below (user input is in bold): Please Please Please Please Please enter enter enter enter enter result result result result result MARK: MARK: MARK: MARK: MARK: 0 1 2 3 4 [id [id [id [id [id mark]: mark]: mark]: mark]: mark]: 2034871 2345392 4837726 3958837 2293847 66.3 91.4 50.2 39.8 81.0

Summary: ID: 2034871 ID: 2345392 ID: 4837726 ID: 3958837 ID: 2293847

66.3 91.4 * 50.2 39.8 81.0

Average mark: 65.740000

We can visualise the memory for this program as in the diagram below. The values that appear in this diagram represent the values at the end of the program (after the loop has finished and all output has been produced, just before the return 0 statement is executed):

Lecture 16

65

Course Manual (C Programming)

EngGen 131 S2 2013

Redirection In the example above, it was inconvenient to have to enter all the results by hand. Instead of 5 entries, imagine if there were 500 we need a more convenient way to get the input into the program. In the next lecture we are going to learn about file I/O, which will allow us to read input directly from files stored on disk. However this will require us to modify our code as we will be using new library functions. How about the example above though, which works perfectly well for obtaining input from the keyboard. Is there any way we can obtain input from a file without needing to rewrite the code? Yes! If we are working at the command line, a nice solution is to use redirection, where we have the input in a text file, and use the < symbol to redirect the contents of the file as the input to the program. For example, if we created a text file called input.txt:

input.txt
2034871 2345392 4837726 3958837 2293847 66.3 91.4 50.2 39.8 81.0

We could get the same output from the program as before by redirecting the file to be the input for the program. From the command line, this would look like the following (where the compiled program is called summary): C:\Lecture 16> summary < input.txt

http://xkcd.com/
Lecture 16

66

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 17: File I/O and Random Numbers


When we are working with large amounts of data, it may not be convenient to use standard input and output to initialise or display the data. C provides file I/O functions which allow us to read data from, and write data to, files on disk. There are lots of situations where it is useful to be able to produce random numbers for example we might be programming a game and we want the behaviour to be unpredictable, or we might be implementing a computer simulation that involves randomness.

Learning outcomes
After this lecture, you should be able to: declare a file pointer variable open either a text file or a binary file for either reading or writing read input from a file using either the fscanf() or fgetc() functions write output to a file using either the fprintf() or fputc() functions close a file that has been opened set the seed of the standard library's random generator to the current time generate a pseudo-random integer, or a pseudo-random floating point number between a specified upper and lower bound using the rand() function

MATLAB Comparison
MATLAB provides the load(), save() and fprintf() functions for reading from and writing to files on disk. In C, the functions fprintf(), fscanf() and fgetc() can be used for file I/O. The rand function (which is used without parentheses for scalar values) in MATLAB returns a pseudo-random, scalar value drawn from a uniform distribution on the unit interval (0 1). For example, to make a random choice between two equally likely values, we could use:
MATLAB

if rand < .5 'heads' else 'tails' end

The C standard library also defines a rand() function which returns a pseudo-random integer value between 0 and RAND_MAX, which is a constant defined in the standard library.
67

Lecture 17

Course Manual (C Programming)

EngGen 131 S2 2013

File Input and Output


Our programs have, so far, received input from the keyboard (using the scanf() function) and produced output to the screen (using the printf() function). Most really interesting problems require that we work with large amounts of data, for which the keyboard and screen may not be the most convenient interfaces. In situations where we want to process large volumes of data, we can have our programs read input from files, and produce output to files. A file is a collection of bytes stored on disk, and has a name with which we can identify it. The general process for reading data as input from a file stored on disk is: open the file for reading read bytes of data from the file close the file The general process for writing data as output to a file on disk is: open the file for writing or appending write bytes of data to the file close the file File pointer To keep track of the file you have opened, and the current position within that file where you are reading from or writing to, C uses what is known as a file pointer. All access to the file goes via this file pointer. Declaring a variable to store this file pointer is done as follows (the file pointer variable is called fp in this example, but it could be called anything you like): FILE *fp; The reason for the * will be explained later, when we look at pointers. Apart from that, this looks pretty much like any variable declaration, with the type (FILE) and the identifier (fp). The FILE type is defined in <stdio.h>, which we must #include at the top of our source file. Text files vs. binary files A file stored on disk can be classified as either a text file or a binary file. When we work with files in our C programs, we need to specify which type of file we are working with. The correct type to use depends on whether or not it is acceptable for automatic conversions to be made to the bytes as they are being either read or written. Text files A text file consists of plain text characters which are arranged into lines. Each line of text is terminated by a special byte or sequence of bytes which indicates the end of the line. This end of line sequence is different between operating systems (for example in Unix, a single line feed character is used whereas in Microsoft Windows, a carriage return followed by a line feed is used). When a text file is being read from disk, although the actual characters of text will not be modified, the program may automatically convert the end of line sequence to a different sequence internally. Likewise, when a text file is being written to disk, the program will write the correct end of line sequence for the current operating system. On Windows, the .txt extension is often used for text files. C source files are another good example of text files.
Lecture 17

68

Course Manual (C Programming)

EngGen 131 S2 2013

Binary files A binary file consists of bytes which must be read in from file exactly as they appear on disk and must be written out to file exactly as they are stored in the program. There should be no conversion performed by the program as there is with text files. Most binary files usually require some special application to interpret the bytes so that the data in the file can be used. For example, an .mp3 music file is a good example of a binary file. In order to listen to the music encoded in the file, a music player application must be used. Other good examples of binary files include image files (such as .bmp and .jpg files). Opening a file To open a file, we call the fopen() function and assign the result of this function call to the file pointer variable, as follows: fp = fopen("<filename>", "<mode>");

where <filename> is the name of the file and <mode> can be any of several I/O modes: Mode r rb w wb a ab Explanation read text opens a text file for reading read binary opens a binary file for reading write text creates a text file for writing, if the file already exists it will be overwritten write binary creates a binary file for writing, if the file already exists it will be overwritten append text creates a text file for writing, if the file already exists, any new output will appear at the end of the file append binary creates a binary file for writing, if the file already exists, any new output will appear at the end of the file

If the file cannot be opened for some reason, for example maybe the file name is invalid, the fopen() function will return NULL (which is a symbolic constant defined in stdio.h). It is therefore good style to handle this case elegantly. For example, the following code attempts to open a text file for reading, and will print an error message if the file cannot be opened: fp = fopen("filename.txt", "r"); if (fp == NULL) { printf("Sorry, couldn't open the file"); } else { < perform the I/O > }

It is always a good idea to test for a NULL file pointer whenever opening a file, because if we try to perform any I/O on a NULL file pointer, our program will crash. An informative error message to the user that the file could not be opened is much nicer than having the program crash.

Lecture 17

69

Course Manual (C Programming)

EngGen 131 S2 2013

Reading from a file The standard input and output functions we have already used (scanf() and printf()) each have variants which work with files rather than the keyboard and the screen. These companion functions are prefixed with the letter "f", and the first parameter is a file pointer. The general form of the fscanf() function is: fscanf(fp, <format string>, <address>, ...); One "value" at a time The fscanf() function returns a value of type int. This value is the number of input items converted and assigned. When the end of the file has been reached and there is no more input to read, this function returns the value EOF, which is a constant defined in <stdio.h>. When fscanf() is called, more than one character (or byte) may be read from the file. The exact number of characters that will be read depends on the data in the file, and the conversion specifier that is used in the call. Consider the example below, in which a text file called "data.txt" contains two lines of text. The first line of text contains the characters "1234" and the second line contains the characters "5678".
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main(void) { FILE *fp; int value; fp = fopen("data.txt", "r"); if (fp == NULL) { printf("Sorry - couldn't open file"); } else { /* Read the first integer value */ fscanf(fp, "%d", &value); printf("first: %d\n", value); /* Read the second integer value */ fscanf(fp, "%d", &value); printf("second: %d\n", value); } return 0; }

data.txt

1234 5678

The first call to fscanf() will read the entire first line of text. The character '1' is read first, then the character '2', then '3' and so on it will continue to read characters from the input file as long as they are a digit from 0 to 9 (which matches the conversion specifier, which is "%d"). After the character '4' is read, the next character is not a digit (it will be the new line character) so the characters are converted to the numeric value 1234 and assigned to the variable value. The second call to fscanf() reads the entire second line the four characters that are read: '5', '6', '7' and '8' are converted to the numeric value 5678 before being assigned to the variable value. The output is therefore: first: 1234 second: 5678
Lecture 17

70

Course Manual (C Programming)

EngGen 131 S2 2013

One char / byte at a time It is also very common to use the fgetc() function for reading data from a file. The fgetc() function takes the file pointer as a parameter, and returns an integer. fgetc(fp); The integer value returned by the fgetc() function is the ASCII code of the next character (if reading from a text file) or the value of the next byte (if reading from a binary file), or the constant value EOF indicating that the end of the file has been reached. Consider the example below, in which a text file called "data.txt" contains two lines of text. The first line of text contains the characters "1234" and the second line contains the characters "5678".
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main(void) { FILE *fp; int c; fp = fopen("data.txt", "r"); if (fp == NULL) { printf("Sorry - couldn't open file"); } else { /* Read the first character */ c = fgetc(fp); printf("first: %d\n", c); /* Read the second character */ c = fgetc(fp); printf("second: %d\n", c); } return 0; }

data.txt

1234 5678

The first call to fgetc()reads the first character in the file, '1'. The value returned by fgetc() is the ASCII code of the character. The ASCII code of the character '1' is the value 49. When this is printed, note that the conversion specifier used in the printf() call is "%d" and so the output will be 49. Likewise, the second call to fgetc() reads the second character from the file and returns the value 50 (the ASCII code of the character '2'). The output is therefore: first: 49 second: 50 To print the characters themselves (rather than their ASCII codes), the "%c" conversion specifier could be used in the printf() statements. So: c = fgetc(fp); printf("first: %c\n", c); c = fgetc(fp); printf("second: %c\n", c);
Lecture 17

would produce the output: first: 1 second: 2


71
Course Manual (C Programming)

EngGen 131 S2 2013

Reading an entire file Although we can open a file and read just a few values from it, it is common to want to read all of the data from a file. As we probably do not know exactly how much data is in the file, we need some way of systematically reading each value until we reach the end of the file (at which point there are no more data values to read). The fscanf() and fgetc() functions will return the value EOF when the end of the file is reached. Consider the following statements: This assignment statement stores the value read from the file (which in this case is a single byte/character) in the variable called value This boolean statement assigns the value read from file to the variable (as described above) but also compares that value with the value EOF. Overall, this expression evaluates to true/1 (if the value is not EOF) or false/0 (if the value is EOF) This while statement uses the boolean (evaluated as described above) to execute while there are still data values in the file being read (while the end of file has not yet been reached)

value = fgetc(fp)

(value = fgetc(fp)) != EOF

while ((value = fgetc(fp)) != EOF)

We can therefore use a loop like the following to read and display all of the characters in a text file: while ((ch = fgetc(fp)) != EOF) { printf("%c", ch); }

The following example illustrates how a text file (called file.txt) may be read, one character at a time, and displayed to the screen: FILE *fp; int ch; fp = fopen("data.txt", "r"); if (fp == NULL) { printf("Sorry, the file could not be opened"); } else { while ((ch = fgetc(fp)) != EOF) { printf("%c", ch); } fclose(fp); }

Lecture 17

72

Course Manual (C Programming)

EngGen 131 S2 2013

The example below illustrates the difference between reading one character at a time and reading one value at a time from a text file. Assume we have the following variable declarations: FILE *fp; int value; and that the file input.txt (shown below) has been opened through the file pointer fp: input.txt 1234 5678

One character at a time: while ((value = fgetc(fp)) != EOF) { printf("Value = %c (decimal=%d)\n", value, value); } Output: Value = 1 (decimal=49) Value = 2 (decimal=50) Value = 3 (decimal=51) Value = 4 (decimal=52) Value = (decimal=10) Notice that the file contains one new line Value = 5 (decimal=53) character at the end of the first line of Value = 6 (decimal=54) the file. This character is read from the Value = 7 (decimal=55) file (just like the other characters). When Value = 8 (decimal=56) the new line character is printed (using the %c conversion specifier, you can see that a new line is actually displayed). The ASCII character code of the new line character is 10. In this example, one character is read at a time from the file. The output displays both the printable representation of the character and the decimal ASCII code of the character.

One value at a time: while (fscanf(fp, "%d", &value) != EOF) { printf("Value = %d\n", value); } In this example, the %d conversion specifier reads one integer from the file. The fscanf() function is smart enough to read a sequence of characters that make up an integer, until it reaches some whitespace (like a new line or a space). So in this case, the two values are read from the file one line at a time.
Lecture 17

Output: Value = 1234 Value = 5678

73

Course Manual (C Programming)

EngGen 131 S2 2013

Writing to a file The general form of the fprintf() function is: fprintf(fp, <format string>, <value>, ...); This behaves like the printf() function, except the first parameter is a file pointer, and the characters are written to the file rather than the screen. It is also common to use the fputc() function for writing data to a file. The fputc() function takes two parameters an integer storing the character (or byte) to be written to file, and the file pointer: fputc(ch, fp); For example, the following code duplicates a binary file (in this case a music file called music.mp3) by reading one byte at a time from the original file using fgetc(), and writing one byte at a time to the output file (called duplicate.mp3): FILE *fpIn, *fpOut; int byte; fpIn = fopen("music.mp3", "rb"); fpOut = fopen("duplicate.mp3", "wb"); if ((fpIn == NULL) || (fpOut == NULL)) { printf("Sorry, the files could not be opened"); } else { while ((byte = fgetc(fpIn)) != EOF) { fputc(byte, fpOut); } fclose(fpIn); fclose(fpOut); }

Notice the "rb" and "wb" modes used for opening the files these are necessary because the files contain binary data. Closing a file It is always a good idea to close a file when you have finished with it, although this will happen automatically when our program exits. To close a file, we call the fclose() function and pass it the file pointer: fclose(fp);

Lecture 17

74

Course Manual (C Programming)

EngGen 131 S2 2013

Random numbers
In practice, we cannot generate truly random numbers in our programs. Instead, we use functions that produce pseudo-random numbers. That is, they appear to be random, but are really generated by a formula. The standard library defines a function called rand(), which generates a pseudo-random number between 0 and RAND_MAX. rand() 0 .... RAND_MAX

The value of RAND_MAX, and the prototype of the rand() function are defined in <stdlib.h>. Therefore, our program must include: #include <stdlib.h> if we are going to use the rand() function. To find out the value of RAND_MAX, we can simply print it out: printf("%d\n",RAND_MAX); which produces the output: 32767 The ANSI C standard defines that 32,767 is the smallest allowable value for RAND_MAX, and in fact this value is common in many modern compilers. To generate a pseudo-random number, we simply call the rand() function: #include <stdio.h> #include <stdlib.h> int main(void) { int r; r = rand(); printf("%d\n", r); return 0; } Try compiling and running the above code. What output do you get? Do you get 41? Amazing coincidence?

Lecture 17

75

Course Manual (C Programming)

EngGen 131 S2 2013

Integer sequences The sequence of values generated by rand() will be the same each time the program is executed. For example, consider the following code: int i, r; for (i = 0; i < 10; i++) { r = rand(); printf("%d\n", r); }

Every time we execute this code, the following output will be produced: 41 18467 6334 26500 19169 15724 11478 29358 26962 24464

It may seem strange that our so-called "random" numbers are so predictable. Sometimes, this can be quite useful, such as when we are debugging our program. It makes it a lot easier to diagnose a problem in our code if we are able to reproduce the same sequence of values that the program is working with each time. However, in most situations we would like our program to produce a different sequence of pseudorandom numbers each time it is executed. To achieve this, the pseudo-random number generator uses what is known as a seed to compute the first number in the sequence. We are able to initialise the value of the seed to whatever we like by calling the srand() function: srand(seed);

where seed is an unsigned int value. The srand() function only needs to be called once to initialise the seed. If it is convenient, this seed value could be provided by the user. A more common way of initialising the seed, which does not require user interaction, is to use the current time. This is useful because each time the program is executed, the current time will be different and therefore so will the seed value. The header <time.h> defines a function called time() which returns the time that has elapsed since 00:00:00 GMT, Jan. 1, 1970 (known as the Unix epoch), measured in seconds. To use this value to initialise the random number generator's seed, we call it with a parameter of NULL, and cast the return value to unsigned int, as shown in the example below: srand((unsigned int)time(NULL));
Lecture 17

76

Course Manual (C Programming)

EngGen 131 S2 2013

Range We can generate a random value between a and b inclusive, using the following formula: (rand() % (b-a+1)) + a The value of (b-a+1) should be less than RAND_MAX.

Floating point sequences To generate a random floating point value between 0.0 and 1.0 (inclusive) we can call the rand() function and divide by RAND_MAX: double randomDouble; randomDouble = ((double)rand()/RAND_MAX);

For example, the following program generates a sequence of random floating point values between 0 and 1. The random seed is set to the current time, so the numbers are different each time the program runs. File: randoms.c #define _CRT_SECURE_NO_DEPRECATE #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int i, numRandoms; double randomDouble; srand((unsigned int)time(NULL)); printf("How many random numbers? "); scanf("%d", &numRandoms); for (i = 0; i < numRandoms; i++) { randomDouble = ((double)rand()/RAND_MAX); printf("%f\n", randomDouble); } return 0; }

An example of this program running is given below (user input is in bold): How many random numbers? 5 0.060305 0.104862 0.692251 0.610340 0.785638
Lecture 17

77

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 17 Summary Program


We can now write programs that read data from and write data to files on disk, one byte or character at a time. For example, the following program strips out all punctuation from the input file input.txt, and stores the result in the output file output.txt. Only upper and lower case alphabetic characters, the space character and the new line character are preserved.

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <ctype.h> int main(void) { FILE *fpIn, *fpOut; int ch; fpIn = fopen("input.txt", "r"); fpOut = fopen("output.txt", "w"); if ((fpIn == NULL) || (fpOut == NULL)) { printf("The files could not be opened.\n"); } else { while ((ch = fgetc(fpIn)) != EOF) { if (isalpha(ch) || (ch == ' ') || (ch == '\n')) fprintf(fpOut, "%c", ch); } printf("Copying complete.\n"); fclose(fpIn); fclose(fpOut); } return 0; }

Lecture 17

78

Course Manual (C Programming)

EngGen 131 S2 2013

If the file input.txt is identical to the source code above, then after the program has executed the output file, output.txt will contain the following text: File: output.txt define CRTSECURENOWARNINGS include stdioh include ctypeh int mainvoid

FILE fpIn fpOut int ch fpIn fopeninputtxt r fpOut fopenoutputtxt w if fpIn NULL fpOut NULL printfThe files could not be openedn else while ch fgetcfpIn EOF if isalphach ch ch n fprintffpOut c ch printfCopying completen fclosefpIn fclosefpOut

return

http://xkcd.com
Lecture 17

79

Course Manual (C Programming)

EngGen 131 S2 2013

http://xkcd.com

Lecture 17

80

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 18: Functions


Although it is possible to define all of the code for our programs in the main() function, this gets very cumbersome as our programs get larger and more complex. We can define our own functions to better organise our code and make the program easier to maintain and reuse.

Learning outcomes
After this lecture, you should be able to: define a function with a name, parameter list, body and return type declare a function prototype write code which calls a function you have defined determine whether a variable is local or global and identify the scope of a variable calculate the output that would be produced by a program consisting of several functions and function calls

MATLAB Comparison
We can define our own functions in MATLAB by creating a function file which contains the statements for the function, and which has the same name as the function (with a ".m" suffix). For example, the following function would be defined in a file called polar_to_cartesian.m:
MATLAB

function [x, y] = polar_to_cartesian(r, theta) x = r .* cos(theta); y = r .* sin(theta); return;

In C, functions do not have to be defined in their own file. A program may consist of a single source file which is made up of several functions, or it may consist of multiple files, each one defining several functions. In MATLAB, the values that are returned by the function are the values that are assigned to the output variables in the body of the function (x and y in the example above). In C, a function can only return a single value, and this value is specified immediately after the return keyword in the function definition.

Lecture 18

81

Course Manual (C Programming)

EngGen 131 S2 2013

Functions
We have seen many examples of calling functions from the standard library. It is useful for us to define our own functions, so that we can decompose our programs into smaller, more manageable pieces. We can think of a function like a black box, as illustrated below. We give the function some inputs, and it produces an output for us. The term black box refers to the fact that we do not need to know about the details of how the function works in order to use it. inputs output

function

Similarly, if we are writing a function definition, we only need to interest ourselves in what the inputs are, and how we should calculate the output. We do not need to know where the function is going to be called from, or what the output value is going to be used for by some other part of the program, in order to define the function. When we define our own functions, they should perform a single, useful, well-defined task. The following table summarises some of the qualities that we should aim for when defining our own functions. Quality reusability Explanation If a function performs its job really well, then we are likely to reuse it not only in the current program, but perhaps in other programs we write as well. The rest of the program should not have to know about the details of how the function is implemented (it should be a black box). This makes the rest of the program easier to design, because we only need to call the function and process its output, without worrying about how the output is calculated. Related to information hiding, if other parts of the program only rely on the output of our function, and don't rely on the details of how it is implemented, then we are free to change the implementation (perhaps to make it more efficient) without affecting the rest of the program. Of course, the new implementation of the function would still need to produce the expected output in order for the program to work correctly. The code we write for the rest of the program can become much clearer if we make use of functions. Particularly if we need to perform some task several times, our program will be much easier to follow if we call a function each time we need to perform that task, rather than having some difficult to understand section of code repeated each time the task needs to be performed.
82
Course Manual (C Programming)

information hiding

maintainability

clarity

Lecture 18

EngGen 131 S2 2013

Defining a function A function is defined by the following: a name, which is used to call the function a list of parameters (also called arguments) which the function is given as input a body, which contains the code that processes the input and calculates the output value a return type, which defines the type of value the function produces as output

Here is an example of a function that takes an integer as a parameter, and produces twice the value of that integer as output: int TimesTwo(int a) { int result; result = a + a; return result; } Identifier names The name of a function should be chosen so that it is meaningful and describes what it is that the function does. This helps to make the code that calls the function very clear. In terms of the style of the name, there are lots of different styles that are commonly used, which differ in where the capital letters and, possibly, underscores are placed: functionName() function_Name() FunctionName() Function_Name() functionname() function_name()

There is no correct or incorrect style. What is most important is that you are consistent, and use the same style throughout. void If the function does not return any output value, for example it may produce output directly to the screen, then the return type should be specified as void. Similarly, if there are no input parameters passed to the function, void should appear between the parentheses. The following is an example of a function that takes no input parameters and returns no value: void PrintGreeting(void) { printf("Hello there!\n"); }

Lecture 18

83

Course Manual (C Programming)

EngGen 131 S2 2013

return For a function which returns a value, the return statement in the body of the function must be followed by an expression which is the same type as the return type. the return type... int TimesTwo(int a) { int result; result = a + a; return result; } ...must be the same as the type of this expression

As soon as the return statement is reached, the function stops executing and the value is returned to the line of code where the function was called from. For a function which does not return a value, the body of the function may contain a return statement which is not followed by any expression. This statement will cause the function to stop immediately, and control will be returned to where the function was called from. void PrintMessage(int a) { if (a > 0) { printf("greater than zero"); return; } } if a is greater than 0, then as soon as the return statement is reached, the function will stop and return to where it was called from in which case this statement won't be printf("less than or equal to zero\n"); executed

If a function which does not return a value does not contain a return statement, then the function returns after the last statement in the function definition has been executed. exit A special function exists which allows us to terminate the execution of a program from within any function. Unlike return, which returns control to wherever the current function was called from, the exit() function completely terminates the program, without returning control to the calling function. The exit() function prototype is declared in <stdlib.h>. This is commonly used when a situation arises from which it is not possible to continue executing the program, for example, attempting to open an input file which does not exist. The exit() function takes one parameter which is the value that will be returned to the operating system upon termination of the program. if (<cannot continue condition>) { exit(EXIT_FAILURE); }

Lecture 18

84

Course Manual (C Programming)

EngGen 131 S2 2013

The following two constants have been defined in <stdlib.h> which can be used: EXIT_FAILURE EXIT_SUCCESS The statement that we have typically seen at the end of our main() function: return 0; is equivalent to return EXIT_SUCCESS; Calling a function We have already seen how to call functions. Previously all of the functions we called were from the standard library, whereas now we are writing the definitions of the functions ourselves. Consider the TimesTwo() function again: int TimesTwo(int a) { int result; result = a + a; return result; } A call to this function would look like: int result; result = TimesTwo(4); The value 4 would be passed into the function, and the result 8 would be returned. The return value is then assigned to the variable result so that we can do something with it (perhaps use it in another calculation, or print it to the screen). When we call a function like this, we think of the function as a black box, as previously mentioned. The diagram below illustrates how we would visualise the TimesTwo() function as a black box:

TimesTwo()

Lecture 18

85

Course Manual (C Programming)

EngGen 131 S2 2013

Notice that when we are thinking about using the TimesTwo() function, we don't worry about how it is implemented. For example, the TimesTwo() function could be rewritten like this: int TimesTwo(int a) { return 3*a - a; } and it would make no difference to the code that called the function. In other words: int result = TimesTwo(4); would still assign the value 8 to the variable result. Let's now look at the complete definition of a program which uses the TimesTwo() function.

#include <stdio.h> int TimesTwo(int a); int main(void) { int a, b; a = 10; b = TimesTwo(a);

function prototype declaration

printf("%d times two is %d\n", a, b); return 0; } int TimesTwo(int a) { return 3*a - a; }

The output from this program is: 10 times two is 20 Notice that there is a function prototype declaration as well as the definition of the TimesTwo() function. Lets now consider what happens in memory when this program is executed.

Lecture 18

86

Course Manual (C Programming)

EngGen 131 S2 2013

Remember the visualisations of memory from Lecture 14? They looked like this:

The main() function is always executed when the program starts, so we always visualise that in memory. When another function is called, memory is allocated to store information about that function call specifically, space is allocated for the variables declared in the function and for any parameters passed to the function. Information about the calling function is also stored, so that when the function returns the program can return back to the calling statement. So as an example, when the main() function calls the TimesTwo() function, we would visualise this as follows: For now, the most important thing to notice here is that the variable declared inside the TimesTwo() function is distinct from the variable of the same name (a) declared inside the main() function. The TimesTwo() function only has access to the variable (a) that we visualise inside the box for its allocated memory. The main() function only has access to the variables (a and b) that we visualise inside the box for its allocated memory. As we will see shortly, these are called local variables.
Lecture 18

87

Course Manual (C Programming)

EngGen 131 S2 2013

Prototypes The function prototype declaration tells the compiler that we intend to call a function somewhere in our program that is called TimesTwo(), that takes a single parameter of type int, and that returns a value of type int. The prototype allows the compiler to check that when we do actually call the function (in this case from the main() function), we have done so correctly. No parameters If the function does not take any input parameters, then when we call the function we leave the parentheses empty. For example, consider the following program: #include <stdio.h> void PrintGreeting(void); int main(void) { PrintGreeting(); PrintGreeting(); return 0; } void PrintGreeting(void) { printf("Hello there!\n"); } The output would be: Hello there! Hello there! Notice that the function calls, in the main() function, have empty parentheses. The function definition uses the word void to indicate that the function does not take any inputs.

function calls

function definition

Lecture 18

88

Course Manual (C Programming)

EngGen 131 S2 2013

Multiple parameters If the function takes more than one input parameter, the parameters are separated by commas in the function definition. When we call the function, we specify the inputs in the corresponding order as the parameters, and separate them with commas.

For example, consider the following program: #include <stdio.h> int SameWholeNumber(int x, double d); function call int main(void) { if (SameWholeNumber(10, 10.9)) { printf("same\n"); } return 0; } int SameWholeNumber(int x, double d) { int truncated = (int)d; return x == truncated; }

function definition

When the SameWholeNumber() function is called, the value 10 is assigned to the input parameter x and the value 10.9 is assigned to the input parameter d, before the function body executes. In this case, the function would return 1 (or true, because the whole part of the double value 10.9 is 10) and so the output of the program would be: same Notice that in the function call, the input values are separated by a comma. Also, the order is important if we made the following function call:

if (SameWholeNumber(1.7, 3))

the compiler would issue a warning, because the input value 1.7 (a floating point value) cannot be assigned to the first input parameter x (of type int) without losing data:

warning: 'function' : conversion from 'double' to 'int', possible loss of data

Lecture 18

89

Course Manual (C Programming)

EngGen 131 S2 2013

Variable scope
The scope of a variable is the section of code in which it is valid to refer to or access the variable. Local variables A local variable is a variable that is defined at the start of a block of code, for example at the start of a function definition. The scope of a local variable is just the block of code in which it is defined. In the diagram below, the variable x is local to the FunctionOne() function. It can only be referred to from within the body of FunctionOne().

int FunctionOne(int a) { int x; the scope of variable x is just the body of ... the function FunctionOne() ... } int FunctionTwo(int a) { ... variable x cannot be referred to from ... within FunctionTwo() }

A local variable exists only while the function is executing. When the function is called and control enters the function, memory is allocated for the local variable. When the function exits, the memory for the local variable disappears. For this example, we can visualise memory being organised as in the diagram on the left (arbitrary values have been assigned to the variables). Local variables, appearing inside the box representing the memory allocated for the function call, are only accessible by code defined in that function definition. So in this case, the local variable x is only accessible by code defined in FunctionOne(). The local variable a in the memory for FunctionOne() is only accessible by code defined in FunctionOne(). The local variable a in the memory for FunctionTwo() is only accessible by code defined in FunctionTwo().
90

Lecture 18

Course Manual (C Programming)

EngGen 131 S2 2013

Global variables A global variable is a variable that is defined outside of any function. The scope of a global variable begins at the declaration of the variable, and ends at the end of the source file. In the diagram below, the variable x is a global variable and it is valid to refer to it from within either FunctionOne() or FunctionTwo().

int x = 10; int FunctionOne(int a) { ... ... } int FunctionTwo(int a) { ... ... }

the scope of variable x is the entire source file

A global variable exists in memory for as long as the program is executing. For this example, we can visualise memory being organised as in the diagram below.

Note the global variable, x, is in a separate section of memory to the memory allocated for the function calls. The global area of memory is accessible by all functions in the program so code defined in FunctionOne() and FunctionTwo() can access this global variable. When FunctionOne() finishes executing, the memory that has been allocated for it will disappear (it is returned to the system, where it can be reused for subsequent function calls). This means that local variables for FunctionOne() will no longer exist when the function finishes. However the global variable exists in memory for as long as the program is running its value persists even when functions start and finish.
Lecture 18

91

Course Manual (C Programming)

EngGen 131 S2 2013

Local or global? Use local variables whenever possible. The main advantage of using a global variable is information sharing every function can access the global variable and so they are all able to share its value. However, as a consequence, this creates code which can be harder to maintain as when one function changes the value of the global variable, this can affect the way another function which relies on the variable behaves. Remember that ideally a function should be like a black box, so it is a good idea to try to avoid having the implementation of the function relying on a global variable. Importantly, as the size of our programs increase, the use of global variables makes the code harder to read and understand. In general, it is a good idea to use local variables whenever possible. We can pass the value of a local variable from one function to another via a parameter. There are two situations where it makes sense to use a global variable instead of a local variable (but these are guidelines rather than set rules): if the variable is critical to the program and is used by every function but it is defined locally in the main() function, then every other function definition will require a parameter so that the value of the variable can be passed to it. In this case, the code may be cleaner if the variable is made global and so the parameter is not needed in every function. if the storage required for that variable is very large (for example, it might be an array with many elements), then there may not be room on the stack (where space for local variables is allocated when a function is called). Global variables are stored in a separate part of memory which is not as limited in size as the stack.

This second point can lead to subtle errors if you are not careful. Compare the following versions of a program that uses a very large array (100,000,000 elements): Uses local variable (this is likely to crash due to an overflow of local storage, which is limited)
#define SIZE 10000 int main(void) { int imageData[SIZE][SIZE]; int i, j; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { imageData[i][j] = 0; } } return 0; } }

Uses global variable (this version will work as global storage is not limited in the same way)
#define SIZE 10000 int imageData[SIZE][SIZE]; int main(void) { int i, j; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { imageData[i][j] = 0; } } return 0;

The version on the left will not work correctly. When we try to run the program, it will crash because there is not enough local storage (to store the memory for the very large local variable imageData). The version on the right will work, because memory for storing global variables is not allocated on the stack and is not limited in the same way.

Lecture 18

92

Course Manual (C Programming)

EngGen 131 S2 2013

Call by value Parameters in a function are exactly like local variables, except that their values are initialised when the function is called. The value from the calling statement is assigned to the parameter which therefore has its own copy of the value. For example, consider the following code: int main(void) { int result; int a = 10; result = TimesTwo(a); printf("a = %d\n", a); printf("result = %d\n", result); return 0; } int TimesTwo(int a) { a = a + a; return a; } The output when this code is executed is: a = 10 result = 20 The value of a is still 10 despite the changes made to the variable a in the TimesTwo() function. Remember that when a function is called, memory is allocated for storing any local variables and any parameters the function defines. Lets visualise the way memory changes as this program is executed. To begin with, as soon as the main() function is entered, space is allocated for its two local variables: Initially, only variable a is given a value. The variable result will initially store a garbage value which we visualise as ???. When the statement: result = TimesTwo(a); is executed, the first thing that happens is the function call takes place. The assignment to the variable result cannot happen until the function call has completed and returned a value.
Lecture 18

93

Course Manual (C Programming)

EngGen 131 S2 2013

When the TimesTwo() function is called, space is allocated for the local variables and parameters of the function. In this case it only has the parameter a. The value from the calling statement (the variable a in the main() function) is assigned to the parameter (the parameter a in the TimesTwo() function). This is equivalent to the assignment statement: a = 10;

Now, the body of the TimesTwo() function executes:

The TimesTwo() function contains just one line of code: a = a + a; This modifies the value of the local variable a in the TimesTwo() function. In particular, notice it has no effect on the local variable of the same name in the main() function.

Next, the return statement is executed: return a; All of the memory allocated for the TimesTwo() function disappears as soon as the function returns. The value, 20, that is returned by the function essentially replaces the function call expression in the calling statement: 20 result = TimesTwo(a);

Lecture 18

94

Course Manual (C Programming)

EngGen 131 S2 2013

Finally, once the value 20 has been assigned to the variable result, we would visualise memory as follows:

The TimesTwo() function no longer occupies memory. When it returns, the memory it was occupying is returned to the system and can be reused by subsequent function calls.

In summary: any parameter (whether it is an int, double or even a structure), is just like a local variable to the function in which it is defined its value is initialised by assignment when the function is called, and any changes made to the parameter are local to the function

Arrays as parameters There is one exception to the previous rule about parameters being local variables and that is when the parameter is an array. This will be discussed in more detail later, but for now it will be useful to see what an array parameter looks like in a function definition. Consider the following code, which creates an array of ints, and then calls a function to calculate the sum of all of the elements in the array: int sum; int values[5] = {2, 6, 4, 1, 3}; sum = SumArray(values, 5);

What would the prototype declaration for this SumArray() function look like? It could look like this: int SumArray(int nums[5], int numElements);

Lecture 18

95

Course Manual (C Programming)

EngGen 131 S2 2013

However, when an array parameter is defined, the number of elements does not need to be specified in the square brackets. Therefore, the prototype could look like this:

int SumArray(int nums[], int numElements);

In fact, this is typically how it is done the square brackets are empty (we will see another, more common, way of declaring an array parameter which does not use square brackets when we learn about pointers). How could this function be defined? Here is one possible definition:

int SumArray(int nums[], int numElements) { int i; int sum = 0; for (i = 0; i < numElements; i++) sum += nums[i]; return sum; } Two-dimensional arrays as parameters When a two-dimensional array is passed to a function as a parameter, the compiler does need to know something about the dimensions of the array. In order to calculate where in memory each element of the array is, the compiler must at the very least know how many columns the two-dimensional array has. For example, consider the following code which passes a two-dimensional array of ints to a function called SumArray():

int sum; int values[4][3] = { {0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {3, 4, 5} }; sum = SumArray(values, 4, 3);

In this case, the prototype for the function must at least specify how many columns the array has: int SumArray(int nums[][3], int rows, int cols);

Although it is often clearer, when passing two-dimensional arrays as parameters to functions, to include both dimensions:

int SumArray(int nums[4][3], int rows, int cols);


Lecture 18

96

Course Manual (C Programming)

EngGen 131 S2 2013

One possible definition of this SumArray() function is as follows: int SumArray(int nums[4][3], int rows, int cols) { int i, j; int sum = 0; for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) sum += nums[i][j]; return sum; }

Lecture 18 Summary Program


We can now write programs that are composed of individual functions. Consider the following program, written as a single main() function, that tests the pseudo-random number generator to see how evenly the values it generates are distributed. It prints the values in the array before and after the loop executes: #include <stdio.h> #include <stdlib.h> #define VALUES 10 #define NUM_RANDOMS 1000000 int main(void) { int frequency[VALUES] = {0}; int i; /* Print the values in the array */ for (i = 0; i < VALUES; i++) { printf("%d: %d\n", i, frequency[i]); } printf("\n"); /* Generate and record the random numbers */ for (i = 0; i < NUM_RANDOMS; i++) { frequency[rand() % (VALUES)]++; } /* Print the values in the array */ for (i = 0; i < VALUES; i++) { printf("%d: %d\n", i, frequency[i]); } printf("\n"); return 0; }
97

The output from this program may be as follows: 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 0 0 0 0 0 0 0 0 0 0 99854 99899 100022 100177 100055 99729 99602 100653 100269 99740

Lecture 18

Course Manual (C Programming)

EngGen 131 S2 2013

The same program could be decomposed into functions. Take, for example, the code that prints the elements of the array. At the moment, this code is repeated in two separate places in the main() function (before and again after the random number generation). This is poor style, because if we wanted to change the way the array was printed then we would have to modify the code in two places. We could therefore define a function to print the array: void PrintArray(int values[], int numValues) { int i; for (i = 0; i < numValues; i++) { printf("%d: %d\n", i, values[i]); } printf("\n"); } Likewise, we could define a function to calculate a random number between an upper and lower bound to make the expression that is used to index the array more readable. The complete code is: #include <stdio.h> #include <stdlib.h> #define VALUES 10 #define NUM_RANDOMS 1000000 void PrintArray(int values[], int numValues); int RandomBetween(int a, int b); int main(void) { int frequency[VALUES] = {0}; int i; for (i = 0; i < NUM_RANDOMS; i++) { frequency[RandomBetween(0, VALUES-1)]++; } PrintArray(frequency, VALUES); return 0; } void PrintArray(int values[], int numValues) { int i; for (i = 0; i < numValues; i++) { printf("%d: %d\n", i, values[i]); } printf("\n"); } int RandomBetween(int a, int b) { return (rand() % (b-a+1)) + a; } the function definitions the function prototype declarations

Lecture 18

98

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 19: Pointers and Strings (Part 1)


Pointers are one of the most powerful programming constructs in the C language. A pointer is a variable that stores the address in memory of some other variable. This allows us to have variables that "point to", or "refer to", other variables. Pointers are critical for allocating memory dynamically although we arent going to be learning about dynamic memory allocation in this course, we will be learning the basics of pointers. We have seen string constants appearing in our printf() statements. A string constant is a collection of characters surrounded by quotation marks: "this is a string constant" Other types of constants, like char, int and double constants have corresponding variable types that are used to store them. There is no "string" type in C. Instead, a string in C is stored as an array of type char. In this lecture, we will look at some of the basic ideas behind pointers and strings.

Learning outcomes
After this lecture, you should be able to: declare a pointer variable and initialise it using the address operator (&) dereference an initialised pointer variable using the dereference operator (*) visualise a pointer variable as a box containing an arrow initialise a null pointer, and test whether a pointer is null use the -> operator to access the fields of a structure via a pointer variable declare an array of chars to represent a string, and initialise that array using the scanf() function define and call a function where the parameters are pointers

MATLAB Comparison
Pointer variables are not supported in MATLAB.

Pointers
A pointer is a variable that stores an address in memory. So far, when we have visualised a variable, we have imagined a section of memory (which we thought of as a box) that stores some value. That section of memory is located at some address in the memory of the computer.

Lecture 19

99

Course Manual (C Programming)

EngGen 131 S2 2013

The diagram below shows a variable called a that stores the value 10: variable identifier

value stored

10
0x12FF60

address in memory of the variable a

A pointer is a special type of variable which can store an address, like 0x12FF60. A pointer variable that stored the address 0x12FF60 would be said to "point to", or "refer to", the variable a. For example, we would visualise a pointer variable, p, that stores the address of the variable a, as below: value stored

0x12FF60
0x1300F0

address in memory of the variable p

Pointers are a powerful part of the C language, and allow us to do some interesting things. However we must be careful when we are working with them, as they are commonly the source of errors when not used correctly. Visualising a pointer When we visualise a pointer variable we don't usually concern ourselves with the actual value of the address that it stores. Instead, we draw a box (as usual) which contains an arrow that originates from the centre of the box and points to some other variable. In the diagram below, the variable a is a variable of type int that currently stores the value 10, and p is a pointer variable that stores the address of the variable a.

10

Lecture 19

100

Course Manual (C Programming)

EngGen 131 S2 2013

Declaring a pointer To declare a pointer variable, we use an asterisk "*": int *p; This creates a variable called p, which is of type "pointer to int". In fact, we say that variable p is of type "int *". Initially, when we first declare a pointer variable, it does not point to anything. We would visualise this variable as follows:

p x x x

The "x x x" indicates that this variable has not been initialised, and reminds us that we should not use the pointer until we have initialised it. Another very common term for an uninitialised pointer is a "bad pointer".

Initialising a pointer To initialise a pointer, we can use the address operator, &. When applied to a variable, the address operator evaluates to the address of the variable exactly what we want to store in a pointer variable. Consider the following code: int a = 10; int *p; p = &a;

The pointer variable p is now initialised to store the address of the variable a. We would visualise this as in the diagram below:

10

Lecture 19

101

Course Manual (C Programming)

EngGen 131 S2 2013

Pointer dereferencing The pointer dereference operator is the asterisk, "*". It is a unary operator which appears on the left hand side of a pointer variable. It accesses the variable that the pointer points to. For example, we can dereference the pointer p in the previous diagram with the following statement: int result = *p;

This would store the value 10 in the variable result. Similarly, we can modify the value of variable a by assigning a new value to *p: *p = 23;

This would assign the value 23 to the variable a, as shown in the diagram below:

23

Warning be careful The most common error with pointers is to try to dereference a bad pointer, that is, a pointer which has not been initialised. The following code will corrupt some random part of memory (wherever the bad pointer happens to point): int *p; *p = 23; (this is wrong) this pointer is bad, and should not be dereferenced

p x x x

By assigning the value 23 to *p before we initialise p, we will overwrite whatever memory the bad pointer happens to point to. This may cause our program to crash immediately, or even worse, it may be much later that the problem becomes apparent. The golden rule is: make sure you never dereference a bad pointer.

Lecture 19

102

Course Manual (C Programming)

EngGen 131 S2 2013

Example Let's look at another example involving pointers. Pay attention to how the variable values, and the pointer values change.

int a = 1; int b = 2; int c = 3; int *p; int *s;

a b c

1 x x x p 2 x x x s 3

Before we initialise p and s, they are bad pointers and so we visualise them as boxes containing "x x x"

a p = &a; s = &c; b c

1 p 2 s 3

We have used the address operator "&" to initialise the pointers

a b c

3 p 2 s 3

*p = *s;

Here we have dereferenced both pointers, and the value stored in the variable that s points to is assigned to the variable that p points to. Essentially this statement does exactly the same thing as if we had done: a = c;

Lecture 19

103

Course Manual (C Programming)

EngGen 131 S2 2013

a b c

3 p 2 s 3

p = s;

Notice that this has quite a different result to the previous statement. No dereferencing is done, so this statement assigns the value stored in pointer s (which is the address of variable c) to the pointer p. Essentially this makes p point to the same variable that s points to.

a b c

3 p 2 s 7

*p = 7;

This stores the value 7 in the variable that p points to. Notice that after this assignment statement, the value of both *p and *s is 7. This is referred to as sharing.

a b c

3 p 14 s 7

b = *p + *s;

The value of *p is added to the value of *s, and the result is stored in the variable b. We must always appreciate the distinction between a pointer and what it points to. For example, consider the following statement in relation to the previous diagram: s = 3; (this is wrong)

This is wrong because we cannot assign a value of type int to a variable of type "pointer to int". Similarly, consider the statement: a = p; (this is wrong)

Again, this is wrong because we can't assign a value of type "pointer to int" to a variable of type int. The types on either side of an assignment statement should always match.
Lecture 19

104

Course Manual (C Programming)

EngGen 131 S2 2013

Null pointers
There is a special value that a pointer can have when we want to indicate that it is not pointing to anything. That value is NULL. A pointer that stores the value NULL is said to be a null pointer. The constant NULL is predefined as part of the standard library, and actually appears in a number of the standard header files, including <stdio.h> and <stdlib.h>. The value NULL can be assigned to a pointer variable in the usual way: int *p; p = NULL; We typically visualise a null pointer as a box that has a line through it, like this:

It is also possible to use the integer constant 0 directly to refer to the null pointer. So p = 0; would also set p to be a null pointer. It is an error to try to dereference a null pointer. For example, the following code: int *p = NULL; printf("%d\n", *p); will cause our program to crash. It is therefore quite common to explicitly use a test before dereferencing any pointer which may be null: if (p != NULL) printf("%d\n", *p); In fact, because any non-zero value represents the boolean true, and because the value NULL is equal to zero, the above test is equivalent to: if (p) printf("%d\n", *p); Usefulness of null pointers Typically, we use a null pointer to indicate that a pointer variable is not currently pointing to anything valid. It is common to have a pointer in a program which points somewhere useful some, but not all, of the time. When the pointer is not pointing anywhere useful, we can use the value NULL to indicate this, and unlike a bad pointer, we can easily test to see if the pointer is null.
105

Lecture 19

Course Manual (C Programming)

EngGen 131 S2 2013

Pointers to structures
Consider the following structure type: typedef struct { int x; int y; } Point;

In the same way that we can declare a variable of this type: Point location;

x location y

we can also declare a pointer to a variable of this type: Point *locationPtr;

locationPtr

x x x

as with any pointer variable, locationPtr will initially be a bad pointer until we explicitly initialise it: locationPtr = &location; x locationPtr location y

To access the fields of a structure through a variable like location, we use the "." operator:

location.x = 10; location.y = 20; location

x y

10 20

Lecture 19

106

Course Manual (C Programming)

EngGen 131 S2 2013

To access the fields of a structure through a pointer variable, we can explicitly dereference it: (*locationPtr).x = 30; (*locationPtr).y = 40; x locationPtr y
40 30

Note that the parentheses above, around the *locationPtr, are necessary because the "." operator binds more tightly (i.e. has a higher precedence) than the "*" operator. Pointers to structures are quite common, and there is a special operator that can be used to access the fields of a structure through a pointer variable, rather than using the explicit dereference as shown above. This operator consists of the following two symbols: "->". locationPtr->x = 30; locationPtr->y = 40;

http://xkcd.com/

Lecture 19

107

Course Manual (C Programming)

EngGen 131 S2 2013

Strings
Strings in C are stored as arrays of type char.

greeting

'h' 'e' 'l' 'l' 'o' '\0'


0 1 2 3 4 5

the characters that make up the string "hello" are stored in the elements of the array

the end of the string is indicated by the null character

The end of the string is denoted by a special character called the null character. The null character has an ASCII code of 0, and is represented by the character constant '\0'. Whenever we write a string constant in our source code, like: char greeting[6] = "hello"; the compiler automatically creates the appropriate array of chars, and terminates it with the null character. Notice that although there are only 5 characters in the word, the size of the array here is 6 to accommodate the null character. We could have set the size of the array to be larger and it would make no difference it is the location of the null character that determines the end of the string. Any other elements in the array after the null character are simply ignored. In fact, we are not required to explicitly include the size of the char array when we are initialising it with a string constant. The compiler will calculate that 6 elements are needed and will allocate the right amount of space, so the following declaration is just fine (and quite common): char greeting[] = "hello";

We use the printf() function to print out a string using the %s modifier: printf("%s", greeting); Initialising a string with scanf() We can use the scanf() function, and the %s conversion specifier, to read a string typed in by the user at the keyboard. For example, consider the following: char name[100]; printf("Please enter your name: "); scanf("%s", name); This will initialise the character array name, to whatever text is typed at the keyboard. Remember that to initialise a variable, the scanf() function requires a pointer to that variable. The array variable name is a pointer to the first element in the array.
Lecture 19

108

Course Manual (C Programming)

EngGen 131 S2 2013

Be aware that the %s conversion specifier will only read the text typed at the keyboard up to the first white-space character (a space, tab or newline). For example, consider the following program: char name[100]; printf("Please enter your name: "); scanf("%s", name); printf("Hello %s", name); When the text "none of your business" is entered, the output would be: Please enter your name: none of your business Hello none

because the input stops being processed after the first space character in the text. If you want to read all of the text that is typed at the keyboard into a character array, then rather than using scanf(), you can use a different function called gets(). The gets() function takes the character array as a parameter, and reads the input typed at the keyboard until a new line character is encountered: char name[100]; printf("Please enter your name: "); gets(name); printf("Hello %s", name);

This time, the output would be: Please enter your name: none of your business Hello none of your business Buffer overflows A notorious vulnerability in C programs is that of buffer overflow. Take the example above the array is only 100 characters long. If the input string is longer than that, the additional characters will overwrite the bytes in memory that appear after the array. Not only can these bugs be hard to find for a programmer (variables in your program that happen to be located in memory after the array can be randomly corrupted), but these are potential security risks. Malicious users may exploit these buffer overflows, and can even modify the way the program behaves. Both scanf() and gets() are vulnerable to buffer overflows which is why the compiler warns us about using them. There are (non-standard) functions available in the Visual Studio compiler that are not vulnerable to overflows. Fortunately, we wont be writing code in this course that anyone will care to exploit!

Lecture 19

109

Course Manual (C Programming)

EngGen 131 S2 2013

Pointers as parameters
When we pass a parameter to a function, that parameter is passed by value in other words, the function gets a copy of the value of the parameter. Parameters are just like local variables inside a function, so any changes made to a parameter does not affect any variables in the code from where the function was called. This means that the following Swap() function, which is supposed to swap the values that are stored in the two parameters, will not work: #include <stdio.h> void Swap(int x, int y); int main(void) { int a = 10; int b = 20; Swap(a, b); printf("a=%d, b=%d\n", a, b); return 0; } void Swap(int x, int y) { int temp = x; x = y; y = temp; }

When the Swap() function is called, the values of a and b (which are local to the main() function) are copied into the parameters x and y. Any changes made to the values of x and y within the body of the Swap() function do not affect the values stored in a and b in the main() function. main() a 10 the values of a and b are copied into the parameters x and y Swap() x 10

20

20

Therefore, the output of this program is: a=10, b=20


110

Lecture 19

Course Manual (C Programming)

EngGen 131 S2 2013

Now that we have seen how pointers work, we can modify the Swap() function to take two parameters of type "pointer to int", as follows: void Swap(int *x, int *y) { int temp = *x; *x = *y; *y = temp; } Now, when we call the Swap() function, we need to initialise the pointer parameters x and y by passing them the addresses of the variables a and b: Swap(&a, &b);

main() a 10

Swap()

20

This time, the program will actually swap the values that are stored in a and b, and the output will be: a=20, b=10

http://xkcd.com/

Lecture 19

111

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 19 Summary Program


We can now write programs that use pointers to manipulate data stored in memory. We have also looked at how to pass pointers as parameters to functions, and how to refer to structures via pointers. For example, the following program simulates the manufacturing of a set of 10 items each of which has a random weight. The program first displays all of the items in the order that they were manufactured, and then displays the items in increasing order of weight. When the program runs, the output is as follows: Manufacturing order: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: (order of manufacture: Sorted (order (order (order (order (order (order (order (order (order (order by of of of of of of of of of of weight: manufacture: manufacture: manufacture: manufacture: manufacture: manufacture: manufacture: manufacture: manufacture: manufacture:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 5, 3, 0, 9, 1, 7, 6, 8, 4,

weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight: weight:

111) 117) 104) 110) 129) 104) 118) 118) 122) 114) 104) 104) 110) 111) 114) 117) 118) 118) 122) 129)

To sort the items by order of weight, a function called SwapLightest() is used. This function takes a pointer to an Item in an array as a parameter, and swaps this Item with any element that is lighter than, and to the right of it, in the array. The main() function calls this SwapLightest() function once for each element in the array. File: main.c #include <stdio.h> #include <stdlib.h> #define NUM_ITEMS 10 typedef struct { int order; int weight; } Item; void PrintItems(Item items[], int num); void SwapLightest(Item *leftPos, int num); void Swap(Item *a, Item *b);
Lecture 19

112

Course Manual (C Programming)

EngGen 131 S2 2013

/* This program creates a set of items, each with a random weight, and then displays the items in the order they were initialised, and also sorted by their weights */ int main(void) { Item items[NUM_ITEMS]; int i; for (i = 0; i < NUM_ITEMS; i++) { items[i].order = i; items[i].weight = rand() % 30 + 100; } printf("Manufacturing order:\n"); PrintItems(items, NUM_ITEMS); for (i = 0; i < NUM_ITEMS; i++) { SwapLightest(items+i, NUM_ITEMS-i); } printf("Sorted by weight:\n"); PrintItems(items, NUM_ITEMS); return 0; } /* Examines all num elements to the right of leftPos, and swaps any element with a smaller weight with the element currently stored at position leftPos */ void SwapLightest(Item *leftPos, int num) { Item *next; next = leftPos + 1; while (next < (leftPos + num)) { if (next->weight < leftPos->weight) { Swap(leftPos, next); } next++; } } void Swap(Item *a, Item *b) { Item temp = *a; *a = *b; *b = temp; } void PrintItems(Item items[], int num) { int i; for (i = 0; i < num; i++) { printf("(order of manufacture: %d, weight: %d)\n", items[i].order, items[i].weight); } printf("\n"); }
Lecture 19

113

Course Manual (C Programming)

EngGen 131 S2 2013

The loop that actually drives the sorting is this one: for (i = 0; i < NUM_ITEMS; i++) SwapLightest(items+i, NUM_ITEMS-i); The first time through the loop, when the SwapLightest() function is called the first parameter is a pointer to the Item at position 0 of the array (i.e. the left-most Item) and the second parameter is the value NUM_ITEMS (i.e. the number of Items in the array).

The SwapLightest() function then examines all elements to the right of leftPos:

leftPos

next

order

order

1 ....

order

weight 111
0

weight 117
1

weight 114
9

Swap() Every element that next points to which has a smaller weight value than the element that leftPos points to, is swapped with leftPos. When this loop within the SwapLightest() function finishes, the Item with the smallest weight will be stored in index position 0 of the array. Then the main loop: for (i = 0; i < NUM_ITEMS; i++) SwapLightest(items+i, NUM_ITEMS-i);

executes again. The second time through this loop, the second smallest Item in the array will be shifted to index position 1. The third time through this loop, the third smallest Item in the array will be shifted to index position 2, and so on. When this main loop finishes, the Items will be sorted into increasing order.

Lecture 19

114

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 20: Project Discussion

Lecture 20

115

Course Manual (C Programming)

EngGen 131 S2 2013

http://xkcd.com
Lecture 20

116

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 21: Pointers and Strings (Part 2)


We have already covered the basics of pointers and we know that a pointer is a variable that stores an address in memory. We will now see that we can perform arithmetic with pointers and that array variables are, in fact, just pointers. We have also seen that a string in C is simply a null terminated array of characters. We will look at some basic functions that we can perform with strings, and we will look at how to pass strings as parameters to functions.

Learning outcomes
After this lecture, you should be able to: explain the result of applying the plus operator (+) to a pointer variable use pointers and array variables interchangeably describe the effect of modifying the elements of an array that is passed as a parameter to a function write code using several of the string functions defined in string.h use the type char * when working with strings and when passing strings as parameters to functions

MATLAB Comparison
A string in MATLAB is actually a vector whose elements are the ASCII codes for the characters. A string is specified using single quote marks:
MATLAB

>> S = 'MATLAB';

String operations, like calculating the length of the string or concatenating strings together, is achieved by performing the appropriate vector operations. In C, the standard library provides several functions for working with strings. These functions are declared in the header file <string.h>.

Lecture 21

117

Course Manual (C Programming)

EngGen 131 S2 2013

Pointer arithmetic
When we declare an array: int numbers[8] = {1, 2, 3, 4, 5, 6, 7, 8}; we can visualise this array stored in memory as below:

0x220

0x224

0x228

0x22C

0x230

0x234

0x238

0x23C

numbers

1
0

2
1

3
2

4
3

5
4

6
5

7
6

8
7

The elements of the array are stored in contiguous memory locations. Notice that in the above diagram, the memory locations (in hexadecimal) of each element are also given (even though we usually would not include these in such a diagram). Each element is an int, and therefore takes up four bytes in memory. Now that we know about the address operator, we can talk about the relationship between the addresses of the elements in the array. For example, the address of the first element in the array could be calculated as: &numbers[0] and in this example, this is 0x220. This is exactly the same as if we just had: numbers The array variable, numbers, is simply a pointer to the first element in the array. The type of the variable numbers is a "pointer to int". numbers &numbers[0]

equivalent

What about the addresses of the other elements in the array? The address of the sixth element in the array, numbers[5], is given by: &numbers[5] We can see from the diagram that the address of this element is 0x234. This is calculated by taking the size of each element in the array (in this case 4), multiplying it by the index position of the element that we want (in this case 5) and adding the product to the address of the first element in the array (in this case 0x220), all in hexadecimal, or base-16, arithmetic. Fortunately, all of these address calculations are handled for us by the compiler, so we do not need to worry about them.

Lecture 21

118

Course Manual (C Programming)

EngGen 131 S2 2013

Pointer "+" operator The + operator can be applied to a pointer variable. The result is also a pointer, which points to a new address. The value of the new address depends on the value that was added to the original pointer, and the type of the pointer.

For example, consider the declarations below: int *p; int numbers[8] = {1, 2, 3, 4, 5, 6, 7, 8};

We can visualise these variables as in the diagram below:

p x x x

numbers

1
0

2
1

3
2

4
3

5
4

6
5

7
6

8
7

Let's initialise the pointer as follows: p = &numbers[1];

The diagram now becomes:

numbers

1
0

2
1

3
2

4
3

5
4

6
5

7
6

8
7

Lecture 21

119

Course Manual (C Programming)

EngGen 131 S2 2013

If we add an integer, say n, to a pointer variable, the result is a pointer to the element at the address which is (n * element size) bytes past the original pointer value. For example, the expression: p + 5 evaluates to a pointer to the element 5 places further on in the array, in this case the element at index 6. We can treat this like any pointer variable for example we can dereference it and assign a new value to it: *(p + 5) = 0;

this will update the array as follows:

numbers

1
0

2
1

3
2

4
3

5
4

6
5

0
6

8
7

We can modify a pointer using arithmetic. For example, the statement: p++; which is equivalent to p = p + 1; will make the pointer p point to the next element in the array, as shown below:

numbers

1
0

2
1

3
2

4
3

5
4

6
5

0
6

8
7

Lecture 21

120

Course Manual (C Programming)

EngGen 131 S2 2013

Pointers and arrays


An array variable is essentially a pointer. It points to the first element in the array. For example, consider the declaration: int numbers[8] = {1, 2, 3, 4, 5, 6, 7, 8};

We can refer to the elements of this array as: numbers[0], numbers[1], numbers[2], ... or equivalently, as: *numbers, *(numbers+1), *(numbers+2), ...

numbers

1
0

2
1

3
2

4
3

5
4

6
5

0
6

8
7

So what's the difference between numbers[3] and (numbers + 3)?

The expression: numbers[3] is the int value stored at index 3 in the array numbers, whereas: numbers + 3 is a pointer to the element numbers[3].

Expression numbers[3] numbers + 3

Type int pointer to int

Evaluates to the value of the element at index 3 a pointer to the element at index 3

Any array expression that can be written with the "[]" notation, can also be written using the pointer "+" notation and a pointer dereference "*". For example, numbers[3] = 10; is equivalent to *(numbers + 3) = 10;
121

Lecture 21

Course Manual (C Programming)

EngGen 131 S2 2013

Array to pointer assignment We can assign an array variable to a pointer variable. For example: int numbers[8] = {1, 2, 3, 4, 5, 6, 7, 8}; int *p; p = numbers; This makes the pointer variable p point to the first element (at index 0) of the array numbers. It is equivalent to assigning: p = &numbers[0];

Subscripting on pointers As far as the compiler is concerned, it does not really distinguish between array variables and pointer variables. We can use the [] notation with a pointer variable in exactly the same way that we use it with an array variable. So, given the same declarations as before: int numbers[8] = {1, 2, 3, 4, 5, 6, 7, 8}; int *p; p = numbers; The following are all equivalent ways of referring to the int value stored at index position 3 in the array numbers: numbers[3] *(p+3) p[3]

Arrays as parameters
When we pass an array to a function as a parameter, the entire array is not copied. This would be very inefficient as arrays can be quite large. Instead, the parameter stores a pointer to the array. Consider the following code. An array called values is declared and initialised, and a function called SumElements() is called which returns the sum of all the elements in the array: int values[8] = {1, 2, 3, 4, 5, 6, 7, 8}; int sum; sum = SumElements(values);

How would this SumElements() function be defined? What is the type of the parameter?
122

Lecture 21

Course Manual (C Programming)

EngGen 131 S2 2013

We now know that an array variable is really just a pointer to the first element of the array. Therefore, the prototype of the SumElements() function could be declared in either of the following two ways: int SumElements(int numbers[]); int SumElements(int *numbers); Either way, the body of the SumElements() function would look something like this: { int i, sum; sum = 0; for (i = 0; i < 8; i++) { sum += numbers[i]; } return sum; } The compiler allows us to define the parameter to this SumElements() function either of the type "pointer to int" or of the type "array of int". Either way, when the function is called, the parameter numbers is going to store the address of the first element of the array. The number 8 which appears in the body of the SumElements() function is a bit inelegant. It means that we can only use this function to add up the first 8 elements of any array and it means that if the actual array has fewer than 8 elements then the program will crash. To resolve this problem, a function like SumElements() would be passed two parameters the array and the number of elements in the array. A more reusable version of the SumElements() function that does this is defined below: int SumElements(int *numbers, int numElements) { int i, sum; sum = 0; for (i = 0; i < numElements; i++) { sum += numbers[i]; } return sum; } Of course, because a function parameter stores a pointer to the first element of an array, any changes that are made to the array in the body of the function will affect the original array. For example, consider the IncrementArray() function defined as follows: void IncrementArray(int *numbers, int numElements) { int i; for (i = 0; i < numElements; i++) { numbers[i]++; } }
Lecture 21

123

Course Manual (C Programming)

EngGen 131 S2 2013

If we called this function from the main() function, as follows: int main(void) { int i; int values[8] = {1, 2, 3, 4, 5, 6, 7, 8}; IncrementArray(values, 8); for (i = 0; i < 8; i++) { printf("%d ", values[i]); } return 0; } the output would be: 2 3 4 5 6 7 8 9

String functions
C provides a number of functions for manipulating and working with strings. Prototypes for the string functions are defined in <string.h>. Several of the common string functions are described below: strcpy() The strcpy() function takes two parameters, and copies the string from the second parameter into the first. char greeting[] = "hello"; char copyOfGreeting[100]; strcpy(copyOfGreeting, greeting); Notice that the destination array must be allocated to be large enough to store the entire string. In this example, the destination array contains 100 elements, which is easily large enough. strcmp() The strcmp() function compares two strings alphabetically. If we call: strcmp(wordOne, wordTwo); the return value will be: 0 a negative number a positive number if wordOne is identical to wordTwo if wordOne comes before wordTwo in standard alphabetical ordering if wordOne comes after wordTwo in standard alphabetical ordering

Lecture 21

124

Course Manual (C Programming)

EngGen 131 S2 2013

strlen() The strlen() function returns the length, or number of characters, in a string. This does not include the null character (which is just an indicator of where the string ends). The following code: char word[] = "cat"; printf("%d", strlen(word)); would print the value: 3 strcat() The strcat() function appends one string onto the end of another. It is important that we have allocated enough space in the array to store all of the characters of the appended string. For example: char phrase[100] = "the quick brown"; char animal[] = "fox"; strcat(phrase, animal); printf("%s", phrase); would produce the output: the quick brownfox Notice that no space is appended between the two strings (we could explicitly include this if we wanted). Also notice that the array phrase, which has the elements of the string animal appended to it, has been allocated to be easily large enough to store the appended characters. char * An array variable is simply a pointer to the first element in the array. For this reason, it is very common to use the type "pointer to char" when working with strings. Consider the following code: char greeting[] = "hello"; char *p, *s; p = greeting; s = p;

greeting

'h' 'e' 'l' 'l' 'o' '\0'


0 1 2 3 4 5

In this case, both p and s point to the same string, so any changes made to the string through one pointer will also be seen by the other pointer.
Lecture 21

125

Course Manual (C Programming)

EngGen 131 S2 2013

We can use either the pointer variables, or the array variable to access the string, so the following statements: printf("%s", greeting); printf("%s", p); printf("%s", s); are all correct and would all produce the same output.

String as parameters If we pass a string to a function as a parameter, we are passing a pointer to the first element in the array. It is therefore common to declare the parameter, for a function that is passed a string, to be of type "pointer to char". For example, here is how we could define our own function for calculating the length of a string: int MyStrLen(char *string) { int i = 0; while (string[i] != '\0') { i++; } return i; } The function call: printf("%d", MyStrLen("correct")); would print the value: 7

Making use of pointer arithmetic, an alternative way of defining the MyStrLen() function would be as follows: int MyStrLen(char *string) { int i = 0; while (*string != '\0') { string++; i++; } return i; }

Lecture 21

126

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 21 Summary Program


We can now write programs that work with Strings. For example, the following program plays a simple game of hangman. It starts by selecting a random word from a dictionary file called "words.txt". The format of this file is as follows: n word 1 word 2 .... word n The first line of the file stores the number of words in the file, and the words themselves are stored one per line. For example, here is a small dictionary file: 4 apple banana orange pear The complete source code for this hangman program is below: #define _CRT_SECURE_NO_DEPRECATE #include #include #include #include <stdio.h> <stdlib.h> <time.h> <string.h>

#define DICTIONARY "words.txt" void GetRandomWord(char *w); void InitialiseGuess(char *w, int n); void ProcessOneGuess(char *w, char *g); int WordGuessed(char *g); int main(void) { char secretWord[100]; char guess[100]; int gameOver = 0; GetRandomWord(secretWord); InitialiseGuess(guess, (int)strlen(secretWord)); while (!gameOver) { ProcessOneGuess(secretWord, guess); gameOver = WordGuessed(guess); } printf("\nWell done - the word was: %s\n", secretWord); return 0; }
Lecture 21

127

Course Manual (C Programming)

EngGen 131 S2 2013

int WordGuessed(char *g) { int i; int countBlanks = 0; for (i = 0; g[i] != '\0'; i++) { if (g[i] == '_') countBlanks++; } return countBlanks == 0; } void ProcessOneGuess(char *w, char *g) { int i; char input[10]; for (i = 0; g[i] != '\0'; i++) { printf("%c ", g[i]); } printf("\nEnter guess: "); scanf("%s", input); for (i = 0; g[i] != '\0'; i++) { if (w[i] == input[0]) g[i] = input[0]; } } void InitialiseGuess(char *w, int n) { int i; for (i = 0; i < n; i++) w[i] = '_'; w[n] = '\0'; } void GetRandomWord(char *w) { FILE *fp; int numWords, pos, i; srand((unsigned int)time(NULL)); fp = fopen(DICTIONARY, "r"); fscanf(fp, "%d", &numWords); pos = rand() % numWords; for (i = 0; i < pos; i++) { fscanf(fp, "%s", w); } fscanf(fp, "%s", w); }

128

Lecture 21

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 22: Recursion


When we define the source code for a function to perform some task, we know we can make a call to another function to help solve part of the task. What happens if the function that is called is the function itself?

Learning outcomes
After this lecture, you should be able to: take a simple recursive definition (consisting of a base case and a recursive case) and convert that to a recursive function draw a diagram representing the function calls made when a given recursive function is called with a small input value work out the output of a program which calls a simple recursive function with a small input value

MATLAB Comparison
Recursive functions can easily be defined in MATLAB. The function definition simply needs to contain a call to itself. For example, the following function will print the greeting "HELLO!" as many times as specified by the parameter x:
MATLAB

function [] = repeat(x) if x > 0 disp('HELLO!'); repeat(x-1); end

Recursion
Recursion is the concept of defining an algorithm in terms of itself. For example, consider how we could define an algorithm for computing the sum of the integers from 0 to n: Algorithm to "compute the sum of integers" from 0 to n: if n is 0, the answer is 0 otherwise, "compute the sum of integers" from 0 to n-1, and then add n
129

Lecture 22

Course Manual (C Programming)

EngGen 131 S2 2013

Recursive definitions Many different algorithms (along with other things) have natural recursive definitions. In order for a definition of this type to be valid, it must consist of at least two parts: 1) 2) one or more base cases (which do not contain any self-reference) one or more recursive cases (which do contain self-reference)

Many mathematical operations have recursive definitions: Sum of the first n natural numbers base case: sum(0) = 0 recursive case: sum(n) = n + sum(n-1) Factorial base case: recursive case:

0! = 1 n! = n (n-1)!

Defining recursive functions Lets start with a very simple, non-recursive function. Consider the Sum() function in the program below. This function calculates the sum of the integers from 0 up to and including the parameter n. In this example program, the function is called with an input value of 3: #include <stdio.h> int Sum(int n); int main(void) { int value; value = Sum(3); printf("The sum of the first 3 integers is %d\n", value); return 0; } int Sum(int n) { int i; int result = 0; for (i = 0; i <= n; i++) { result += i; } return result; } The output of this program would be: The sum of the first 3 integers is 6
Lecture 22

130

Course Manual (C Programming)

EngGen 131 S2 2013

We can visualise the memory for this program as follows. When the program first starts, the main() function is called. The main() function only has one local variable, called value. Memory for this local variable is allocated on the stack.

The main() function then calls the Sum() function, passing the value 3 as an input argument. New memory is allocated for the new function and its local variables. The parameter, n, is initialised to 3.

Now the Sum() function executes, and the loop iterates four times (for i = 0,1,2,3), stopping when i becomes 4. The value of result becomes 6. Immediately before the Sum() function returns, the values of the local variables would be as shown.

The value stored in result is finally returned to the main() function, and the memory allocated for the Sum() function disappears (is returned back to the system).

Lecture 22

131

Course Manual (C Programming)

EngGen 131 S2 2013

We know the Sum() function works correctly, because the code is very simple to follow. We could now define another function, called Sum2(), which also calculates the sum of the first n integers. However, in this new function we could make use of the Sum() function. Look carefully at the definition of Sum2() in the following program, and notice that the main() function now calls Sum2(): #include <stdio.h> int Sum(int n); int Sum2(int n); int main(void) { int value; value = Sum2(3); printf("The sum of the first 3 integers is %d\n", value); return 0; } int Sum2(int n) { return n + Sum(n-1); } int Sum(int n) { int i; int result = 0; for (i = 0; i <= n; i++) { result += i; } return result; }

It is very easy to understand how the Sum2() function works. To compute the sum of the integers from 0 to n, it uses the Sum() function (which we know works correctly) to compute the sum of the integers from 0 to n-1, and then simply adds n. So the Sum2() function will also work correctly. Note that the Sum2() function above is not recursive because although it does call another function, it does not call itself. How does memory change?

Well, again we start with the main() function:

Lecture 22

132

Course Manual (C Programming)

EngGen 131 S2 2013

This time it calls the Sum2() function, and passes the value 3 to it as an input value. Memory is allocated on the stack for this new function call.

The Sum2() function now calls Sum(), but with an input value of 2. Memory is allocated on the stack for this new function call. Notice that at this point, all three functions occupy space in memory on the stack. Immediately before the Sum() function returns, memory will be organised as follows.

Now when the Sum() function returns, it passes the value 3 back to the Sum2() function. The only statement in the Sum2() function is the return statement followed by an arithmetic expression: return n + Sum(n-1); So the value returned by the Sum() function call is added to the value stored in the local variable n and this will then be returned back to the main() function.

Finally, the Sum2() function returns the result of 3+3 to the main() function.

But now what we have are two correctly working functions, Sum() and Sum2(), that do exactly the same thing they both compute the sum of the integers from 0 to n. So presumably, anywhere we call the Sum() function, we could equally well call Sum2() and get the same result.
Lecture 22

133

Course Manual (C Programming)

EngGen 131 S2 2013

The reason the Sum2() function calls the Sum() function, is to compute the sum of the integers from 0 to n-1. But computing sums is exactly what the Sum2() function does! We could replace that function call to Sum() with a call to Sum2(). We would then have: int Sum2(int n) { return n + Sum2(n-1); } This is almost a correct recursive method. The problem is it will never finish. It will keep calling itself with smaller and smaller values (which will eventually become negative) until we run out of memory for storing the local variables that are allocated each time the function is called. The thing that is missing is the base case. The base case for this problem will be that the result 0 should be returned if the value of the parameter n is 0. We can now write our first, correct, recursive function: int Sum2(int n) { if (n == 0) { return 0; } else { return n + Sum2(n-1); } } Now consider the original program again, but this time using just a recursive version of the Sum() function: #include <stdio.h> int Sum(int n); int main(void) { int value; value = Sum(3); printf("The sum of the first 3 integers is %d\n", value); return 0; } int Sum(int n) { if (n == 0) { return 0; } else { return n + Sum(n-1); } }

Lecture 22

134

Course Manual (C Programming)

EngGen 131 S2 2013

One of the best ways to understand recursion is to visualise how memory changes when a recursive function executes. So, how does memory change when we run the previous program? Well, as always we start with the main() function:

Every time the function calls itself, memory is allocated on the stack for the new function call:

Eventually the base case is reached (when the function is called with an input value of 0). At this point the functions begin to return, freeing the memory used on the stack:

Lecture 22

135

Course Manual (C Programming)

EngGen 131 S2 2013

Incorrect recursion As indicated by the previous example, a recursive function must have a base case without one the function will be incorrect and will not finish. For example, the function below is incorrect because there is no base case defined. It is easy to see that this function will never finish: void Bad(void) { printf("very bad\n"); Bad(); } But just having a base case is not enough. Consider the following incorrect method: int Sum(int n) { if (n == 0) { return 0; } else { return Sum(n+1) - (n+1); } } The logic here seems OK: the sum of the integers from 0 to n is the sum of the integers from 0 to n+1 with (n+1) subtracted. However, this function will not work. Lets say we call Sum(10). Successive calls to the Sum() function will be passed larger and larger parameters: Sum(11), Sum(12), In fact, the value of n will never be 0 and so the base case is never reached. So, although it is important to have a base case, it is equally important that the recursive step makes progress towards that base case. Factorial The factorial of a number, n, is the product of all integers from 1 up to and including n. The factorial of 0, is defined to be 1:

0! = 1 n! = n * (n-1) * (n-2) * (n-3) * ... * 3 * 2 * 1

In other words:

0! = 1 n! = n * (n-1)!

If we were to write a function to compute the factorial of a number, we could easily do it iteratively (using a loop), or recursively. The recursive code follows easily from the definition:

Lecture 22

136

Course Manual (C Programming)

EngGen 131 S2 2013

int Factorial(int n) { if (n == 0) { return 1; } else { return n * Factorial(n-1); } }

Fibonacci sequence In 1202, Fibonacci investigated how fast rabbits could breed in ideal circumstances. He posed the following problem: Suppose a newly-born pair of rabbits, one male, one female, are put in a field. Rabbits are able to mate at the age of one month so that at the end of its second month a female can produce another pair of rabbits. Suppose that our rabbits never die and that the female always produces one new pair (one male, one female) every month from the second month on. The following diagram shows how the rabbit population would grow. Each row in the diagram shows the total number of pairs of rabbits for a given month:

The Fibonacci sequence lists the number of pairs of rabbits in the population every month: 1 1 2 3 5 8 13 21 34 55 89 144 233 ...

Every number is the sum of the previous two numbers in the sequence. This sequence can be defined recursively as follows. There are two base cases: Fibonacci(1) = 1 Fibonacci(2) = 1
Lecture 22

137

Course Manual (C Programming)

EngGen 131 S2 2013

and the recursive case is: Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2) ie. the number of rabbits in any given month = the number of rabbits in the month before + the number of rabbits there were two months ago (which is the number of newly born rabbits this month)

Using this definition, we can write a method to calculate the nth Fibonacci number in the sequence as follows: int Fibonacci(int n) { if (n <= 2) { return 1; } else { return Fibonacci(n-1) + Fibonacci(n-2); } }

Efficiency
Is recursion an efficient way of solving problems? When we discuss efficiency, we are usually interested in how much of a particular resource (space or time) is used. Space: Every time we call a function, space is allocated for its parameters and local variables. This memory is only deallocated when a function returns, which doesn't happen until we reach the base case. Typically, the amount of memory used is proportional to the number of nested calls that are made to the same function which is often much larger than if the solution was coded iteratively (using loops rather than recursion). Time: There is a certain amount of time overhead in calling functions local memory is allocated and parameters are assigned their initial values. Again, this overhead is often significant compared to an iterative solution.

Lecture 22

138

Course Manual (C Programming)

EngGen 131 S2 2013

Consider the Fibonacci example again: int Fibonacci(int n) { if (n <= 2) { return 1; } else { return Fibonacci(n-1) + Fibonacci(n-2); } } This is actually extremely inefficient. Consider the following function call: Fibonacci(5); We can visualise the function calls being made as in the diagram below: 5 4 3 2 1 2 2 3 1

As we can see, 9 function calls are required to calculate the 5th Fibonacci number. This grows extremely quickly. To calculate the 20th Fibonacci number requires 13,529 function calls. To calculate the 44th Fibonacci number requires 1,402,817,465 function calls.

Lecture 22

139

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 22 Summary Program


We can now write programs that solve simple problems recursively. The basic idea is to define a function which decomposes a problem into a smaller problem, then solves the smaller problem recursively by calling the function using the smaller problem, and then takes the solution to the smaller problem and processes it in some way to generate a solution to the original problem. For example, the following program prompts the user to enter a string, and then displays the string in reverse. The process of printing the string in reverse is done recursively, according to the following rules: Base case: if the string is empty, then print nothing Recursive case: print the reverse of the string consisting of all except for the first character print the first character The code for this program is below: #define _CRT_SECURE_NO_DEPRECATE #include <stdio.h> /* Recursive function to display a string in reverse */ void PrintReverse(char *word) { if (*word == '\0') { return; } else { PrintReverse(word+1); printf("%c", *word); } } int main(void) { char word[100]; printf("Enter a word: "); scanf("%s", word); printf("Backwards: "); PrintReverse(word); return 0; } Output from this program is shown below (user input is in bold): Enter a word: programming Backwards: gnimmargorp
Lecture 22

140

Course Manual (C Programming)

EngGen 131 S2 2013

Lecture 23: Software Engineering Overview


Software Engineering is the discipline of applying engineering principles to the design, creation and maintenance of large software systems.

Analogy

small scale development is simple, and detailed planning and the application of theories is not necessary

large scale theories of development are required to provide cost effective and reliable construction

What is Software Engineering?


"Software is becoming as important as oil for making the world go round" Software engineering encompasses the theory, technology, practice and application of software development. A number of important steps are involved in the development process, as listed in the next section.

Software Development Steps


Requirements Engineering Requirements engineering involves working with the people who are going to use the software (the clients) to understand exactly what it should do. It is usually an on-going process, as the clients begin to better understand how a system can help them achieve what's required. As the requirements become known, they are carefully specified in a document which is checked by the clients before the design phase starts. User Interface Engineering User interface engineering is related to requirements engineering, but focuses closely on the actual interface through which the clients will use the software. The capabilities and needs of the different users will be taken into account to design an interface which is intuitive and easy to use.
Lecture 23

141

Course Manual (C Programming)

EngGen 131 S2 2013

Software Architecture and Design As the requirements get clearer, the overall design of the software can be developed. The requirements are analysed to generate a plan for the structure and organisation of the system, which includes identifying the individual components from which the system will be built. Detailed Design and Programming Once the software architecture is clear, detailed design and programming (coding) can occur. If the design is good, the components that make up the system can be coded and tested independently. While many of the larger decisions have been made, there are still many local design decisions required. Quality Assurance Errors and misguided decisions inevitably creep in during all phases of software development, from requirements gathering through to installation especially with large and/or complex systems. This can lead to software products that don't do what is required or are difficult to use. Quality assurance is achieved through setting measurable requirements for correctness, reliability and performance, and with thorough reviewing and testing. Installation, Support and Maintenance Once a software product is ready, it needs to be installed so that it can be used. Support can be provided through a combination of "help" within the software as well as dedicated "help desks". Successful software is very likely to be changed, as satisfied users can see how it could be extended to help them even more.

Software Development models


There are several different models of software development, each describing how and when the above software development steps are applied in order to complete a software project. Waterfall model The oldest model is the waterfall model, in which the software development steps are carried out sequentially, in the order they were listed above. Iterative models There are several different iterative models in which the software development steps are repeated in cycles. A small part of a software project is developed and thoroughly tested before additional functionality is added in subsequent cycles. Examples of iterative models include the spiral model (which is essentially a sequence of waterfall models where additional design goals are added in each cycle), and more recently eXtreme Programming (which relies on feedback to drive the design, as well as introducing practices such as pair programming and test driven development).

Lecture 23

142

Course Manual (C Programming)

EngGen 131 S2 2013

EngGen 131 2013 Lab 7 An Introduction to Visual Studio


GOALS FOR LAB 7
After successfully completing the compulsory exercises for this lab, you will: be able to write simple C programs that perform basic arithmetic and text-based input and output via the command line be able to use the cd and dir commands to navigate your way around directories from a Windows command line be able to compile C programs using either the command prompt interface (text-based interface) or Visual Studio (graphical-based interface) understand how a structure type is defined in C, and visualise structure variables know what happens when one structure variable is assigned to another

GETTING STARTED
Download the .zip file called Lab7Resources.zip from Cecil. This file contains all of the resources that you will need to complete this lab.

THE BASIC IDEA


To develop programs using the C language, a program called a compiler converts the source code that we write (the actual statements that we type that define our program) into an executable file that contains instructions the computer can actually perform. This process is known as compiling. Once this conversion process is successfully completed, we can run the executable file which represents our program.

C source file e.g. hello.c

compiling
( Preprocessor, Compiler, Assembler, Linker )

Executable file e.g. hello.exe

In this lab, you will gain experience compiling very simple C programs using both the Visual studio Command Prompt tool (this is a text-based interface) and the full graphical Visual Studio environment. It is important in this course that you are comfortable with how to use BOTH environments.
Lab 7

143

Course Manual (C Programming)

EngGen 131 S2 2013

REFERENCE VIDEOS
Sometimes videos provide the best way to illustrate certain mechanical processes, like writing and compiling a program using the Visual Studio Command Prompt. For this lab, three videos have been created you do not have to watch them, but you may find them useful to refer to if you get stuck at any point. Links to the videos are available on Cecil, or you can access them directly here:
http://youtu.be/0wgJmmbjI-g http://youtu.be/wpBQSY3WNEc http://youtu.be/iEv7GiwiODw

Using the Command Prompt Tools


We will be using the Microsoft Visual Studio environment to develop our C programs. This environment gives us the choice of a powerful graphical based interface, or a very simple text based interface. The text-based interface (which we will begin working with) provides a very simple way of compiling source code. It is quick to use, and for simple programs you may find it faster and more convenient than launching the graphical interface and creating a new project. To launch the Command Prompt tool, from the Start menu: Microsoft Visual Studio 20xx > Visual Studio Tools > Visual Studio 20xx Command Prompt

The graphical interface provides an excellent editor for writing source code and a debugger that allows you to step one statement at a time through the execution of your program and inspect the values of variables as you do so. Programs are organised into projects which make it easy to manage a large number of source files (although most of the programs we write in this course will involve just one or two). To launch the graphical Visual Studio environment, from the Start menu: Microsoft Visual Studio 20xx > Microsoft Visual Studio 20xx

Lab 7

144

Course Manual (C Programming)

EngGen 131 S2 2013

OK, lets write our first C program


To begin, we must write the source code for our program. Our first program is going to be very simple, and will just display a basic greeting (we will follow tradition in this example, and display the words hello world). Write your source code We are going to create a file (and a folder). It is extremely important that you know where the file is located (e.g. your USB stick, the local hard drive of the computer, the network drive, etc.) most errors that are made in this first stage happen when you are not sure where the file that you are working with is located. If you are working in the labs, it would be an excellent idea to have Windows explorer open (just like in the screenshot that follows) so that you can visualise where on disk your files are stored. Start by creating a folder called Hello. Again, make sure you know where this folder is located! Then, create a file inside this folder called hello.c (one simple way to do this on Windows is to right-click and select New > Text Document, and then rename the created file to hello.c):

STOP! Are you sure you have created a file with a .c extension? Take care that your file does not end with .txt or .c.txt or .cpp or anything other than .c. All your C source files must end with a .c extension. Alright, now the easy part. Open the file in a text editor (you can use any editor you like for this first exercise, even something very basic like Notepad) and write the source code for a program which prints out "hello, world":

#include <stdio.h> int main(void) { printf("hello world\n"); return 0; }


Lab 7

145

Course Manual (C Programming)

EngGen 131 S2 2013

Compile the program Launch the Visual Studio Command Prompt tool. This will bring up a command prompt window which looks similar to the following:

Start by changing the drive displayed by the prompt to the drive on which your file is located. For example, assume that we have stored our source file in the following location: H:\EngGen131\Hello\hello.c As the source file is stored on the H:, we would select this drive by typing "H:" at the command prompt. You can then use the "cd" command to navigate to the directory which stores your source file, hello.c.
Setting environment for using Microsoft Visual Studio 2008 x86 tools. C:\Program Files\Microsoft Visual Studio 9.0\VC> H:\>

H:

cd EngGen131 cd Hello _

H:\EngGen131>

H:\EngGen131\Hello>

Once the current directory is: "H:\EngGen131\Hello" you can compile the source file in this directory using the command: cl /W4 hello.c The purpose of the /W4 option is to ask the compiler to warn you about any problems it detects in your source code. Assuming all goes well, you should see output similar to the following appear in the command prompt window:
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. hello.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out: hello.exe hello.obj

Lab 7

146

Course Manual (C Programming)

EngGen 131 S2 2013

Run the program If you look inside the directory: H:\EngGen131\Hello you should see that a new file called "hello.exe" has been created (there will also be a .obj file which was produced by the assembler and passed to the linker to create the .exe). You can run the program by typing the name of the executable file, "hello", at the command prompt:

H:\EngGen131\Hello> hello, world

hello

Success! Notice that the output from the program appears directly in the command prompt window. Summary of useful commands (change drive, cd, dir)
Change drives For example, to change to the H: C:\> H: List files in the current directory For example, to list files in H:\temp H:\temp> dir Change to the parent directory (i.e. up one level) For example, to move from H:\A\B to H:\A H:\A\B> cd .. Change to a child directory (i.e. down one level) For example, to move from H:\A to H:\A\B H:\A> cd B

EXERCISE ONE:
Demonstrate to the lab tutor that you are able to open a Visual Studio Command Prompt window and compile and execute a simple C program. In particular, you should be comfortable with how to open the command prompt window, how to navigate to the directory that contains your source file, how to use the cl tool to compile the code and how to run the resulting executable. The program you demonstrate must display your name and prompt the user to enter two integers at the command line. An equation that displays the sum of the two integers must then be displayed. An example of the output that your running program may produce is shown below (user input is in bold, and obviously the program should display YOUR name!). H:\EngGen131\Lab7\ex1> ex1 Welcome to Lab One by Paul Denny! Enter two integers: 12 34 12 + 34 = 46
BONUS exercise: Remember, the int data type has a fixed size (typically 4 bytes) and therefore supports only a limited range of values. Try adding together two numbers that are so large you generate an integer overflow. What happens?

Lab 7

147

Course Manual (C Programming)

EngGen 131 S2 2013

Using the command prompt is very convenient and reliable. Next we will look at how to compile programs using Visual Studios graphical interface.

Launching the Visual Studio Application


The screenshot below shows the main menu of Visual Studio. The look and feel of the version that you are using may differ slightly from this (the version in the screenshot is Express 2012 for Windows Desktop). Under the File... menu you will see the option New Project....

Creating a new project Visual Studio organises all of the files that are related to the program that you are working on into what it calls a Project. You can either create a brand new Project for every program that you develop, or alternatively, you can create one project and change the files that it contains each time you develop a different program. To create a new Project, select "New > Project" from the "File" menu.

Lab 7

148

Course Manual (C Programming)

EngGen 131 S2 2013

A dialog box will appear when you create a new project. Visual Studio supports programming in a range of languages, and hence there are many different types of Projects that you can choose from. We will be writing C programs that get input and produce output via a text window. These are known as Console Applications. Select "Win32 Console Application" from the templates listed under the Visual C++ Win 32 project type.

Enter a name (such as Lab or Lab7) for your project, and choose a location (just like before, when you were working from the command prompt, make sure you know where this location is. If you are working in the labs, it would be a good idea to view this in Windows Explorer). Make sure the box labelled "Create directory for solution" is NOT ticked:

Click OK and you will be shown the "Application settings":

IMPORTANT: Make sure to choose Empty project under Additional options above. If you didnt, you will run into problems when you try to compile the project. Finally, click "Finish" and your new Project will be created.
Lab 7

149

Course Manual (C Programming)

EngGen 131 S2 2013

You will then be taken back to the main Visual Studio window, and the Solution Explorer pane will display the name of your project (in this example "Lab") along with folders "Header Files", "Resource Files" and "Source Files". This is an empty project our next step will be to add a source file to this project. We can then write our code in the source file, and compile and run the program from within the Visual Studio interface. STOP! Before you go on, locate the folder on disk where the new project has been created. Have a look at the new files that have been created. As you can see, a project is comprised of a number of files. The only one that really matters to us will be the source file that we will add to the project.

Creating a new source file To develop a program, you need to add a source file to the project. To do this, right click on the "Source Files" folder inside the Solution Explorer window, and select "Add > New Item" from the menu. Or, alternatively, you can select "Add > New Item" from the Project menu. You will then be shown the "Add New Item" dialog box below. NOTE: Visual Studio does not list "C source files" explicitly as one of the installed template file types. Instead, select the "C++ file" type:

C++ and C are different languages. All C source files should end with a ".c" extension (NOT .cpp)

IMPORTANT: when you create a new source file you must explicitly add a ".c" extension to the end of the filename. In this example, the full filename is "hello.c". A common mistake is to forget to include the extension, or to explicitly add ".cpp". This is INCORRECT as Visual Studio will NOT link against the correct standard libraries when compiling your program, which in some situations can lead to frustrating errors.
Lab 7

150

Course Manual (C Programming)

EngGen 131 S2 2013

When you click the "Add" button, a new empty file will be created (called "hello.c") and added to your project. The Solution Explorer pane displays the name of the file "hello.c" in the "Source Files" section of the project. The editor displays the contents of the "hello.c" file (initially it is empty) this is where you type your source code.

Double-click hello.c in the Solution Explorer to open the source file and type the source code for the C program directly into the editor as shown on the right. When you have finished writing the source code, you should save it. From the "File" menu, select "Save hello.c". This will save the source code you typed into the file "hello.c".

Compiling and running your program To compile the code, select the option Build Solution from the "Build" menu:

To run the program, select the option Start Without Debugging from the Debug menu:

Lab 7

151

Course Manual (C Programming)

EngGen 131 S2 2013

Trouble shooting
You may encounter problems from time to time that are no fault of your own this can be frustrating but fortunately most problems have simple solutions. One error that may occur when you try to run your program is the following:

This application has failed to start because MSVCR80D.dll was not found. Re-installing the application may fix the problem

You may not encounter this problem, but if you do, one fix is the following: Go into the project settings and change the "Code Generation" option under "Project Properties" from either MDd to MTd or from MD to MT, as shown in the screenshot below:

Comments
Comments begin with the /* symbol, and end with the */ symbol. Commenting your code makes it easier for others to read and understand what your program is doing. You will notice that Visual Studio automatically colours any text between these symbols in green, to make it obvious that they are comments and will be ignored by the compiler. Every program you write should include a comment which describes the purpose of the program even if the program is very simple.

Lab 7

152

Course Manual (C Programming)

EngGen 131 S2 2013

Structures in C
It can be convenient to compose new types of data by combining the basic data types. This allows us to work with variables which more closely match the basic kinds of data that we are manipulating in our programs. For example, we might want to write a program that processes points on a 2-dimensional plane, each of which consists of an x and a y value. There is no predefined data type that allows us to store a point, so we can define our own data type for this. In C, the basic user-defined data type is the structure (sometimes referred to as a record). A structure consists of a collection of fields (also called components or members) which can be of different types, grouped together under a single name. We can define a new structure type using the following syntax: typedef struct { <type> <fieldName1>; <type> <fieldName2>; ... } <name>; For example, to define a structure to store our point data, we could declare the following structure: typedef struct { int x; int y; } Point; This structure definition does not allocate space for any variables. All that it does is define a new type called Point. Before we can store anything using this structure, we need to declare a variable of this new type. #include <stdio.h> typedef struct { double x; double y; } Point; int main(void) { Point a, b; a.x = 10; a.y = 20; b.x = 10; b.y = 40; return 0; } The definitions of structures, or typedefs, usually occur just after the #include and #define statements (if there are any) at the top of the source file. For example, the program on the left defines and uses Point structures.

Lab 7

153

Course Manual (C Programming)

EngGen 131 S2 2013

Declaring a structure variable Declaring a variable of a structure type is done in the same way as declaring a variable of one of the basic types. We simply specify the type of the variable and give it a name. For example, we could declare two variables called p1 and p2 of type Point as follows: Point p1, p2; We would visualise these variables as follows:

x p1 y p2

x y

The amount of memory that is allocated to store a structure is just the sum of the amount of memory required to store each of the fields. So in this case, both p1 and p2 would require 8 bytes of storage (two variables of type int, which each require 4 bytes)

Accessing fields To access the fields of a structure we use the structure selection operator, which is a ".":

Point p1; p1.x = 100; p1.y = 150; p1

x 100 y 150

Structure assignment The assignment operator copies all of the fields from one structure to another:

Point p1, p2; p1.x = 100; p1.y = 150; p2 = p1; This assignment statement copies the values stored in each field of variable p1 into the fields of p2. x 100 p1 y 150 p2 y 150 x 100

Lab 7

154

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE TWO:
Carefully read through the following code:
#include <stdio.h> #define #define #define #define #define #define #define #define ACE JACK QUEEN KING 1 11 12 13 10000 20000 30000 40000 Remember, the #define preprocessor directive performs a text search-and-replace BEFORE the code is even compiled. Think about the effect this will have on the actual values that are stored in the fields of the structure, and the values that are printed out.

SPADES HEARTS CLUBS DIAMONDS

typedef struct { int suit; int value; } Card; int main(void) { Card card1, card2; card1.suit = HEARTS; card1.value = KING; card2 = card1; card1.suit = SPADES; card1.value = ACE; printf("Card 1 = (%d %d)\n", card1.suit, card1.value); printf("Card 2 = (%d %d)\n", card2.suit, card2.value); return 0; }

Notice that the main() function declares two variables (called card1 and card2 of type Card) these are visualised as in the diagram below. Directly on the diagram below, clearly show how the values stored in the fields of these variables would change as the code above executes.

Lab 7

155

Course Manual (C Programming)

EngGen 131 S2 2013

Before running the code, predict what you think the output is going to be. Write the output in the space provided below:

Now, confirm that your prediction is correct. Create a new project in Visual Studio and add a new, empty source file to this project (call the source file ex2.c). Type the code exactly as it appears above into this source file. Then build and run the program.

Changing source files


What should you do if you want to run a different program? Well, you could create a brand new project. However, it is often more convenient to simply reuse an existing project all you need to do is swap the source file currently in your project with the new source file that you want to run. To do this, right click on the source file in the Solution Explorer and select the "Exclude from Project" option. This will remove the source file from the Project. To add a different source file, you can either create a new source file just as you did for "hello.c" or you may have an existing file that you would like to add (select Add > Existing Item).

Lab 7

156

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE THREE:
Inside the ex3 folder of the Lab7Resources.zip file you will find a source file named othello.c. Have a quick look at the source code for this program. This is an example of an obfuscated C program it has been deliberately written to be very difficult to understand. Although it is impossible to make any sense of the source code, quite remarkably the program still functions correctly! It is a good example of how NOT to write programs in this course! The source code that you write should be as clear as possible, so that others can look at your code and understand what your program is doing. Can you guess what this program will do when you run it, just by looking at the source code? (Let me answer that for you: no) Run the program and have a look. Begin by entering a difficulty level: 0-10 (easy-hard). Then play the game of Othello (sometimes called Reversi): 12345678 ........1 ........2 ........3 ...XO...4 ...OX...5 ........6 ........7 ........8 To make your move, enter a number between 11 and 88 (representing the row and column in which you want to place your piece), or a 99 to pass. If you are unsure of the rules of Othello, have a look at the Wikipedia entry: http://en.wikipedia.org/wiki/Reversi Good luck trying to beat this game on level 10 (although you may have to be patient with the time it takes the computer to move)! For this lab exercise, you do not need to win the game! However, you should be comfortable demonstrating to the tutor that you can remove a specific source file from your project ("Exclude from Project") and add a new one ("Add > Existing Item..."). If you like, you can reuse your project for the other lab exercises in this course.

COMPULSORY LAB TASKS END HERE


Lab 7

157

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE FOUR:
The Roomba is a robotic vacuum cleaner developed by the iRobot company. Millions of Roombas have been sold since they were first available in 2002 (this is probably because vacuuming is not much fun!) The unique feature of a Roomba is that it uses sensors to navigate its way around a room avoiding obstacles. In this exercise you will program a robot called picobot which attempts to cover the entire floor space of a room using just local sensors (that can detect obstacles in the North, South, East and West directions).

Start by reading through the rules for using picobot. You will find these in the PicobotRules.pdf file in the Lab7Resource.zip file. The picobot environment is also available on Cecil just choose the appropriate link depending on which browser you are using. How does this relate to programming? Abstractly, we can think about a running program as being in a particular state (as represented by the current values stored in the programs variables) and transitioning from one state to the next by executing program statements (that use and modify the values stored in variables). Picobot is a very simplistic version of this at any point in time picobot has a state that is a single number. As it senses its environment, it can transition from one state to another and move in one of four directions. NOTE: This exercise is a challenging puzzle that requires you to understand and interact with a simple computational system. If you solve it, and want an extra challenge, have a look at some of the other maps that picobot can attempt to explore!
Lab 7

158

Course Manual (C Programming)

EngGen 131 S2 2013

EngGen 131 2013 Lab 8 Conditionals, Loops, Arrays and Structures


This lab provides a summary of some of the topics covered in the corresponding lectures (Lectures 15 and 16). You should read through these notes before your lab session as preparation for the lab.

GOALS FOR LAB 8


After successfully completing the compulsory exercises for this lab, you will: understand how to store and access data in 1-dimensions arrays (this includes both arrays of integers and arrays of structures) know how to use loops to systematically access all of the elements of an array be able to write basic conditional statements to make decisions in your program have practiced storing data values typed by the user into an array

GETTING STARTED
Download the .zip file called Lab8Resources.zip from Cecil. This file contains all of the resources that you will need to complete this lab. This resource file contains only source files. You will need to create a Visual Studio project in order to compile and run the programs (of course you can do this using the command prompt if you prefer). Feel free to work either from the command prompt (just like Exercise 1 of Lab 7) or using a Visual Studio project (just like Exercise 2 of Lab 7). Make sure you organise your files appropriately. If you choose to use Visual Studio, here is one approach you might like to take: create a new project called Lab 8 copy the source files (ex1.c, ex2.c, ....) into the folder that was created for the project you can then use Add existing item... to add a source file to the project you can only have one source file (containing a main() function) at a time in the project, so to work on another exercise you will need to remove the existing source file first

Lab 8

159

Course Manual (C Programming)

EngGen 131 S2 2013

Conditionals
You can make decisions about which code to execute using if and else statements. Conditions are formed by combining relational and logical operators. A very simple condition is below: int number = 12; if (number > 10) { printf("The number is greater than 10.\n"); }

this condition evaluates to true, so the printf() function will be executed. Below is a more complex example. See if you can work out what the output of the following code is, and then try it out to confirm your answer: int a = 10; int b = 15; if ((a > b) || !((a == 11) || (b < 1000))) { printf("First"); } else if ((a == 10) && (b > 0)) { printf("Second"); } else { printf("Third"); }

Notice that although each block consisted of only a single statement, curly braces were still used to define the blocks. This is a good habit to get into, as forgetting to include the braces when a block of code does consist of multiple statements can lead to frustrating errors.

Loops
You can use a for loop or a while loop to repeatedly execute a block of statements. The following loops both print out the values: 0 1 2 3 4 for int i; for (i = 0; i < 5; i++) { printf("%d ", i); } int i; i = 0; while (i < 5) { printf("%d ", i); i++; } while

Lab 8

160

Course Manual (C Programming)

EngGen 131 S2 2013

Arrays
An array is a collection of variables of the same type. An array of ints is declared as follows: int numbers[SIZE]; where SIZE specifies how many elements the array should store.

We can visualise an array as a set of boxes, lined up horizontally side by side, each labelled with unique, increasing, index numbers starting from 0. For example, given the following declaration:

int data[8];

we would visualise this array as below:

data
0 1 2 3 4 5 6 7

We can access individual elements of the array by specifying the index or subscript inside square brackets. For example, the following loop will calculate the sum of all the integer values stored in the array above: int i, sum; sum = 0; for (i = 0; i < 8; i++) { sum += data[i]; }

Lab 8

161

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE ONE:
The following picture shows a 6-digit digital odometer in a car (in this case displaying miles).

Assume that the digits in this odometer are stored in an array, with the most significant digit stored in index position 0. We can visualise this array as follows:

odometer

0
0

9
1

1
2

3
3

0
4

8
5

To display the current odometer reading, you can use a loop to print out each element of the array. The values that are stored in the array will be entered by the user when the program runs. For this exercise, given any particular set of starting values, you need to calculate the very next reading for the odometer assuming that the car has travelled 1 more mile. In the case of the digits above, the very next odometer reading would be:

odometer

0
0

9
1

1
2

3
3

0
4

9
5

The very next reading after this one would be:

odometer

0
0

9
1

1
2

3
3

1
4

0
5

Lab 8

162

Course Manual (C Programming)

EngGen 131 S2 2013

You should start with the source file ex1.c from the Lab8Resources.zip file. Some of the code for this exercise (for reading input and displaying the initial odometer reading) has already been written for you. Here is the provided code in ex1.c:

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #define NUM_DIGITS 6 int main(void) { // The array for storing the odometer digits int odometer[NUM_DIGITS]; int digit, i; int changePos; printf("Lab 8 - Exercise 4\n\n"); for (i = 0; i < NUM_DIGITS; i++) { printf("Enter the value for digit %d: ", i); scanf("%d", &digit); odometer[i] = digit; } printf("\nOdometer reading: "); for (i = 0; i < NUM_DIGITS; i++) { printf("%d ", odometer[i]); }

you need to complete the code here

return 0; }

To update the digits stored in the odometer array correctly, an algorithm that you might like to consider is as follows: _____________________________________________________________________ start at the rightmost (least significant) digit of the array increase the value of this digit by 1 as long as the value stored in the element is 10 set the element to 0 move one position to the left increase the value at this new position display the odometer reading _____________________________________________________________________

Lab 8

163

Course Manual (C Programming)

EngGen 131 S2 2013

The code for reading the initial set of values from the user, and displaying the odometer reading has already been written. When you run the program, the user is asked to enter the digits one at a time, and the initial odometer reading is displayed as shown in the example below (user input is shown in bold): Lab 8 - Exercise 4 Enter Enter Enter Enter Enter Enter the the the the the the value value value value value value for for for for for for digit digit digit digit digit digit 0: 1: 2: 3: 4: 5: 0 9 1 3 0 8

Odometer reading: 0 9 1 3 0 8 What you need to do is add the code that calculates the very next odometer reading (assuming the car has travelled 1 more mile) and then print the new odometer reading to the screen beneath the original reading. When you have completed this exercise, the output from the program should look like this (user input is shown in bold): Lab 8 - Exercise 4 Enter Enter Enter Enter Enter Enter the the the the the the value value value value value value for for for for for for digit digit digit digit digit digit 0: 1: 2: 3: 4: 5: 0 9 1 3 0 8

Odometer reading: 0 9 1 3 0 8 Odometer reading: 0 9 1 3 0 9 Here is one more example: Lab 8 - Exercise 4 Enter Enter Enter Enter Enter Enter the the the the the the value value value value value value for for for for for for digit digit digit digit digit digit 0: 1: 2: 3: 4: 5: 1 5 9 9 9 9

Odometer reading: 1 5 9 9 9 9 Odometer reading: 1 6 0 0 0 0

PLEASE NOTE: For this exercise, you DO NOT need to test the case where the input is: 9 9 9 9 9 9. However, you should think about what might be needed to handle this case.

Lab 8

164

Course Manual (C Programming)

EngGen 131 S2 2013

Storing user input in an array


An array can be used to store a series of data values (these data values could, for example, be entered by the user or they could be read from file on disk). Lets assume the user is entering information regarding a set of cards. The structure definition on the right shows the fields for a variable of type Card: typedef struct { int suit; int value; } Card;

The array itself, which consists of 100 elements of type Card, is declared as follows: Card cards[100]; We could visualise this array as below (only the first 8 elements are shown in the diagram):

Lets say the user enters suit and value information for a particular card and we store this information in the variables suitInput and valueInput. We can now store this user input in the first position of the array using two assignment statements: cards[0].suit = suitInput; cards[0].value = valueInput; Assuming the user entered the values 10000 and 5, the array would now look like:

That was easy enough, but now what happens when the user enters a second set of values? We dont really want to be assigning one value at a time into the array using individual assignment statements (imagine if hundreds of values were going to be stored our source code would not look very nice). Instead, we would probably want to use a loop, and inside the body of the loop we would assign a set of values to the next available position in the array. To keep a track of where this next available position is, we could use a variable to store it. Therefore, we should declare not only the array, but also a variable that will indicate how many values are currently stored in the array. This variable will also tell us the index position of the next available element. For example: Card cards[100]; int numCards = 0; So now we have:

Lab 8

165

Course Manual (C Programming)

EngGen 131 S2 2013

Now, lets assume the user is going to enter information about 5 cards. We could use a loop that executes exactly 5 times, and each time through the loop we store the values entered by the user in the next available position of the array. Pay attention to how the variable numCards is being used: while (numCards < 5) { ... // get input from the user cards[numCards].suit = suitInput; cards[numCards].value = valueInput; numCards++; } For example, the very first time through the loop, assume the user enters the values 10000 and 5. After these loop statements have been executed, we would visualise the variables as follows:

The very next time through the loop, assume the user enters the values 20000 and 13. This time, the value of numCards is 1, so the values entered by the user will be stored in index position 1 of the array. After the loop body is executed, and numCards has been incremented from 1 to 2, the variables would now store the data as follows:

Lets do this one more time. Now assume the user enters the values 30000 and 1. These values will be stored in index position 2, and then the value of numCards will be incremented (from 2 to 3). The variables will now store the following values:

You get the picture. The variable numCards always keeps a track of how many of the arrays elements are actually being used to store values. It also indicates the index of the next available element of the array.
Lab 8

166

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE TWO:
For this exercise, you will write a program that asks the user to enter information regarding 5 playing cards (these cards will represent a hand in a game of poker). Your program will store these 5 cards in an array (each card is represented by a suit and a value, which will both be integers), and will then print all 5 cards in the hand. Finally, the program should display whether or not the hand represents a flush. In this case, a flush is when all cards in the hand have exactly the same suit. Two examples of the program running are shown below (user input is in bold). Note the following things: the prompt should display the number of the card to be entered (between 0 and 4) when the hand is displayed, each card should appear inside parentheses the last line of output should display whether or not the hand represents a flush > ex2 Enter suit and value for card Enter suit and value for card Enter suit and value for card Enter suit and value for card Enter suit and value for card (10000 1) (10000 2) (10000 3) You do not have a flush. > ex2 Enter suit and value for card Enter suit and value for card Enter suit and value for card Enter suit and value for card Enter suit and value for card (20000 3) (20000 5) (20000 7) You have a flush! 0: 10000 1 1: 10000 2 2: 10000 3 3: 30000 4 4: 30000 5 (30000 4) (30000 5)

0: 20000 3 1: 20000 5 2: 20000 7 3: 20000 9 4: 20000 11 (20000 9) (20000 11) BONUS challenge: it is actually pretty easy to determine if the hand is a flush. For more of a challenge, can you also check for other common poker hands? Consider displaying just the strongest hand that the user has a pair, three of a kind, straight, full house, etc.

Hint: an algorithm for this problem might be along the following lines: - while the number of cards stored is less than 5 - prompt for and read the next values from the user - store these values in the next available position of the array - update the number of cards - use a loop to print the values in the array - use a loop to check if all of the suit values are the same

COMPULSORY LAB TASKS END HERE


Lab 8

167

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE THREE:
This exercise is similar to the previous one in that we will store a series of values entered by the user in an array (although this time the array is of type int). However, unlike before when we knew exactly how many values were to be stored, this time we do not know ahead of time how many elements will be stored in the array we will leave this up to the user to decide. It means we have to think a little more carefully about the stopping condition for the loop that we use to store the values in the array. Write a program that prompts the user to repeatedly enter integer values. The input should end as soon as the user enters the same value as the first value they entered. At this point, all of the values entered by the user should be displayed (in the opposite order to which they were entered) and then the program should end. Several examples of the output that should be produced by this program are shown below (user input is in bold): Enter Enter Enter Enter Enter 8 6 4 value: value: value: value: value: 2 2 4 6 8 2

Enter value: 1 Enter value: 1 1

Enter Enter Enter Enter Enter Enter Enter 2 3 4

value: value: value: value: value: value: value: 3 2 1

1 2 3 4 3 2 1

Hint: an algorithm for this problem might be along the following lines: - use a variable to record whether the input loop should finish (initially set this to false) - while the input is not finished - prompt for and read the next value from the user - if this value matches the first value the user entered, set the loop variable to true - otherwise, store the value in the array - use a loop to print the values in the array, from index numValues-1 to 0 Run through this algorithm on paper to convince yourself it works, and then implement your solution!

Lab 8

168

Course Manual (C Programming)

EngGen 131 S2 2013

Debugging
When your program does not work correctly, for example maybe it produces unexpected output or it simply crashes, you need to locate and correct the bugs, or errors, in your code. This process is known as debugging. The debugging process basically involves confirming all the things that you believe are true when your program executes. For example, you may believe: that at a certain point in your code, the value stored in the variable x should be 42 that when a certain if/then/else statement is evaluated, the else part should be executed that when you call a function and pass it a certain value, it should return a certain result and so on... As soon as you encounter something that you believe should be true, but it turns out not to be, you should work backwards and locate the bug that is causing the error. Developing good debugging skills and effective debugging techniques are vital to becoming a successful programmer. In many cases, using printf() statements is a simple and highly effective way to confirm things that you believe are true. You can: use a printf() statement to print out the value stored in the variable x at any point in your code put a printf() statement in each block of an if / else statement to see which branches are being executed use a printf() statement to display the value returned by a particular function call and so on... However, for some complex cases using a proper debugging tool is a good idea. A debugging tool usually allows you step one line at a time through your program code, and inspect the values of variables as you do so. Visual Studio includes a good debugging tool.

Using the Visual Studio Debugging Tool Firstly, when you compile your code, ensure that the current configuration is set to Debug rather than Release, as shown in the screenshot below:

To use the debugger, you can set a break point on the line of code where you would like execution to halt so that you may inspect the values currently stored in variables at that point.

Lab 8

169

Course Manual (C Programming)

EngGen 131 S2 2013

To set a break point you simply click the mouse button in the left margin next to the line you want to set the break point on:

Then, select "Start Debugging" from the "Debug" menu.

The program will then execute up to the line of code with the break point. Once the break point is reached, you can inspect the values stored in each of the variables at that time:

You can then choose to execute the program one line at a time and watch the values of the variables change. The statement that is about to be executed is indicated by a small yellow arrow:

Lab 8

170

Course Manual (C Programming)

EngGen 131 S2 2013

You have the following options: Step Into this executes the current line of code (pointed to by the yellow arrow). If this line of code is a function call, then the debugger will take you into where the function is defined and you can continue stepping through the code inside the function definition. If this line of code is not a function call, then you will simply step down to the next line of code. Step Over this executes the current line of code (pointed to by the yellow arrow). If this line of code is a function call, then the debugger will not take you into the function definition, but will execute the function call automatically and then take you down to the next line of code. Step Out this executes the current line of code (pointed to by the yellow arrow). If you have previously entered a function definition with the Step Into option, this option will take you back out to the original line of code which made the function call. Continue this resumes normal execution of the program, up until the next time a breakpoint is encountered.

Note: If for any reason you do not see these options in the Debug menu, you can add them by customising the menu. This can be done by selecting Tools > Customize and dragging the commands from the Commands list to the toolbar or menu:

Lab 8

171

Course Manual (C Programming)

EngGen 131 S2 2013

Locals and Autos Consider the code below (this is the summary program from the end of Lecture 15). A break point has been placed on the line of code that increments the variable numInputs:

At the bottom left corner of Visual Studio is the "Autos" window which shows the variables of interest at this point of the program (notice the line of code above has just updated an element of the array).

This window should appear automatically when you start debugging, but if it doesnt, you can open it from the menu under Debug > Windows > Autos:

The "Autos" window displays the variables that you are currently using at this point of the function. If you want to display a list of all of the variables that are available to the function (called the local variables of the function) you can select the "Locals" window:

Lab 8

172

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE FOUR:
In this exercise, which gives you practice using the Visual Studio debugger, you should explore the Autos window of the debugger which allows you to inspect the current values stored in your programs variables. To begin, choose one of your previous programs that uses an array (or write a new one). Then, set a break point on any line of code that modifies the value of an element in the array. When you start to debug the program, view the Autos window of the debugger. Make sure you can view the elements of your array (in the screenshot below, an array called values is being inspected):

Now Step Into the code, which should execute the statement on which you have your break point set. You should see the value of an element of the array change. Conveniently, the updated value should be highlighted red in the debugger:

Lab 8

173

Course Manual (C Programming)

EngGen 131 S2 2013

Lab 8

174

Course Manual (C Programming)

EngGen 131 S2 2013

EngGen 131 2013 Lab 9 File I/O and Functions


This lab provides a summary of some of the topics covered in the corresponding lectures (Lectures 17 and 18). You should read through these notes before your lab session as preparation for the lab.

GOALS FOR LAB 9


After successfully completing the compulsory exercises for this lab, you will: be able to open and read input data from text files stored on disk know how to define and call your own functions have mastered conditional statements have practiced calling functions from an external library (LibBMP) have decoded a message hidden in an image using steganographic techniques

Function Prototypes
If we are going to call a function we have defined, we need to tell the compiler what the name of the function is, what type of parameters it is passed, and what type its return value is. This can be done by declaring a prototype of the function, at the top of the program before the main() function is defined.

#include <stdio.h> int Add(int a, int b); int main(void) { int sum; sum = Add(10, 25); printf("Sum: %d", sum); return 0; } int Add(int a, int b) { return a + b; }
Lab 9

the prototype of the Add() function is declared here

the Add() function is called here

the Add() function is defined here

175

Course Manual (C Programming)

EngGen 131 S2 2013

File I/O
Reading A file on disk can be opened for reading by calling the fopen() function. The return value of this function should be assigned to a FILE pointer variable. For example, to open an input text file on disk called words.txt, you can call: FILE *fp; fp = fopen("words.txt", "r");

To read one character at a time from the file, you can call the fgetc() function, which returns a value of type int representing the character read from the file. It is common to call this function inside a loop, which stops when the end of file is reached: int c; while ((c = fgetc(fp)) != EOF) { /* process the character stored in c */ } To read one value of a particular type at a time (which may consume more than a single character from the file), you can call the fscanf() function. This is just like scanf() except you also must pass it the file pointer. You also need to provide the appropriate conversion specifier for example to read an integer value from file, you could use: int value; fscanf(fp, "%d", &value);

Writing A file on disk can be opened for writing by calling the fopen() function. The return value of this function should be assigned to a FILE pointer variable. For example, to open an output text file on disk called output.txt, you can call: FILE *fp; fp = fopen("output.txt", "w");

To write formatted data into the output file, you can use the fprintf() function: fprintf(fp, "my formatted output %d %f\n", a, b);

Closing It is good style to close a file when you have finished with it: fclose(fp);
Lab 9

176

Course Manual (C Programming)

EngGen 131 S2 2013

Displaying cards
A series of #define directives has been created for you to represent information regarding a set of Uno playing cards: #define #define #define #define #define #define #define #define #define #define #define NUMBER SKIP REVERSE DRAW_TWO WILD WILD_D4 RED GREEN BLUE YELLOW NONE 0 1 2 3 4 5

10000 20000 30000 40000 50000

A new type has also been defined for you, to store information about a single Uno card: typedef struct { int type; int colour; int value; } Card; Consider the follow two variables defined to be of this new type: Card card1, card2; These variables could be initialised with a set of assignments statements, such as: card1.type = NUMBER; card1.colour = GREEN; card1.value = 7; card2.type = REVERSE; card2.colour = YELLOW; card2.value = 0; The diagram below shows an example of how we might visualise the values stored in these two variables after initialisation:

Lab 9

177

Course Manual (C Programming)

EngGen 131 S2 2013

Your first job is to write a function that displays these cards in a format that is easy to read. The function will have a return type of void (because it doesnt actually return a value to the calling statement, it just produces output to the screen using printf()). The name of the function will be DisplayCard, and it will take one input of type Card. The function prototype declaration will therefore look like: void DisplayCard(Card c) Lets have a look at some code. Assume the DisplayCard() function has been defined (this is what you need to do) and that our main() function makes a call to this function, as follows: void DisplayCard(Card c) { .... } int main(void) { Card card1, card2; card1.type = NUMBER; card1.colour = GREEN; card1.value = 7; card2.type = REVERSE; card2.colour = YELLOW; card2.value = 0; DisplayCard(card1); .... } Notice that the function call in the main() function: DisplayCard(card1); passes the variable card1 to the DisplayCard() function as an input. So, what actually happens here? Well, remember that when you pass an input to a function, the value in the calling statement is effectively assigned to the input parameter in the function definition. So in this example, we are assigning the variable card1 to the variable c. Because these are both structures, the values in the fields of the variable card1 are all copied across to the input parameter c. This is illustrated in the diagram on the next page.

Lab 9

178

Course Manual (C Programming)

EngGen 131 S2 2013

The DisplayCard() function should print out the card information in a readable format. For example, the output from the function call illustrated above should be: 7_GREEN Now consider these two function calls: printf("Two cards: "); DisplayCard(card1); printf(" "); DisplayCard(card2); This time, the output should be: Two cards: 7_GREEN REVERSE_YELLOW Pay attention to the following things: the output is all in upper case the DisplayCard() function itself does not print a new line or a space separate words in the display of one card are separated by an underscore character if the card is of type number, then the value followed by the colour is displayed if the card is of type wild or wild draw 4, then only the type is displayed if the card is any other type, the type followed by the colour is displayed

Lab 9

179

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE ONE:
Consider a text file that contains a list of integers:
7 1 10000 0 0 30000 9 4 50000 0 3 40000 0 0 20000 0 0 20000 4 5 50000 0

The first integer in the file represents the number of cards that are represented by the remaining values. In this example, 7 cards are represented. The remaining numbers, when considered in groups of 3, then represent a set of cards. For example, the first set of 3 numbers after the first number (i.e. 7) is: 1 10000 0 Interpreting this as the type, colour and value information for a card, this represents the card SKIP_RED The next set of three numbers after this is: 0 30000 9 which represents the card: 9_BLUE In fact, displaying all 7 cards represented by this data would give:
SKIP_RED 9_BLUE WILD DRAW_TWO_YELLOW 0_GREEN 4_GREEN WILD_DRAW_FOUR

Open the file called input.txt. This is the input file that contains the data representing a set of cards. Write a program which reads this file as input, and displays each of the cards. The output should be as shown above. You should start by using fscanf() to read the first number from the file. Then, use fscanf() in a loop to read the remaining values, three at a time. Your program should begin with the preprocessor directive: #include "cards.h" which includes the header file that contains the constants representing the different cards.

Lab 9

180

Course Manual (C Programming)

EngGen 131 S2 2013

The LibBMP library Recall from Lecture 16 that the LibBMP library provides the following methods that you can use to load and access the pixel values of a .bmp image: LoadBMPFile("fi.bmp", &width, &height) This loads an image file stored on disk in BMP format into memory. Parameters: "fi.bmp": is the name of the input image file this will already exist on disk in the project folder &width: the address of the int variable that will be initialised to the width of the image once it is read from disk &height: the address of the int variable that will be initialised to the height of the image once it is read from disk GetPixelValue(row, col, channel) Once an image file has been loaded into memory, this function returns an integer value (between 0 and 255) representing the intensity of the specified colour channel of a pixel in the image. Parameters: row: the row, or vertical position, of the pixel col: the column, or horizontal position, of the pixel channel: the colour channel (0 = red, 1 = green, 2 = blue) SetPixelValue(value, row, col, channel) Once an image file has been loaded into memory, this function sets the intensity value (between 0 and 255) of the specified channel of a pixel in the image. Parameters: value: the intensity value (between 0 and 255) to be stored in the specified channel of the pixel row: the row, or vertical position, of the pixel col: the column, or horizontal position, of the pixel channel: the colour channel (0 = red, 1 = green, 2 = blue) SaveBMPFile("fo.bmp", width, height) This saves the image in memory out to a file on disk in BMP format. Parameters: "fo.bmp": is the name of the output image file this will be created as output by the program and appear in the project folder width: the width of the image height: the height of the image

Lab 9

181

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE TWO:
Read through the description of the LibBMP library on the previous page. Make sure you understand how the two functions for getting and setting pixels work: GetPixelValue() int red; red = GetPixelValue(row, col, 0); would return the intensity of the red channel for the pixel at position (row, col), and: int blue; blue = GetPixelValue(row, col, 2); would return the intensity of the blue channel for the same pixel. SetPixelValue() The colour of the pixel at position (row, col) can be changed to black by setting each of the three colour channels (R=0, G=1, B=2) to 0: SetPixelValue(0, row, col, 0); SetPixelValue(0, row, col, 1); SetPixelValue(0, row, col, 2); Have a look at the image "auckland.bmp" in the Lab9Resources.zip file.

This image looks perfectly normal, however there is a secret message hidden in the pixels of this image. Using the LibBMP library, see if you can decode the hidden message. Hint: the message is encoded in the "odd" pixels (the pixels for which the intensities of the red, green and blue components sum to an odd number).

COMPULSORY LAB TASKS END HERE


Lab 9

182

Course Manual (C Programming)

EngGen 131 S2 2013

Random numbers
You can call the rand() function to generate a random number between 0 and RAND_MAX. int r = rand(); The value of RAND_MAX is typically 32,767. The RAND_MAX constant, and the prototype for the rand() function are defined in the <stdlib.h> header file, so you must include: #include <stdlib.h>

To generate a random integer value between a and b inclusive, you can use the formula:

int r = (rand() % (b-a+1)) + a;

To generate a random double (floating-point) value between 0.0 and 1.0 inclusive, you can use the formula: double r = ((double)rand() / RAND_MAX);

http://xkcd.com/

Lab 9

183

Course Manual (C Programming)

EngGen 131 S2 2013

Circuit Reliability Estimation The components that make up a system can be connected in series or in parallel, or a combination of the two. The system will work if there is a pathway of working components from one side of the system to the other. If you know the reliability of each individual component within a system, then you can determine the overall reliability of the system. Series When components are connected in series, for example:

both A and B must work for the system to work. In this case, the reliability of the system, RS, is related to the reliabilities of the components, RA and RB, according to the formula: RS = RA RB For example, if the reliability of component A is 75% (i.e. component A will be working 75% of the time) and the reliability of component B is 40%, then the reliability of the system will be 30% (i.e. there will be a working pathway through the system 30% of the time). Parallel When components are connected in parallel, for example: A

the system will work as long as at least one of the components work. In this case, the reliability of the system, RS, is related to the reliabilities of the components, RA and RB, according to the formula: RS = 1 ((1 RA) (1 RB)) For example, if the reliability of component A is 75% (i.e. component A will be working 75% of the time) and the reliability of component B is 40%, then the reliability of the system will be 85% (i.e. there will be a working pathway through the system 85% of the time).

Lab 9

184

Course Manual (C Programming)

EngGen 131 S2 2013

Monte-Carlo estimation While an exact, theoretical measurement is ideal, it is not always possible in practice. One technique that can provide good estimates easily is a Monte-Carlo simulation. With a Monte-Carlo simulation random values are used to calculate one possible outcome this is then repeated many, many times to get an overall, average measurement. Using our system reliability example, the basic idea would be: for each component, generate a random number between 0 and 1. If the probability of the component working is greater than this number, then you can consider the component to be working if there is a pathway of working components, then the system is working repeat these steps many times and determine the ratio of how many times the system is working to the total number of trials

One approach you can use for determining whether there is a pathway of working components is to assign a value of 1 (true) to the working components, and assign a value of 0 (false) to the components that are not working. For example, if the components are connected in series:

we can determine which components are working using: a = ((double)rand() / RAND_MAX) < RA; b = ((double)rand() / RAND_MAX) < RB; and the system will be working if (a && b) is true.

If the components are connected in parallel:

we can again determine which components are working using: a = ((double)rand() / RAND_MAX) < RA; b = ((double)rand() / RAND_MAX) < RB; but now the system will be working if (a || b) is true.

Lab 9

185

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE THREE:
Implement a Monte-Carlo algorithm to estimate the reliability of the following system:

C A D B E F

given that the reliabilities of the components are as follows:

RA = 0.4 RB = 0.9 RC = 0.6 RD = 0.5 RE = 0.2 RF = 0.7

Questions: What is the theoretical reliability of this system? What is the calculated reliability from your program when you run:

10 trials? 100 trials? 1000 trials? 10000 trials?

Approximately how many trials do you need to perform in the Monte-Carlo simulation before the calculated reliability is accurate to 3 decimal places compared with the actual reliability?

Lab 9

186

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE FOUR:
Read the description of the following algorithm very carefully. This algorithm describes a process for plotting a collection of points. 1) Initialise point A to (100, 173), point B to (0, 0) and point C to (200, 0). The relative positions of these points is illustrated in the diagram below they approximately form an equilateral triangle. Plot these points.

A
(100, 173)

(0, 0)

(200, 0)

B
Ask the user how many points they would like to plot.

2)

Define a fourth point (call it D) which should be randomly positioned anywhere it does not need to be inside the triangle. Plot this point. Select either A or B or C at random, and plot the midpoint between this random vertex and point D. Set D equal to the last point plotted. Repeat steps 3 and 4 over and over again, until the number of points the user wanted to plot have been plotted ________________________________

3)

4) 5)

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #define FILENAME "points.txt" typedef struct { double x; double y; } Point; int main(void) { FILE *fp; Point a, b, c, d; int i, numPoints, pick;
Lab 9

For this exercise, you need to write a C program which implements this algorithm and which writes out to file the positions of all the points plotted. You should start by downloading the file ex4.c from the Labs website. The code for this file is listed on this page.

187

Course Manual (C Programming)

EngGen 131 S2 2013

/* Initialise the positions of points a, b and c */ a.x = 100; a.y = 173; b.x = 0; b.y = 0; c.x = 200; c.y = 0; /* Read the number of points to plot */ printf("Enter number of points: "); scanf("%d", &numPoints); /* Initially point d is at some random location */ d.x = (double)(rand() % 100); d.y = (double)(rand() % 100); fp = fopen(FILENAME, "w"); if (fp == NULL) { printf("Error opening exit(EXIT_FAILURE); } /* Output the initial three fprintf(fp, "%f %f\n", a.x, fprintf(fp, "%f %f\n", b.x, fprintf(fp, "%f %f\n", c.x, fprintf(fp, "%f %f\n", d.x, /* Close the file */ fclose(fp); printf("%d points output to file.\n", numPoints); return 0; }

file.\n"); points */ a.y); b.y); c.y); d.y);

You need to add the appropriate code in here to implement steps 3, 4 and 5 of the algorithm

The first two steps of the algorithm have already been implemented for you. You should begin by compiling and running the program and observing the output. A new file should have been created on disk called "points.txt". Open this file and have a look at the values it stores. Make sure you understand where the values have come from in the source code of the program. The format of this output file should be one point to a line. Each line should store the x-coordinate, then a space, then the y-coordinate of a single point. When you have finished writing the code that implements the remaining steps of the algorithm, you should test your program by plotting a small number of points. For example, if you plot 10 points and then look at the contents of the file "points.txt" you may see something like: 100.000000 173.000000 0.000000 0.000000 200.000000 0.000000 41.000000 67.000000 20.500000 33.500000 10.250000 16.750000 105.125000 8.375000 52.562500 4.187500 76.281250 88.593750 88.140625 130.796875

Lab 9

188

Course Manual (C Programming)

EngGen 131 S2 2013

Finally, you should use MATLAB to visualise where these points are positioned when plotted. This file can be loaded into MATLAB and plotted using the commands: >> load points.txt >> plot(points(:,1), points(:,2), '.');

Next, try plotting a large number of points, say 10,000 and visualise them again using MATLAB. What does the pattern look like? Zoom in to certain areas of the plot and note the self-similarity.

Image manipulation The GetPixelValue() and SetPixelValue() functions from the LibBMP library can be used to modify a BMP image, as you would have seen in Exercise Three. However, the image is stored internally in memory and cannot be accessed directly. The LibBMP library includes two other functions, which require you to understand structures and arrays, but which give you direct access to the pixel data in memory. LoadColourArray("fi.bmp", bi, &width, &height); where: "fi.bmp": is the name of the input image file this will already exist on disk in the project folder bi: is a two-dimensional array of Colour structures that will be initialised when the image is read from disk &width: the address of the int variable that will be initialised to the width of the image once it is read from disk &height: the address of the int variable that will be initialised to the height of the image once it is read from disk once this function has been called, the colour information for the pixel at position (row, col) can be accessed using: bi[row][col].red; bi[row][col].green; bi[row][col].blue;

SaveColourArray("fo.bmp", bo, width, height); and:

"fo.bmp": is the name of the output image file this will be created as output by the program and appear in the project folder bo: is a two-dimensional array of Colour structures that stores the colour information for the output image to be written to disk width: the width of the image height: the height of the image

Lab 9

189

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE FIVE:
Write a program which uses the LibBMP library and takes a BMP image file as input, and produces a BMP image file as output which is identical to the input image except it has been rotated by 180 degrees.

The following skeleton file, called ex5.c, can be downloaded from the Labs website and will help you get started. At the moment, this program simply duplicates the image file, without performing any rotation. You should compile and run it and have a look at the output file. #include <stdio.h> #include "LibBMP.h" Colour bitmapIn[MAXW][MAXH]; Colour bitmapOut[MAXW][MAXH]; int main(void) { int width = 0; int height = 0; int row, col; /* Read colour information */ LoadColourArray("vulture.bmp", bitmapIn, &width, &height); for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { bitmapOut[row][col] = bitmapIn[row][col]; } } /* Save colour information */ SaveColourArray("vultureout.bmp", bitmapOut, width, height); return 0; }
these arrays are declared outside of the main() function because they require a large amount of memory

Lab 9

190

Course Manual (C Programming)

EngGen 131 S2 2013

Note, ordinarily you would declare your variables inside a function definition such as the main() function. However, the variables bitmapIn and bitmapOut are declared outside of the main() function. The amount of storage required for these variables is large, and it is more efficient to store them outside of the function definition. Try manipulating the image in other ways, for example try converting the colour image to gray scale (so it looks like a black and white photograph). To do this, you will need to set the red, green and blue components for each pixel to the same intensity value, so that the resulting colour is a shade of gray. What is the correct intensity value to use? You could try calculating an average of the red, green and blue components for each pixel, but this will not produce a nice result for certain colours. The correct formula to use, which computes the luminance of a pixel is:

luminance = 0.3red + 0.59green + 0.11blue

The colour information for each pixel is defined by the structure: typedef struct { byte red; byte green; byte blue; } Colour;

where the byte type is a synonym for unsigned char, as defined by the following typedef: typedef unsigned char byte;

These definitions are in the file LibBMP.h.

Lab 9

191

Course Manual (C Programming)

EngGen 131 S2 2013

http://xkcd.com/

http://xkcd.com/

Lab 9

192

Course Manual (C Programming)

EngGen 131 S2 2013

EngGen 131 2013 Lab 10 Pointers and Strings


GOALS FOR LAB 10
After successfully completing the compulsory exercises for this lab, you will: understand how strings are represented in C as an array of characters have practiced passing strings to functions as parameters have an understanding of how memory is organised when function calls are made know how to write functions that accept pointers to structures as inputs

Pointers
To declare a pointer variable, we use the * operator: To initialise a pointer variable, we use the & operator: To dereference a pointer variable, we use the * operator: int *p; p = &a; int result = *p;

A simple example using pointers involves swapping the values stored in two variables without using an explicit assignment statement for either variable. Consider the code given below:

int a = 10; int b = 20; /* Now lets swap the values in a and b */ int *x, *y; int temp; x = &a; y = &b; temp = *x; *x = *y; *y = temp; printf("%d %d\n", a, b); The output of this code is: 20 10
Lab 10

193

Course Manual (C Programming)

EngGen 131 S2 2013

Pointers as parameters
If you pass a variable (of any type except for an array) to a function as a parameter, then a copy of the value stored in the variable is given to the function. The function may change this copy, but it cannot modify the original value back in the function from which it was called. For example, if you have a variable of type Point (a structure type), and you pass this to a function called SwapCoordinates(), the SwapCoordinates() function will store its own copy of the values which make up the structure variable:

main()

SwapCoordinates(Point p)

x 100 p y 200 p

x 100 y 200

function call:

SwapCoordinates(p)

However, you can use pointers to modify a variable that is passed to a function. When you call the function, you pass it the address of the variable. The function can then modify the value stored in the variable by dereferencing the pointer. For example, this time when we call the SwapCoordinates() function, we pass it the address of the variable:

main()

SwapCoordinates(Point *p)

x 100 p y 200 p

function call:

SwapCoordinates(&p)

Lab 10

194

Course Manual (C Programming)

EngGen 131 S2 2013

As a complete example, the program below swaps the values of the coordinates x and y that are stored in a Point structure: #include <stdio.h> typedef struct { int x; int y; } Point; void SwapCoordinates(Point *p); int main(void) { Point p; p.x = 100; p.y = 200; printf("Point: (%d, %d)\n", p.x, p.y); SwapCoordinates(&p); printf("Point: (%d, %d)\n", p.x, p.y); return 0; } void SwapCoordinates(Point *p) { int temp = p->x; p->x = p->y; p->y = temp; } main() SwapCoordinates(Point *p)

x 200 p y 100 p

function call:

SwapCoordinates(&p) The output of this program is: Point: (100, 200) Point: (200, 100)
Lab 10

195

Course Manual (C Programming)

EngGen 131 S2 2013

Here is a slightly more complex example where a pointer is passed from one function to another: int main(void) { int myValue = 0; passingAnIntPointerOne(&myValue); printf("%d ", myValue); passingAnIntPointerTwo(&myValue); printf("%d ", myValue); return 0; } void passingAnIntPointerOne(int *value) { *value = *value + 10; } void passingAnIntPointerTwo(int *value) { passingAnIntPointerOne(value); } The output of this is: 10 20 Notice that when we call the two passing... functions from within the main() function, we must use the & operator to calculate the address of the variable myValue (which is declared in the main() function). However, when passingAnIntPointerTwo() calls passingAnIntPointerOne() it is not necessary to use the & operator because we already have the address of the myValue variable. Look carefully at LINE X in the program above. The variable myValue is actually stored within the memory allocated for the main() function. Therefore on LINE X it is necessary to calculate the address of this variable (using &) to pass it to the function. See the diagram to the right. However, within the memory allocated to the passingAnIntPointerTwo() function, the parameter value already stores an address. Therefore, on LINE Y, the value is passed directly (without &). // LINE X

// LINE Y

Lab 10

196

Course Manual (C Programming)

EngGen 131 S2 2013

Colour matching
In this exercise you will write a program that finds a card (within a players hand) that matches the colour of another card (the top card of the discard pile). The user will input the values and colours of all of the cards, and the code for this part of the exercise is provided to you. To begin with, consider the following two structure definitions: typedef struct { int type; int colour; int value; } Card; typedef struct { int yourNumberOfCards; Card yourCards[MAX_CARDS]; Card topCard; } GameState; The GameState structure shown here is a simplification of the structure that is used in your project. Understanding how this structure is used in this simple example will help you with your project. Assume we have a variable called gameState declared: GameState gameState; If the user has 3 cards in their hand (2_GREEN, 8_RED and 6_BLUE) and the top card on the discard pile is 2_RED, then this structure would store this information as follows:

Lab 10

197

Course Manual (C Programming)

EngGen 131 S2 2013

The following main() function initialises this gameState variable using values entered by the user: int main(void) { GameState gameState; int i, numCards; Card card; int result; // Get the top card of the discard pile printf("Top of discard pile: "); scanf("%d %d %d", &card.type, &card.colour, &card.value); gameState.topCard = card; // Get player's cards printf("How many cards? "); scanf("%d", &numCards); for (i = 0; i < numCards; i++) { printf("Enter card: "); scanf("%d %d %d", &card.type, &card.colour, &card.value); gameState.yourCards[i] = card; } gameState.yourNumberOfCards = numCards; // Call the play function result = Play(&gameState); // Display the result printf("Top card: "); DisplayCard(gameState.topCard); if (result == -1) { printf(" - no match"); } else { printf(" matches: "); DisplayCard(gameState.yourCards[result]); } return 0; } You should type this main() function into your program. Please note: this function calls DisplayCard(), which is the function you should already have written in Lab 9. You should reuse your DisplayCard() function in this exercise this function also calls Play() you still need to write this function. The Play() function should return the index position of a card in the players hand that matches the colour of the top card on the discard pile. If no matching cards exist, then the function should return -1.

Lab 10

198

Course Manual (C Programming)

EngGen 131 S2 2013

Note: When the Play() function is called, it is passed the address of the gameState variable. The actual memory for the gameState variable is allocated in the main() function. We would visualise this situation as shown in the diagram on the right.

EXERCISE ONE:
Complete the program (for which the main() function has been provided) by providing the Play() function. This function should return an integer. It should return the index position of any card in the players hand that is the same colour as the top card on the discard pile. For this exercise, you can assume that wild cards (WILD and WILD_D4) will not be entered. A couple of examples of how the program should behave are shown below. First, if there is a matching coloured card, both cards should be displayed: Top of discard pile: 0 10000 2 How many cards? 3 Enter card: 0 20000 2 Enter card: 0 10000 8 Enter card: 0 30000 6 Top card: 2_RED matches: 8_RED If there isnt a matching coloured card, simple display no match: Top of discard pile: 0 10000 2 How many cards? 3 Enter card: 0 20000 2 Enter card: 0 40000 8 Enter card: 0 30000 6 Top card: 2_RED - no match

Lab 10

199

Course Manual (C Programming)

EngGen 131 S2 2013

Strings in C
Strings in C are stored as arrays of type char.

greeting

'h' 'e' 'l' 'l' 'o' '\0'


0 1 2 3 4 5 6 7

...

the characters that make up the string "hello" are stored in the elements of the array

the end of the string is indicated by the null character

The end of the string is denoted by a special character called the null character. The null character has an ASCII code of 0, and is represented by the character constant '\0'. Example: Consider the following program: #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main(void) { char greeting[100]; printf("Enter a word: "); scanf("%s", greeting); printf("The word you entered was: "); printf("%s\n", greeting); return 0; } In this example, the user is prompted to enter a word. The scanf() function is used to read the string the user types and store it into the array called greeting (one character will be placed into each element of the array, followed by the null character). Finally, the content of this array is printed to the screen. The output from this program would be as follows (user input in bold): Enter a word: hello The word you entered was: hello NOTE: When used with the scanf() function, the %s conversion specifier will only read characters up to the first white space (space, tab, newline). If the input includes, for example, a space character, then nothing after that character will be stored in the array.
Lab 10

The variable greeting is an array, so the & is not used when calling scanf()

200

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE TWO:
A palindrome is a word which reads the same forwards and backwards. The following are examples of palindromes: a noon redivider For this exercise, you need to define a function called IsPalindrome(). This function should be passed one parameter, which should be a string (an array of type char). The function should return true if the string is a palindrome, and false otherwise. You can start by downloading the file ex2.c from the Labs website. The main() function has already been written for you: int main(void) { char input[100]; printf("Please enter one word: "); scanf("%s", input); if (IsPalindrome(input)) { printf("\"%s\" is a palindrome", input); } else { printf("\"%s\" is not a palindrome", input); } return 0; } Make sure you visualise memory correctly. The main() function declares the array of characters that will store the string the user enters. The memory for this array is allocated within the block of memory for the main() function. The main() function then calls the IsPalindrome() function, and passes the array to it as a parameter. When an array is passed to a function as a parameter, only the address of the first element of the array is actually passed. The IsPalindrome() function can therefore access the nth character in the array by dereferencing the pointer, using either of the following approaches: *(word + n) word[n]

Lab 10

201

Course Manual (C Programming)

EngGen 131 S2 2013

So how should the IsPalindrome() function determine whether or not the string is a palindrome? The basic idea will be to locate the last character in the word, and compare that with the first character in the word. Then compare the second to last character with the second character, and so on. If the two characters being compared are different, then the string will not be a palindrome. If the characters being compared are the same, all the way to the middle character(s) of the string, then the string will be a palindrome.

So, how should you locate the first and last character? Well, the first character is easy that is always going to be at index position 0. The last character is going to be to the left of the null character, so you can either search for this, or if you know the length of the string you can work out what the index position will be. Note: you can assume the string will not contain any punctuation. Several examples of the program running are given below (user input is in bold): Please enter one word: deliver "deliver" is not a palindrome Please enter one word: noon "noon" is a palindrome Please enter one word: a "a" is a palindrome

COMPULSORY LAB TASKS END HERE

Lab 10

202

Course Manual (C Programming)

EngGen 131 S2 2013

Basic encryption
One of the simplest possible encryption schemes for a plain text string is to replace each character in the string with the character that appears some fixed number of letters down the alphabet. This is known as a Caesar cipher (named after Julius Caesar who used this kind of encryption when sending classified messages) and the most common variant of this is known as ROT13 which is when the fixed number of letters used to replace each character is 13. The mapping from one character to the next is shown (for the lower case characters) below:

The same mapping exists for the upper case characters. Only the upper and lower case alphabetic characters are modified when the ROT13 cipher is applied. All other characters (punctuation, white space) are preserved. As an example, the phrase: Welcome to ENGGEN 131 would be encoded to Jrypbzr gb RATTRA 131

One feature of the ROT 13 cipher, a consequence of 13 being half the number of letters in the alphabet, is that performing ROT 13 on an encoded phrase will restore the original phrase. For example, performing ROT 13 on the phrase: Jrypbzr gb RATTRA 131 would give: Welcome to ENGGEN 131

Lab 10

203

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE THREE:
Write a program that encodes a phrase entered by the user using the ROT13 cipher. For this exercise you need to define two functions: EncodePhrase() EncodeCharacter() The EncodePhrase() function is responsible for encoding an entire phrase. It makes use of the EncodeCharacter() function, which is responsible for encoding a single character. You should make sure that lower and upper case characters are encoded correctly. NOTE: Some code is already provided to you for this exercise. In particular, the main() function is complete: #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #define MAX_PHRASE_LENGTH 100 void EncodePhrase(char *original, char *encoded); char EncodeCharacter(char c); int main(void) { char phrase[MAX_PHRASE_LENGTH]; char encoded[MAX_PHRASE_LENGTH]; printf("Enter phrase to encode: "); gets(phrase); EncodePhrase(phrase, encoded); printf("Encoded: %s\n", encoded); return 0; } The constant MAX_PHRASE_LENGTH represents the longest possible phrase, and is used to specify the size of the string arrays. You can assume the user will not enter phrases longer than this. Once completed, an example of the output that should be produced is given below (user input in bold): Enter phrase to encode: Wow, it's no wonder Julius Caesar was so successful Encoded: Jbj, vg'f ab jbaqre Whyvhf Pnrfne jnf fb fhpprffshy

Lab 10

204

Course Manual (C Programming)

EngGen 131 S2 2013

You need to have a clear picture of how memory is organised. When the program first begins, memory is allocated for the main() function. Inside the memory allocated for the main() function are blocks of memory for the local variables that it declares. In this case, two arrays are declared in the main() function, visualised as in the diagram below.

When the main() function calls the EncodePhrase() function, only the addresses of the arrays are passed as parameters to the EncodePhrase() function (whenever an array is passed to a function, only the address of the first element of the array is actually passed). We would therefore visualise this as follows:

Any changes made to the arrays in the EncodePhrase() function will change the original arrays (back in the main() function, which is where the memory is actually allocated).

Lab 10

205

Course Manual (C Programming)

EngGen 131 S2 2013

Finally, when the EncodePhrase() function calls the EncodeCharacter() function, the value of the character (i.e. the corresponding ASCII code) is passed to the EncodeCharacter() function. For example, assume the character a is passed to the EncodeCharacter() function from the EncodePhrase() function. The character a has an ASCII code of 97, so the memory would be organised as follows:

If the EncodeCharacter() function were to change the value stored in the local variable c, this would have no effect on the values stored in the arrays. The EncodeCharacter() function therefore needs to return a value back to the EncodePhrase() function, which can in turn modify values in the original arrays. NOTE: When you implement the EncodeCharacter() function, there are lots of ways this can be done. One obvious way is to use a very large if/else statement (consisting of 46 options, one for each of the upper and lower case characters), but this is a poor solution. See if you can make use of the ASCII character codes to develop a more elegant solution (have a look at an ASCII table).

Lab 10

206

Course Manual (C Programming)

EngGen 131 S2 2013

EngGen 131 2013 Lab 11 Project and Strings


GOALS FOR LAB 11
After successfully completing the compulsory exercises for this lab, you will: have thought about an approach for testing the code you write for your project gain further practice working with strings and functions that accept string inputs

GameState
In the Project, the following structure has been defined to represent the state of a game of Uno at a particular point in time: typedef struct { int numberOfPlayers; int directionOfPlay; int lastColourCalled; int yourPosition; int yourNumberOfCards; Card yourCards[MAX_CARDS]; int numberOfDiscards; Card discardPile[MAX_CARDS]; int allPlayersCardsLeft[MAX_PLAYERS]; int allPlayersColoursCalled[MAX_PLAYERS]; int allPlayersScores[MAX_PLAYERS]; } GameState;

Your main task in the project is to define a function called abcd001_Play() which is passed a pointer to one of these GameState structures and which returns the index into the yourCards array that represents the card that you want play (of course, change abcd001 to your UPI!). When you have written your abcd001_Play() function, you will want to test this using various different GameStates. This lab exercise will give you practice designing tests for your project and provides you with a program that you can use to automatically run your tests on your abcd001_Play() function.

Lab 11

207

Course Manual (C Programming)

EngGen 131 S2 2013

Lets start with three examples. In each case, the players hand consists of exactly 4 cards, and the top card on the discard pile is shown. For each example, write down ALL of the index positions in the players hand representing cards that could validly be played given the top card shown. EXAMPLE 1: Top card: Your hand:

List ALL index positions in Your hand for Example 1 that represent valid cards that could be played: EXAMPLE 2: Top card: Your hand:

List ALL index positions in Your hand for Example 2 that represent valid cards that could be played: EXAMPLE 3: Top card: Your hand:

List ALL index positions in Your hand for Example 3 that represent valid cards that could be played:
208

Lab 11

Course Manual (C Programming)

EngGen 131 S2 2013

One approach we can use for testing our abcd001_Play() function involves a test file that consists of a sequence of possible game states. Each game state concludes with a list of all valid values that could be returned by the abcd001_Play() function for the corresponding game state. Each game state in the file begins with an asterisk. This is followed by all of the information for a GameState structure. The very last line of each test lists all valid index positions that could be returned when the abcd001_Play() function is called with the given game structure (the first value on this line is the total number of these valid positions that exist). Take a look at the example test file below (containing three separate tests) and make sure you understand what all the numbers mean. In particular, look at the last line of each test and you should be able to make sense of those numbers based on the values you just wrote down on the previous page. * number of players direction of play 4 last colour called 7 50000 your position at the 0 table 4 0 10000 1 0 20000 2 0 30000 3 0 40000 4 the number of cards 1 in your hand 2 30000 0 4 4 4 4 your actual cards 50000 50000 50000 50000 0 0 0 0 the number of cards 1 2 on the discard pile the number of cards the cards in the
each player has left discard pile

* 4 the last colour called the current scores of by each player each player 7 50000 0 4 0 10000 1 0 30000 2 0 40000 3 0 30000 4 1 2 30000 0 all valid index 4 4 4 4 positions for your 50000 50000 50000 50000 hand 0 0 0 0 2 1 3 * 4 7 50000 0 4 0 10000 1 0 20000 2 0 40000 3 0 20000 4 1 2 30000 0 4 4 4 4 50000 50000 50000 50000 0 0 0 0 0
Lab 11

the number of valid index positions for cards you can play in your hand

209

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE ONE:
An example test file is shown below. Three example test cases are defined in the file, but they are incomplete they are each missing the last line that displays the total number of valid index positions in the players hand, and the index positions themselves. Complete each of these test cases by filling in the correct values that should appear in the last line and show your solution to the lab tutor. * 4 7 50000 0 6 0 30000 1 0 20000 2 0 40000 3 0 20000 4 1 30000 0 2 20000 0 3 2 10000 0 3 10000 0 0 20000 6 4 4 4 4 50000 50000 50000 50000 0 0 0 0

* 4 7 20000 0 4 0 10000 1 2 40000 0 0 30000 3 0 40000 4 2 0 40000 8 5 50000 0 4 4 4 4 50000 50000 50000 50000 0 0 0 0

* 4 7 50000 0 5 0 10000 1 0 30000 2 0 20000 3 4 50000 0 1 10000 0 5 2 30000 0 0 30000 6 0 10000 6 1 10000 0 1 40000 0 4 4 4 4 50000 50000 50000 50000 0 0 0 0

Lab 11

210

Course Manual (C Programming)

EngGen 131 S2 2013

Storing a collection of strings


We know that a string in C is represented as a 1-dimensional array of characters. What if we want to store a list of strings? The solution is to use a 2-dimensional array where each row of the array will store a different word. For example, lets declare a 2D array of type char that consists of 5 rows and 10 columns: char words[5][10]; With this 2-dimensional array, we can store up to 5 words in our list (where each word is no longer than 9 characters... remembering we have to leave space for the null terminating character!). The first word will be stored in the first row of the array. The second word in the second row, and so on. In our code, we can refer to an individual word by including the first index after the array variable (which essentially gives us access to one row of the array). So, how do we actually get the words in there? There is a function called strcpy() that we can use to copy strings into arbitrary rows of the array. For example, we can use it to copy string literals into some of the rows of the array: strcpy(words[0], "grape"); strcpy(words[1], "orange"); strcpy(words[3], "plum"); We can visualise the results of these strcpy() function calls as:

And we can display individual words in the list using appropriate printf() function calls. For example: printf("%s\n", words[0]); printf("%s\n", words[1]); printf("%s\n", words[3]); would produce the following output: grape orange plum
Lab 11

211

Course Manual (C Programming)

EngGen 131 S2 2013

We can also store user input using scanf(). For example, consider the following input statement: scanf("%s", words[2]); This will store the users input in the third row (the row with index 2) of the 2-dimensional array. For example, if the user enters the string lemon, then the array will now contain the values as follows:

Swapping two strings in the list How would we swap two strings in the list? To do so, we would need some temporary storage (as we always do when we swap the values stored in two variables). Lets declare the temporary storage (this will be a 1dimensional array because it will be used as temporary storage for a single word) and then visualise it along with the 2-dimensional array that stores our words: char temp[10];

Now, lets say we want to swap the words orange and lemon in our list. These words are stored at row positions 1 and 2 respectively. We would begin by copying one of the words into the temporary storage location: strcpy(temp, words[1]); This has copied the word orange into the temporary storage location. The next step is to copy the word from row 2 to row 1: strcpy(words[1], words[2]); This has copied the word lemon over the top of the word orange. Note that once the null terminating character for the word lemon is copied across, no other values in the array change. Because in this case the word orange is one character longer than the word lemon, the old null terminating character for the word orange would still appear in the array. This has no effect though, because the null terminating character for the word lemon defines the end of the word.
212

Lab 11

Course Manual (C Programming)

EngGen 131 S2 2013

Finally, we just need to copy the word from the temporary storage location to row 2 of the array: strcpy(words[2], temp);

And thats it! We have now swapped the words at these two positions.

Comparing strings When working with strings, a common operation we want to perform is comparison. For example, we would like to know whether two strings are equal, or we would like to know which string would appear first in standard alphabetical order. To compare two strings, we can use a function called strcmp(). This function returns an integer value. For example, consider the following comparisons:

printf("%d\n", strcmp("abc", "def")); printf("%d\n", strcmp("def", "abc")); printf("%d\n", strcmp("ghi", "ghi"));

The output produced by this program is as follows:

-1 1 0

As you can see, when the two strings are the same, the strcmp() function evaluates to 0. It evaluates to a negative number (typically -1) when the first string appears alphabetically before the second string, and a positive number (typically 1) when the first string appears alphabetically after the second string.

Lab 11

213

Course Manual (C Programming)

EngGen 131 S2 2013

Sorting
Sorting data is one of the most fundamental operations in computing. Some algorithms (like, for example, search algorithms) are inefficient when the data to be searched is unsorted but are extremely efficient on sorted data. Sorting is a widely researched topic and many different algorithms for sorting data have been proposed and studied. It is very easy to describe sorting informally: the input is a list of data items, and the output is a reordering of the list such that all elements are in non-decreasing order (the ordering might be lexicographical or numerical, depending on the type of elements being sorted). For example, we can sort integers:

or strings:

or any list of data values where two values can be compared. One simple algorithm (known as bubble sort) for sorting a list of values involves an operation called Bubble. Bubble: o examine each pair of elements in the list (for example, initially compare element 0 and element 1, then compare element 1 and element 2, then compare element 2 and element 3, and so on) o if the pair of elements being examined is out of order then swap them

If you carry out the Bubble operation on an unsorted array, at the end of the operation the largest value in the array will be in the last position. If you perform the Bubble operation again, then you are guaranteed that the second largest element will be in the second to last position. Naturally, if you perform the Bubble operation enough times, the list of elements will be sorted correctly.

Lab 11

214

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE TWO:
Read the previous description that explains how to store a list or collection of strings, how to swap two strings in the list, and how to compare two string lexicographically. If you need to, write some small test programs to ensure you understand how the strcmp() and strcpy() functions work. Note that in order to use the string-related functions in C, you must include: #include <string.h> at the top of your source file. Write a program that reads a list of words from the user and then displays those words in alphabetical order. A constant called NUM_WORDS should be defined at the top of your program to determine how many words will be in the list. An example of the output of this program is shown below (user input is in bold): > ex2 Please enter 5 words: Enter word 0: cherry Enter word 1: banana Enter word 2: apple Enter word 3: kiwifruit Enter word 4: grape Sorted: Word 0 = Word 1 = Word 2 = Word 3 = Word 4 =

apple banana cherry grape kiwifruit

Test your program thoroughly, with different values for NUM_WORDS. At the very least, make sure that you test the following input lists:

aaa bbb ccc ddd eee

eee ddd ccc bbb aaa

eee aaa bbb ccc ddd

bbb ccc ddd eee aaa

aaaaa aaaa aaa aa a

For this exercise you should define three functions: Sort(), Bubble() and Swap(). The Sort() and Bubble() functions should be passed an array, and the Swap() function should be passed an array and the indices of the two items that are to be swapped.

COMPULSORY LAB TASKS END HERE


Lab 11

215

Course Manual (C Programming)

EngGen 131 S2 2013

Crossword helper
Sometimes when struggling to solve a crossword it would be nice to have some way of viewing all of the possible words that could fit in a particular place given the partial clues that have already been solved. For example, if you already had the first two and the last two characters of a 6-letter word:

ab--rd
it might be useful to be able to produce a list of all possible words that could fit. In this case, there are just two:

aboard absurd
We can solve this problem using a word list, and a function to check if a word matches a given pattern.

EXERCISE THREE:
For this exercise, you need to complete the function: int WordMatchesPattern(char *word, char *pattern) which takes two strings as input, and returns true only if the string word is a possible completion of the string pattern. The pattern string is a partial word where some of the characters have been replaced with hyphens (-). The word will be a completion of the pattern if all the non-hypen characters match. For example: word: apple and pattern a-p-e is a match word: apple and pattern a---e is a match word: apple and pattern --ple is a match whereas word: apple and pattern app--e is not a match word: apple and pattern ---p- is not a match word: apple and pattern --l-e is not a match The main() function, which reads all of the words from a provided text file of English words, is already provided for you. The program prompts the user for a pattern, and should display all of the words that match the pattern along with a count of the total number of matching words.

Lab 11

216

Course Manual (C Programming)

EngGen 131 S2 2013

The main() function that is provided to you is given below:


int main(void) { FILE *input; char word[100]; char pattern[100]; int count; input = fopen("wordlist.txt", "r"); count = 0; if(input == NULL) { printf("Could not open file."); exit(EXIT_FAILURE); } printf("Enter pattern: "); scanf("%s", pattern); while (fscanf(input, "%s", word) != EOF) { if (WordMatchesPattern(word, pattern)) { printf("%s\n", word); count++; } } printf("\n%d matches", count); return 0; }

Below are some examples of how the program should behave if you correctly define the WordMatchesPattern() function (user input is in bold).
Enter pattern: p-uplug plum plus pouf pour pout 6 matches Enter pattern: apple apple 1 matches Enter pattern: a-b-c-d 0 matches

Enter pattern: --------e-------compartmentalised compartmentalises compartmentalized compartmentalizes counterrevolution environmentalists intergovernmental plenipotentiaries 8 matches

Lab 11

217

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE FOUR:
A partial anagram is a word that consists of a subset of the letters of another word. For example: of is a partial anagram of food restful is a partial anagram of fluster (in fact, it is also a full anagram) off is not a partial anagram of food (the letter f may only appear once) In this exercise, you will write a program that displays all of the partial anagrams for a word typed in by the user. An example of the output your program should produce, assuming the user types the word apple as input, is given below: Your word: apple ale ape apple lap leap pa pal pale pap pea peal pep plea There are 13 partial anagrams of apple To get started, have a look in the Lab11Resources.zip file. Here you will find a word list called wordlist.txt. This is a comprehensive list of English words, one per line, taken from the 12dicts repository (http://wordlist.sourceforge.net/12dicts-readme-r5.html). This will provide the set of candidate words that you can test to see if they are partial anagrams of the word the user provides as input. You will also find the source file ex3.c. The main() function has already been provided for you. It prompts the user to enter a word, reads all of the words from the word list, and calls the IsPartialAnagram() function to check for partial anagrams. You should define the IsPartialAnagram() function: int IsPartialAnagram(char *word1, char *word2)

Lab 11

218

Course Manual (C Programming)

EngGen 131 S2 2013

This function takes two strings as parameters, and returns true if the first word is a partial anagram of the second word. The source code that has been provided to you is below:

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> int IsPartialAnagram(char *word1, char *word2); int main(void) { FILE *input; char word[100]; char userword[100]; int count; input = fopen("wordlist.txt", "r"); count = 0; if(input == NULL) { printf("Could not open file."); exit(EXIT_FAILURE); } printf("Your word: ", userword); scanf("%s", userword); while (fscanf(input, "%s", word) != EOF) { if (IsPartialAnagram(word, userword)) { printf("%s\n", word); count++; } } printf("\nThere are %d partial anagrams of %s", count, userword); return 0; }

You can see that the IsPartialAnagram() function is called in a loop. Each time it is called, it is passed the next word from the word list as the first parameter, and the user supplied word as the second parameter.
219

Lab 11

Course Manual (C Programming)

EngGen 131 S2 2013

So, how can you implement this function? One way to implement the IsPartialAnagram() function is to use an array that records character counts. For example, consider that the user enters the word faded and that you are testing to see whether the word deaf (from the word list) is a partial anagram. Each character in the users word can be examined, and the corresponding character count can be incremented. Next, each character in the word being tested can be examined, and the corresponding character count can be decremented. If all of the resulting character counts are positive or zero, then the word being tested is a partial anagram. The diagram below illustrates this process:

Note, only the relevant part of the character count array is shown. You will need to decide how many elements this array should have, and how it should be declared. In the example above, the IsPartialAnagram() function would return true, and the word deaf would be printed. How many partial anagrams are there for the word programming?

Lab 11

220

Course Manual (C Programming)

EngGen 131 S2 2013

EngGen 131 2013 Lab 12 Recursion and Project Reviews


GOALS FOR LAB 12
After successfully completing the compulsory exercises for this lab, you will: be able to implement simple recursive algorithms be able to review your classmates solutions to the project, and provide feedback to them

Recursion
A recursive function is one which calls itself. There are certain problems for which the solution can be defined in terms of solutions to smaller, identical problems. For these kinds of problems, it is often straightforward to write a recursive function to solve them. A trivial example is printing all of the integers between a start value and an end value. The recursive case is quite simple. To print all the values between start and end, we can just print start, and then recursively print all the values between start+1 and end. The base case is also simple. If start and end are the same, then we just print that value. A recursive function for printing the values between start and end could be defined as follows: void PrintValuesBetween(int start, int end) { if (start == end) { printf("%d ", start); } else { printf("%d ", start); PrintValuesBetween(start + 1, end); } }

Lab 12

221

Course Manual (C Programming)

EngGen 131 S2 2013

Combinations
The number of combinations of m things chosen out of n is written: n m and is pronounced "n choose m". For example, there are 52 cards in a deck, all distinct. The number of possible poker hands is the number of different ways we can pick five cards from the deck:

52 5 n There is an elegant recursive definition of m as follows:


Base cases

n 0 = 1. That is, there is only one way to pick zero things out of n: pick nothing. n n = 1. That is, the only way to pick n things out of n is to pick them all.
Recursive case

n n 1 n 1 If 0 < m < n, then m = + m m 1 n Directly from this definition, we can define a function to calculate m as follows:
int Choose(int n, int m) { if ((m == 0) || (m == n)) { return 1; } else { return Choose(n-1, m) + Choose(n-1, m-1); } }

Type this function definition into a program and call it with a variety of input values. For example, you should be able to use this function to verify that:
6 choose 2 is 15

and

52 choose 5 is 2598960

Add the following printf() function call as the very first statement in your function definition:
printf("Choose(%d, %d)\n", n, m);

and you can see exactly what function calls are made to calculate n choose m recursively.
Lab 12

222

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE ONE:
For this exercise you need to define a recursive function called PrintReverse() which should print an integer where the digits appear in reverse order. For example, the function call:
PrintReverse(123456);

should print:
654321

The following diagram illustrates how we can solve this problem recursively:

How should the PrintReverse() function be defined?

void PrintReverse(int n) { }
Base case: A good base case for this problem will be when the input value, n, is less than 10 (i.e. a single digit). In this case, the digit can just be printed Recursive case: Step 1: the expression n % 10 gives you the right-most digit which can just be printed Step 2: the expression n / 10 gives you the number excluding the rightmost digit, which can then be the input to the recursive function call.

Lab 12

223

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE TWO:
The prime factors of a positive integer, n, are the prime numbers that divide n exactly without leaving a remainder. The prime factorisation of a positive integer is the list of its prime factors expressed as a product. For example, the prime factorisation of 288 is: 2x2x2x2x2x3x3 To find out how many times 2 appears in the prime factorisation of a number, we can repeatedly divide the number by 2 until the result is an odd number. Another way of looking at this is that an odd number has no factors of 2, numbers that are twice an odd number have one factor of 2, numbers that are four times an odd number have two factors of 2 and so on. For example, the number 40 has 3 factors of 2. Write a recursive function called FactorsOfTwo() which takes an integer as input and returns the number of factors of 2 that the number has. For example, the function call:
FactorsOfTwo(40)

should return
3 Obviously, because the function is recursive, you must not use any loops!

How should the FactorsOfTwo() function be defined?

int FactorsOfTwo(int n) { }
Base case: If the input number is odd, we can just return 0 (an odd number has no factors of 2) Recursive case: the number of factors of 2 that n has, will be one more than the number of factors of 2 that n/2 has

COMPULSORY LAB TASKS END HERE

Lab 12

224

Course Manual (C Programming)

EngGen 131 S2 2013

EXERCISE THREE:
The Towers of Hanoi is a puzzle consisting of three rods and a number of discs, of different sizes, which fit onto the rods.

Starting with all of the discs on a single rod, in increasing order of disc diameter, the goal of the puzzle it to shift the entire stack of discs to a different rod. The following two rules must be obeyed: 1) 2) only one disc may be moved at a time a larger disc must never be placed on top of a smaller disc

Imagine looking down at the puzzle from above. Discs can either be moved clockwise or anticlockwise. There is a simple algorithm for solving this puzzle, which requires 2 -1 moves for n discs. This algorithm can be defined recursively as follows: To move n discs clockwise: 1) first move n-1 discs anti-clockwise (this would leave disc n with no discs on top of it) 2) next, move disc n clockwise as required 3) finally, move n-1 discs anti-clockwise Write a program that solves the Towers of Hanoi puzzle for a given n (where n is the number of discs that the user enters) by printing out a list of moves for the discs. Each move will name a particular disc (the smallest disc is 1, the largest is n) and a direction (clockwise or anti-clockwise). This program will make use of one important function with the following prototype:
void Solve(int n, int clockwise)
n

Here, n is the size of the problem and clockwise is either true or false depending on the direction the disc should be moved.

Lab 12

225

Course Manual (C Programming)

EngGen 131 S2 2013

Consider some correct outputs for this program (user input is in bold):
Please enter the number of discs: 1 1 clockwise

Please enter the number of discs: 2 1 anti-clockwise 2 clockwise 1 anti-clockwise

Please enter the number of discs: 3 1 clockwise 2 anti-clockwise 1 clockwise 3 clockwise 1 clockwise 2 anti-clockwise 1 clockwise

Please enter the number of discs: 4 1 anti-clockwise 2 clockwise 1 anti-clockwise 3 anti-clockwise 1 anti-clockwise 2 clockwise 1 anti-clockwise 4 clockwise 1 anti-clockwise 2 clockwise 1 anti-clockwise 3 anti-clockwise 1 anti-clockwise 2 clockwise 1 anti-clockwise

For what values of n does it take your program too long to solve? This is an example of exponential growth the list of solutions grows extremely quickly as the value of n increases (it basically doubles every time n increases by 1).

Lab 12

226

Course Manual (C Programming)

EngGen 131 S2 2013

Reviewing with Arop


The final project has now been handed in. In this lab, you will have access to a small number of projects developed by your peers, and you are to review each of the projects and provide constructive feedback. Feel free to comment on the style of the code that you see and make suggestions for improvement. If you are not able to complete your reviews during the lab session, you may complete them outside of your scheduled lab time, up until the end of the week the deadline is 10:00pm, Friday 25th October.

_______________________________________________

Lab 12

227

Course Manual (C Programming)

EngGen 131 S2 2013

Lab 12

228

Course Manual (C Programming)