105
Programming compensatory course Materials fot the InfoGeoLog Project Faculty of Mathematics and Computer Science University of Lodz Banacha 22 90–238 Lod´ z winter semester 2013/1014

Programming compensatory coursecybula/c++/scriptC++.pdf · The programs stored in the memory are in the form of machine code of a given computer system, i.e., have the form of binary

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Programmingcompensatory course

Materials fot the InfoGeoLog Project

Faculty of Mathematics and Computer ScienceUniversity of Lodz

Banacha 2290–238 Lodz

winter semester 2013/1014

Contents

I Basics of programming - C 2

1 Basics of C 5

2 Making decisions 16

3 Loops 24

4 Arrays 34

5 Pointers 42

6 Functions 45

7 Structures 55

II Object-Oriented Programming - C++ 64

8 Objects, data abstraction 65

9 Classes, data protection 70

10 Operator overloading 75

11 Inheritance and composition 80

12 Polymorphism 89

13 Exception handling 96

14 Templates and standard library 100

1

Part I

Basics of programming - C

2

Introduction

Computers can be treated as devices aimed at storing and processing informa-tion. Computer’s operation is controlled by a program it runs. Most computersperform their operations on a very primitive level - e.g., they can know how toincrease a number by one or to check whether a number is greater than zero.The basic operations of a computer system form the computer’s instruction set.

The elements of a computer most involved in running a program are the cen-tral processing unit - CPU (a processor) and the (internal) memory. The latterstores the executed program’s instructions and data, one per a cell or a groupof cells. As the memory cells consist of bits, the instructions are represented asbinary numbers. In turn, a component of the CPU called a CU (a control unit),reads the instruction from the memory and supervises their execution.

The programs stored in the memory are in the form of machine code of agiven computer system, i.e., have the form of binary numbers correspondingdirectly to specific machine instructions and locations in the computer’s mem-ory. The programs of this form can therefore be executed by the CU “as theyare”. However, writing programs in the machine code is rather complicated,although it was the only way the computers could be programmed after beingfirst developed. The next advance was developing assembly languages whichenable the programmers to work on a slightly higher level, e.g., to use sym-bolic names referring to specific locations in the memory. To be executed, aprogram in the assembly language has to be translated into a machine code,which is done by a special program called an assembler. However, assembly lan-guages still preserve the one-to-one correspondence between their statementsand machine code instructions, which makes the programs dedicated to specificmachines and therefore non-portable. The next step in programming develop-ment were high-level programming languages. Each language of this kind offersa set of instructions and statements, execution of which corresponds to manydifferent machine instructions. Codes of programs in high-level languages arenot designated for a particular computer architecture.

To execute a program in a high-level language, the statements of its codeneed to be translated into the particular instructions of the computer. A com-puter program capable to do this is called a compiler. The program code isusually written in a text file. The first step of the compilation process consistsin checking whether the program statements in that file confirm to the syntaxand semantics of the language. If there are no errors, the compiler translateseach statement into the equivalent statement or statements in the assemblylanguage, and then - the statements into actual machine instructions. The re-sulting binary code, called an object code, is written into another file - an objectfile, of the name ending with “.o” or “.obj”. The next step is linking, which

3

IGL - Programming

consists in linking together the object program and other programs it uses, like(sub)programs from system libraries and other (sub)programs stored in files pro-cessed by the compiler previously. The resulting excutable object code is readyto be executed on the computer, and is stored in an executable file, usually ofthe name ending with “.out” (Unix) or “.exe” (DOS, Windows).

The first part of this course is devoted to programming in C, the second part- to object-oriented programming in C++.

4

Chapter 1

Basics of C

The chapter introduces some basic elements of the C language.

A first program

In the C/C++ programming language lowercase and uppercase letters are dis-tinct. However, it does not matter where a line of a program begins, i.e., eachindentation is acceptable.

C programs consist of functions and variables. Each function contains state-ments and instructions specifying the operations to be done; variables are usedto store values used during these operations. The functions have names given bythe programmer; each name being a proper identifier can be used, but the namemain has a special meaning: the program begins its execution at the beginningof the main function. The main function can call (run) other functions, bothwritten in the same file and in other files; among them - elements of standardlibraries.

Consider a simple example program which displays the text

Hello :-)

I’m your first program

The program code looks as follows:

#include<s t d i o . h>

int main (void ){p r i n t f ( ”He l lo :−) \n” ) ;p r i n t f ( ” I ’m your f i r s t program” ) ;return 0 ; /∗ end o f the program ∗/}

Listing 1.1: A first program

The first line of the program tells the compiler that the program uses someresources from the standard input/output library (stdio). The second onedefines the main function, specifying its name (here main), list of arguments(parameters), i.e., values which should be provided to the function when it is

5

IGL - Programming CHAPTER 1. BASICS OF C

called, and the type of the result. The list of arguments is given in brackets (

); the keyword void means that the function accepts no arguments. The resultis of the type int, which stands for an integer value.

The statements of the function are enclosed in braces { }. There are threestatements in this case: the first two are calls of the function printf fromthe stdio library; the third one ends the execution of main and returns tothe system a status value of 0 (zero is used by convention to indicate that theprogram completed successfully). Each statement is terminated by a semicolon.

The calls of functions in the program are of the form: the function namefollowed by a parenthesised list of parameter values to be passed to the function.Parameters of printf are here character strings (in quotes) to be displayed onthe screen. The last two characters of the first string, i.e., the backslash andthe letter n form a special sign - the newline character. Some other sequencesare \a for an alert (a bell character), \t for tab, \\ for backlash, and \" fora quote. A complete list can be found for example at http://crasseux.com/

books/ctutorial/Special-characters.html.The text between /* and */ is a comment and is not taken into account by

the compiler. The text commented can consist of several lines.

Variables and data types, arithmetic expressions

The next program illustrates using numeric values and variables.

#include<s t d i o . h>

int main (void ){

int sum , b , a=20;int c ;

b=10;sum = a+b ;p r i n t f ( ” adding %i and %i g i v e s %i \n\n” , a , b , sum) ;p r i n t f ( ” g ive a number : ” ) ;s can f ( ”%i ” ,&a ) ;p r i n t f ( ” g ive a second number : ” ) ;s can f ( ”%i ” ,&c ) ;sum=a∗c ;p r i n t f ( ”mul t ip ly ing %i and %i g i v e s %i \n\n” , a , c , sum) ;p r i n t f ( ” i n t e g e r d i v i s i o n r e s u l t : %i , the remainder : %i \n ” ,

a/c , a%c ) ;return 0 ;

}

Listing 1.2: A program with simple arithmetics

In order to store values used in the program one shoud declare variables. Allthe variables must be declared before they are used, usually at the beginning ofthe function before executable statements. A declaration specifies the propertiesof the variable, i.e. its name and type (e.g., int a). The program declares fourvariables: sum, a, b and c, all being of the type int (integer variables). Thedeclaration enables the compiler to generate the correct instructions for storing

6

IGL - Programming CHAPTER 1. BASICS OF C

and handling values of this variable. Each name of a variable in C must beginwith a letter or the underscore, and can be followed by any combination ofletters, underscores and digits. Small and capital letters are distinguished (i.e.,a and A are different variables).

Each variable not assigned a value in the program stores a random valueof the appropriate type. In order to assign a value we can use the assignmentoperator =. Example assignments in the program are: b=10 and sum = a+b; itis also allowed to use assignments like a = a+b, i.e., the ones in which the samevariable is used to compute the value to be assigned and to point to the placewhere this value will be stored (reading the value, i.e. the operations from theright side of = are performed first). The assignment can be also used to givean initial value to a variable (to initialise it) while the declaration, as it is donewith a. Notice that sum and b are not initialised this way (to achieve this, onehas to write for example int a=0, b=7, sum=0;).

Most calls of the function printf aim at displaying not only a text, butalso values of variables or arithmetic expression. The string given as the firstparameter contains in some places the sequence of characters %i which meansthat an integer value is to be displayed in this place. The values replacing %i’sare taken from the further parameters of printf, in the order in which they arelisted.

Values of variables can be also read from the input, which is done by usingthe function scanf from the stdio library. The first parameter of scanf isa string specifying what types of values are to be read from the input. Thenotation is similar as in printf - i.e., %i is used to specify an integer value. Thesecond parameter specifies where the value read should be stored, which is doneby giving the name of the variable preceded by the ampersand (&). Informally,the meaning of &a is “refer to the memory area aimed at storing the variablea”. The details will be discussed in th further part of the course.

The program uses integer values only. Other basic data types available inthe language are listed and explained below.

Data Types

The basic data types in C are:

• int aimed at storing integer values

• float aimed at storing single-precision floating point numbers

• double storing double-precision floating point numbers

• char capable of holding one character,

• Bool aimed at storing logical values, denoted by 0 and 1 1

Unlike in mathematics, each numeric type has a certain range of values, whichfollows from its representation in computer’s memory2. There are also several

1C standards 1999 (denoted C99) or later2Each type is assigned a fixed number of bit, and each value of this type is represented

using this number of bits, which limits the range of values. For example, if we assume thata type uses 8 bits and represents nonnegative integer values, then the smallest number is 0(written as the binary sequence 00000000 stored in bits, and the greatest value is 11111111 inbinary, i.e. 256).

7

IGL - Programming CHAPTER 1. BASICS OF C

qualifiers which can be applied to the basic types: short, long, long long3

signed, unsigned. Not all of them apply to all the types; more precisely

• short, long and long long apply to integers, enabling either to restrictor to extend the range of values stored. Each compiler is free to chooseits own sizes of int, short int and long int not violating the rules thatshort and int should be at least 16 bits long, long should take at leas32 bits, and short is not longer than int, int is not longer than long,which in turn is not longer than long long. When declaring variables ofthe types short int, long int and long long int the word int can beomitted.

• signed and unsigned apply to char or integer types. Unsigned numberare always positive or zero; plain chars can be either signed or unsigned,but printable characters are always positive.

• long can also apply to double, offering extended–precision floating pointnumbers. The sizes of all the floating point types are implementation-dependent, but float has to be not longer than double which in turn isnot longer than long double.

The ranges of the types can be learned out by using the libraries limits.h

(for integer types) and float.h (for floating-point types), and printing the val-ues of appropriate constants defined there. An example for int is shown below;the names of constants for other types can be found at http://www.cplusplus.com/reference/climits/ and http://www.cplusplus.com/reference/cfloat/:

#include<s t d i o . h>#include<l im i t s . h>

int main (void ){

p r i n t f ( ” the range o f the i n t type i s from %i to %i ” ,INT MIN ,INT MAX) ;

return 0 ;

}

Listing 1.3: Displaying the range of int

Constants

Any number, single character, or character string is known as a constant.

• integer constants are sequences of one or more digits, possibly precededby the minus sign (e.g., 123 or -123). It is not allowed to use spaces orother signs to separate groups of digits (e.g., 1 200 000 and 12,000 arenot valid integer constants). In order to understand an integer constantas being expressed in base 8 (in octal notation) it must be preceded withthe digit 0 and consist of digits from the range 0-7 (e.g., 020, which isthe value 16 in decimal). If the integer constant is preceded by zero and

3C standards 1999 or later

8

IGL - Programming CHAPTER 1. BASICS OF C

the letter x either lowercase or uppercase) the value is considered as beingexpressed in hexadecimal notation (base 16). It has to be composed ofthe digits from the range 0-9 and the letters a-f or A-F (e.g., 0xAF561).

As it has been said before, displaying integer values at the terminal isobtained by using the format character %i in the format string (i.e., inthe first argument of printf). It is allowed to use %d instead (which isequivalent to %i). For octal notation one should apply %o; the value isdisplayed without the leading zero unless we use the format character %#o.For hexadecimal notation one shoud use %x to display the value withoutthe leading 0x, and %#x otherwise.

• floating point constants can be expressed by sequences of digits con-taining a decimal point. The digits before or after the decimal point canbe omitted, the number can be preceded by the minus sign. Some exam-ples of float constants are 12.3, -3. and .001. The constants can bealso expressed in scientific notation consisting of a float, a letter e or E,and an integer denoting the power of 10 by which the number before e

is to be multiplied (an example of such a constant is -1.7e-4, which isinterpreted as −1.7 ·10−4). Hexadecimal notation for floats consists of thepreceding 0x followed by some hexadecimal digits, p or P, and a binaryexponent (e.g., 0x0.3p10 represents 3

16 · 210).

Displaying floats requires using in the printf format string %f for the“normal” format, and %e for the scientific format. Using %g enables printfto decide which format should be used.

• double constants look like floating point constants, except for they cancontain more significant digits. Usually all the floating-point constantsare treated as double values; to explicitely express a float constant oneshould add f or F to the end of the number (e.g., -1.2f).

To display double values, the format chaacters %f, %g and %e can be used.

• character constants are obtained by enclosing a character in a pair ofsingle quotation marks (e.g., ’a’, ’4’, ’;’). Constants like ’\n’ corre-sponding to special characters are proper character constants as well.

In format strings of printf use %c to display values of the type char.

• Bool constants are just integer constants 0 and 1; the standard librarystdbool.h defines the values true, false and bool.

• long and long long integer constants look like integer ones except fortheir (wider) range. To indicate that a given number should be treated aslong we add to the number the suffix L, lower- or uppercase (e.g., 12345L),for long long one should use double L. To display these constants, theformat characters i, d, o and x should be preceded by l ot ll respectively(e.g., %lx will display the long value in the hexadecimal format).

• long double constants are obtained and displayed by applying the rulesfor long to the rules for double (e.g., 12.3e4L is a long double constant).

• there is no way to explicitely write a constant of type short; in order toprint an integer value as short one should precede the format characteri, d, o or x by the letter h (e.g., %hi).

9

IGL - Programming CHAPTER 1. BASICS OF C

• all the unsigned constants are obtained by adding u or U after the constantof the appropriate type. The letter can be combined with L’s for longtypes.

It can be mentioned here that format characters for printf can containadditional elements modifying the way the values are displayed. For exam-ple, %4i denotes that the width of the number printed is at least 4 charac-ters (if this is to much to print the number some leading spaces are added).Similarly, %4.2f allows to modify the way of displaying a float - it will con-tain at least four digits before the decimal point, and two digits after it. Amore complete description of printf format characters can be found at http:

//www.cplusplus.com/reference/cstdio/printf/.

Besides declaring variables, one can declare constants in programs, which isdone by adding the keyword constant at the beginning of the declaration:

int a = 2; declares a variable (initialised to 2), whileconst int a = 2; declares a constant.

As it is easy to guess, values of variables can be changed by the program exe-cution, while values of costants cannot.

Arithmetic expressions

The avalable arithmetic operators are: +, -, * (multiplication), / (division) and% (modulus). It is obvious that if both their operands are of the same type X,the result is of the type X as well. The above holds for the division as well, i.e.,applying “/” to two integers gives an integer result (so the operator means theinteger division), otherwise the “standard” division is applied (see the examplebelow):

#include<s t d i o . h>int main (void ){

p r i n t f ( ”25 / 2 = %i \n” , 25/2) ; /∗ r e s u l t : 12 ∗/p r i n t f ( ” 25 .0 / 2 .0 = %f ” , 2 5 . 0 / 2 . 0 ) ; /∗ r e s u l t : 12.5 ∗/return 0 ;

}

Listing 1.4: The division operator

The modulus operator is defined for integers only, and computes, roughlyspeaking, the remainder resulting from the division of two integers; more pro-cisely its result is defined by the expression a = a/b ∗ b+ a%b.

The precedence (priority) of the operators +, -, * and / is standard (like inmathematics); the precedence of % is equal to the precedence of * and /. It isallowed to use parentheses “( )” (possibly nested); the expressions containingthem are evaluated in the same way as in algebra.

When an operator has operands of different types, they are converted to acommon type. The automatic conversions convert a “less precise” (“narrower”)type into a “more precise” (“wider”) one, as this can be done without losinginformation (e.g., 22+2.0 converts 22 to float). However, expressions which can

10

IGL - Programming CHAPTER 1. BASICS OF C

lead to losing information, like assigning a float to an integer or a longer integerto a shorter are not illegal, although a warning can be generated by a compiler.An example of such a conversion is presented in the listing below:

#include<s t d i o . h>#include<l im i t s . h>

int main (void ){

long long x=10000000000000LL ;int y ;y=x ;p r i n t f ( ”%l l i converted to %i − maximal p o s s i b l e va lue : %i

” , x , y , INT MAX) ;return 0 ;

}

Listing 1.5: A conversion which can lose information

Its result (under my compiler) is10000000000000 converted to 1316134912 - maximal possible value:

2147483647

The next program shows some examples of conversions described above:

#include<s t d i o . h>int main (void ){

int n1 , n2=12;f loat f 1 =12.89 , f 2 ;

n1=f1 ;p r i n t f ( ”%f as s i gned to an i n t e g e r v a r i ab l e g i v e s %i \n” , f1 ,

n1 ) ;f 2= n2 ;p r i n t f ( ”%i as s i gned to a f l o a t v a r i ab l e g i v e s %f \n” , n2 , f 2

) ;

f 1 = n1 + 2 . 3 ;p r i n t f ( ”%i added to %f g i v e s %f \n” , n1 , 2 . 3 , f 1 ) ;

f 1 = n2 /10 ;p r i n t f ( ”%i d iv ided by 10 g i v e s %f \n” , n2 , f 1 ) ;f 1 = n2 /10 . 0 ;p r i n t f ( ”%i d iv ided by 10 .0 g i v e s %f \n” , n2 , f 1 ) ;

return 0 ;}

Listing 1.6: Examples of conversions

The output produced by the program is

12.890000 assigned to an integer variable gives 12

12 assigned to a float variable gives 12.000000

12 added to 2.300000 gives 14.300000

11

IGL - Programming CHAPTER 1. BASICS OF C

12 divided by 10 gives 1.000000

12 divided by 10.0 gives 1.200000

As in can be seen, the float-to-integer conversion just truncates the decimalportion of the number. The integer-to-float conversion does not change thevalue of the number; the value is just stored in the floating point form. Thefurther lines show that if the operands of an operator are integers the result ifthe expression is computed using the integer arithmetics (the same applies toshort, long and long long); if at least one operand is a floating-point value,the operation is performed as a floating-point operation.

From the listings 1.4 and 1.6 we could work out how to “force” C to applyreal-arithmetics computations to expressions containing integer constants (thesolution was to add “.0” to at least one integer value used). However, such atrick cannot be applied to expressions consisting of integer variables. In sucha case one has to use explicit conversion, using a unary operator called a typecast. When we use a construction of the form(typename) expression

the expression is converted to the type given. More precisely, the constructionoperates as if the expression was assigned to a variable of the given type (whichmeans that the rules of conversion described above still apply) which is thenused instead of this construction. The “original” value (e.g., of a variable theconversion is applied to) resmains unchanged. Some examples are shown in thelisting below:

#include<s t d i o . h>int main (void ){

int a , b , r e s ;f loat r e s u l t ;p r i n t f ( ” type an i n t e g e r ” ) ; s can f ( ”%i ” ,&a ) ;p r i n t f ( ” type a second i n t e g e r ” ) ; s can f ( ”%i ” ,&b) ;r e s u l t = ( f loat ) a / ( f loat )b ;p r i n t f ( ” the quot i ent o f %i and %i i s %f \n” , a , b , r e s u l t ) ;r e s = ( int ) r e s u l t ;p r i n t f ( ” the r e s u l t converted to i n t e g e r i s %i ” , r e s ) ;return 0 ;

}

Listing 1.7: Explicit conversions

The output for 17 and 3 is

the quotient of 17 and 3 is 5.666667

the result converted to integer is 5

Notice that the conversion to int is done in the same way as described before,i.e., the decimal part of the number is truncated.

Assignment, Increment and Decrement Operators

The C language contains several “unusual” operators not existing in many otherlanguages. The first group of them are assignment operators, combining arith-metic operations and assignments. Their general format is op=, where op can

12

IGL - Programming CHAPTER 1. BASICS OF C

be +, -, *, /, %, e.g., +=, -= etc. The meaning of a statement of the formvariable op= expression

is to apply the operator op to the variable on the left and the expression on theright, taking variable as the left (first) operand, and to store the result backin the variable on the left-hand side of the operator. For example,a += 10

should be interpreted asa = a + 10

anda /= x + y

meansa = a / (x+y).

The next group of “unusual” operators are these of increment and decrement.The increment operator ++ adds 1 to its operand, while the decrement operator-- subtracts 1. They can be used either as prefix operators (before a variable,e.g., ++n or as postfix operators (after a variable, e.g. n++). The expression++n (--n respectively) increments (decrements, resp.) n before its value is used,while n++ (n-- respectively) increments (decrements resp.) n after its valuehas been used. The difference can be seen in the output of the program inListing 1.8:

#include<s t d i o . h>int main (void ){

int k=10, m=10, n=10, p=10;

n++;p r i n t f ( ” the new value o f n i s %i \n” , n) ;−−m;p r i n t f ( ” the new value o f m i s %i \n” , m) ;n = k++;p r i n t f ( ” a f t e r n = k++ we have n=%i and k=%i \n” , n , k ) ;m = ++p ;p r i n t f ( ” a f t e r m = ++p we have m=%i and p=%i \n” , m, p) ;return 0 ;

}

Listing 1.8: Explicit conversions

which looks as follows:

the new value of n is 11

the new value of m is 9

after n = k++ we have n=10 and k=11

after m = ++p we have m=11 and p=11

The value of k became increased after using the previous value (i.e., assigning itto n), while the value of p became increased before using it (i.e., before assigningthe value of p to m).

Integer representation of char

The values of the type char are represented as integers corresponding to thevalues of the characters in the machine’s character set. For example, the charac-

13

IGL - Programming CHAPTER 1. BASICS OF C

ter constant ’A’ is represented by 65, i.e., the value of A in the ASCII characterset. Due to this, the constructions like these shown in the program below.

#include<s t d i o . h>int main (void ){

char c ;int n ;

n = 10 + ’A ’ ;p r i n t f ( ” the va r i ab l e n equa l s %i or in other words %c \n” , n

, n) ;p r i n t f ( ” type a lowercase l e t t e r ” ) ;s can f ( ”%c” , &c ) ;p r i n t f ( ” the corre spond ing c a p i t a l l e t t e r i s %c \n” , c + ( ’A ’

− ’ a ’ ) ) ;

return 0 ;}

Listing 1.9: Characters treated as integers

Mathematical functions

The standard library math.h introduces mathematical constants and fumctions.Some examples of the functions are:

• pow of two arguments: a base and an exponent, both of the type double,returning the double value equal to baseexponent,

• sqrt returning the square root of the argument, both the argument andthe result are of the type double,

• trigonometric functions: sin, cos, tan, acos etc.; the argument and theresult of each of them is double as well,

• log and log10 computing respectively the natural logarithm and the com-mon logarithm of the argument (again, the argument and the result areof the type double,

• exp computing the exponential function etc.

It should be mentioned that the C99 standard introduces similar functions alsofor other floating-point types, i.e., for float and for long double.

The library can (but not must) introduce also some mathematical constants(not being part of the standard of C, neither ANSI nor C99)4. A complete list ofthese constants can be found for example at http://www.gnu.org/software/

libc/manual/html_node/Mathematical-Constants.html; the most popularare M PI (the value of the number π), M PI 2 (the value of π

2 ), M E (the value of

the number e being the base of natural logarithms) and M SQRT2 (√

2).A program using some elements of the library is presented in Listing 1.10

4For example, under Dev-C++ the constants are available if the compiler option ensuringcompatibility with the ANSI C standard is set to No.

14

IGL - Programming CHAPTER 1. BASICS OF C

#include<s t d i o . h>#include<math . h>

int main (void ){

f loat r , a , b , ang d ;double area , ang r ;

p r i n t f ( ” g ive the rad iu s o f a c i r c l e > ” ) ; s can f ( ”%f ” , &r ) ;area = 2∗ M PI ∗ r ;p r i n t f ( ” i t s area : %f \n\n” , area ) ;

p r i n t f ( ” g ive the l eng th s o f two s i d e s o f a t r i ang l e> ” ) ;s can f ( ”%f ” , &a ) ; s can f ( ”%f ” , &b) ;p r i n t f ( ” g ive the ang le between them ( in degree s ) > ” ) ;s can f ( ”%f ” , &ang d ) ;ang r = ( ang d ∗ M PI) /180 ; /∗ degrees to rad ians ∗/area = 0 .5 ∗ a∗b ∗ s i n ( ang r ) ;p r i n t f ( ” the area i f the t r i a n g l e : %f ” , area ) ;

return 0 ;}

Listing 1.10: Using mathematical constants and functions

Exercises

1. Write a program which displays the ranges of all the arithmetic typesmentioned in the section.

2. Write a program which computes the results of the following expressions:

• 23−4(−1)3·(2.5+4)

• 3 · 12345−456734·4 − 49

7

3. Write a program which reads 5 integers and computes their sum and theirarithmetic mean

4. Write a program which computes the volume and the area of a sphere ofthe radius read from the input.

5. Write a program which reads lengths of the sides of a triangle, and com-putes the area (hint: use the Heron’s formula)

15

Chapter 2

Making decisions

One of the important elements of each programming language are these whichenable to decide whether a certain sequence of statements should be executed ornot, or which of the sequences should be chosen for execution. Making decisionis a fundamental property, as many algorithms contain elements of this kind(see for example a fragment of the algorithm searching for roots of a quadratictrinomial:...compute the discriminant of the quadratic trinomialif the discriminant is a negative number there are no rootsif the discriminant equals 0 there is one roototherwise there are two roots...).

However, before we learn out how to make the program decide which se-quence of statements should be chosen we should know how to block sequencesof statements in a way enabling to treat them as a single statement (from thesyntactical point of view). We should also know how to express condtions. Theproblems mentioned is explained below.

Blocks

An expression such as n++, printf(...) etc. becomes a statement when it isfollowed by a semicolon (n++; printf(...);). The semicolon is a statementterminator. Statements and declarations can be grouped together using braces{ and }, forming a compound statement or block equivalent syntactically to asingle statement. There is no semicolon after the brace ending the block.

Specifying conditions

The condition under which a set of statements is to be executed is specifiedusing an expression which can be evaluated to true or false. The simple versionof the expression is the one in which we use a relational operator to comparetwo other expressions. The relational operators are> <= => >

16

IGL - Programming CHAPTER 2. MAKING DECISIONS

all of the some precedence, and== !=

of the meanting “equal” and “not equal”, of the precedence just below theoperators listed above. Some examples of such expressions are

size >= 0

b*b-4*a*c < 0

answer == ’y’

etc. It is also possible to create compound relational tests using logical con-nectives && denoting logical AND (conjunction) and || denoting logical OR(disjunction). The precedence of these connectives is lower than that of thecomparison operators mentioned above, so for example the expression x>y &&

y<100 will be evaluated in the way expected. In turn, the precedence of && ishigher than that of ||. In general, the expressions containing logical connec-tives are evaluated from left to right, and the evaluation is stopped as soon asthe truth or falsehood of the result is known (so, for example, for x=1 and y=5

the second conjunct of the expression x>y && y<100 will not be evaluated, asevaluating the first one is sufficient to determine that the whole expression isfalse). However, although C provides strict rules expressing the precedence ofthe operations, it is beneficial to use parentheses to increase readability of theexpression and to avoid problems following from a mistaken assumption aboutthe precedence of the operators in the expression.

Now we are ready to present conditional statements, which is done in thesections below.

The if statement

The basic version of the if statement enables to make a decision whether a partof the program should be executed or not. Its formal syntax is

if (expression)

program statement

where the expression, specifying the condition under which the program sta-

tement is executed, is an expression of the form described in the previous sec-tion. The program statement can be either a single simple statement (endedwith a semicolon), or a compound statement (block) enclosed in braces as de-scribed in page 16. If the expression evaluates to true the program statement

is executed, otherwise it is omitted.Three examples of programs using the if statements are presented in the

listings below. The program in Listing 2.1 computes the absolute value of thenumber. The value computed is stored in the same variable as the one providedas an input. Thus, if the user types a negative number the variable is assignedthe opposite one.

The program in Listing 2.2 computes the area and the circumference of arectangle of the sides given as an input. If at least one of the input values isincorrect (i.e., not greater than 0) the computations (in braces) are omitted,and the program returns an “error message”, i.e. the value 1; otherwise theresults are printed and the “normal termination” value 0 is returned.

17

IGL - Programming CHAPTER 2. MAKING DECISIONS

The program in Listing 2.3 displays the information whether the numbergiven as an input is even or odd. The task is realised by two separate if

statements of complementary conditions.

#include<s t d i o . h>

int main (void ){

int n ;p r i n t f ( ” g ive a number > ” ) ; s can f ( ”%i ” ,&n) ;i f (n<0)

n = −n ;

p r i n t f ( ” the abso lu t e va lue o f the number i s %i ” , n ) ;return 0 ;

}

Listing 2.1: Computing the absolute value of a number

#include<s t d i o . h>int main (void ){

int a , b , v , c ;p r i n t f ( ” type a s i d e o f a r e c t ang l e : ” ) ; s can f ( ”%i ” ,&a ) ;p r i n t f ( ” type the second s i d e : ” ) ; s can f ( ”%i ” ,&b) ;i f ( a>0 && b>0){

v = a∗b ;p r i n t f ( ” i t s area i s %i \n” , v ) ;c=2∗(a+b) ;p r i n t f ( ” i t s c i r cumfe rence i s %i ” , c ) ;return 0 ;

}return 1 ;

}

Listing 2.2: Computing the area and the circumference of a rectangle

#include<s t d i o . h>int main (void ){

int a ;p r i n t f ( ” g ive a number > ” ) ; s can f ( ”%i ” , &a ) ;i f ( a % 2 == 0)

p r i n t f ( ” even number ” ) ;i f ( a % 2 != 0)

p r i n t f ( ”odd number” ) ;return 0 ;

}

Listing 2.3: Checking whether a number is even or odd

18

IGL - Programming CHAPTER 2. MAKING DECISIONS

The if-else construct

Another form of the if statement is the one which enables to choose one of twoexisting possibilities, i.e., to execute either one or another statement. Its formalsyntax is

if (expression)

program statement 1

else

program statement 2

If the expression evaluates to true the program statement 1 is executed;otherwise the program executes the program statement 2. Similarly as be-fore, each of the program statements can be a simple (single) statement or acompound statement.

Although the above result can be achieved by using two separate if state-ments of complementary conditions (as in the program in Listing 2.3), using theif-else construct allows to increase both the readability of the code and theprogram effectiveness (as the condition is tested only once).

Some examples of programs using this statement are presented below. Thefirst one (Listing 2.4) is a modification of that in Listing 2.3.

#include<s t d i o . h>int main (void ){

int a ;p r i n t f ( ” g ive a number > ” ) ; s can f ( ”%i ” , &a ) ;i f ( a % 2 == 0)

p r i n t f ( ” even number ” ) ;else

p r i n t f ( ”odd number” ) ;return 0 ;

}

Listing 2.4: Checking whether a number is even or odd - version 2

The program in Listing 2.5 computes the solution of a linear equation of theform ax + b = 0. If a 6= 0 the solution is x = −b

a , otherwise the result dependson the value of b - if b = 0 the equation is 0x + 0 = 0 and there is an infninitenumber of values of x for which this equality holds; if b 6= 0 we get contradictionfor each value of x. Checking the value of b in the case when a = 0 is done in anested if-else construction.

#include<s t d i o . h>int main (void ){

int a , b ;f loat x ;

p r i n t f ( ” type the ( i n t e g e r ) c o e f f i c i e n t s o f a l i n e a r equat ionax + b = 0 \n a = ” ) ;

s can f ( ”%i ” , &a ) ;p r i n t f ( ” b = ” ) ; s can f ( ”%i ” , &b) ;i f ( a == 0){

19

IGL - Programming CHAPTER 2. MAKING DECISIONS

i f (b == 0)p r i n t f ( ” i n f i n i t e number o f s o l u t i o n s \n” ) ;

elsep r i n t f ( ”no s o l u t i o n s \n” ) ;

}else{

x = ( f loat )(−b) /a ;p r i n t f ( ”one s o l u t i o n x= %f \n” , x ) ;

}return 0 ;

}

Listing 2.5: Solving a linear equation

The else-if construct

A third version of the if statement enables to choose for execution one of severalavailable statements. Its formal syntax is

if (expression_1)

program statement 1

else if (expression_2)

program statement 2

else if .....

.....

else

program statement N

The statement operates as follows: if the condition expressed by expression 1

holds then the program executes program statement 1 and the whole con-struction terminates (the other possibilities are not checked). If the conditiongiven by expression 1 does not hold, the condition given by expression 2 istested, and if it holds program statement 2 is executed and the constructionis terminated; otherwise we go to testing the condition given by the next ex-pression, and so on. If none of the conditions holds the program executes theprogram statement N. Analogously as before, the program statements can besimple or compound statements.

A simple example if a program using the else-if construct is presented inListing 2.6. The next program (Listing 2.7) is a modification of the program inListing 2.5, with nested ifs replaced by the else-if construct.

#include<s t d i o . h>int main (void ){

f loat a ;p r i n t f ( ” type a number > ” ) ; s can f ( ”%f ” , &a ) ;i f ( a>0)

p r i n t f ( ”a p o s i t i v e va lue ” ) ;else i f ( a == 0)

p r i n t f ( ” zero ” ) ;else

p r i n t f ( ”a negat ive va lue ” ) ;

20

IGL - Programming CHAPTER 2. MAKING DECISIONS

return 0 ;}

Listing 2.6: Testing the sign of a number

#include<s t d i o . h>int main (void ){

int a , b ;f loat x ;

p r i n t f ( ” type the ( i n t e g e r ) c o e f f i c i e n t s o f a l i n e a r equat ionax + b = 0 \n a = ” ) ;

s can f ( ”%i ” , &a ) ;p r i n t f ( ” b = ” ) ; s can f ( ”%i ” , &b) ;i f ( a == 0 && b == 0)

p r i n t f ( ” i n f i n i t e number o f s o l u t i o n s \n” ) ;else i f ( a == 0 && b != 0)

p r i n t f ( ”no s o l u t i o n s \n” ) ;else{

x = ( f loat )(−b) /a ;p r i n t f ( ”one s o l u t i o n x= %f \n” , x ) ;

}return 0 ;

}

Listing 2.7: Solving a linear equation - version 2

The switch statement

Another type of a statement enabling to decide which statements should beexecuted is a switch statement. Its form is

switch (expression)

{case value 1:

program statements 1

case value 2:

program statements 2

....

default:

program statements N

break;

}

The expression is successively compared against the values which mustbe simple constants or constant expressions, and must be different for all thecases. If a case whose value is equal to the value of theexpression is found, thestatements corresponding to this case are executed. There can be many state-ments, not enclosed in braces. After executing the corresponding statementsthe program executes the statements of the next case etc. It is possible to avoid

21

IGL - Programming CHAPTER 2. MAKING DECISIONS

continuing with the next cases by placing the break statement at the end ofthe statements corresponding to a given case. Executing break terminates thewhole switch construction. A good practice is to put the break statement afterthe last case, to avoid problems when a new case at the end will be added.

The special optional case called default is executed if the value of expressiondoes not match any of the case values.

It should be noticed that the else-if construction can be replaced by theswitch construction with breaks at the end of the list of statements for eachcase.

A simple example of using this construction is provided in Listing 2.8. Noticethat “lists of cases” occurring in the program are in fact many subsequent casesof empty sets of instructions.

#include<s t d i o . h>int main (void ){

char a ;p r i n t f ( ” type a charac t e r ” ) ; s can f ( ”%c” ,&a ) ;switch ( a ){

case ’ 0 ’ : case ’ 1 ’ : case ’ 2 ’ : case ’ 3 ’ : case ’ 5 ’ :case ’ 6 ’ : case ’ 7 ’ : case ’ 8 ’ : case ’ 9 ’ :

p r i n t f ( ”a number \n” ) ;break ;

case ’ a ’ : case ’ b ’ : case ’ c ’ :p r i n t f ( ”a l e t t e r at the beg inning o f the alphabet \n ” )

;break ;

default :p r i n t f ( ” I cannot r e c ogn i s e the value \n” ) ;break ;

}return 0 ;

}

Listing 2.8: Testing a character

Exercises

1. Write a program which reads three numbers and if they can be lengthsof the sides of a triangle (i.e., they satisfy all the appropriate conditions),computes the area and the circumference of this triangle.

2. Write a program which solves a quadratic equation of the form ax2 + bx+c = 0. The coefficients of the equation should be read from the input. Theprogram should work properly for all the possible values of the coefficient(i.e., also for the case of a = 0).

3. Write a program testing whether the year read as the input is a leap year.

4. Write a program which reads the radius of a circle and allows the user tochoose by typing A or B whether the area or the circumference is to becomputed.

22

IGL - Programming CHAPTER 2. MAKING DECISIONS

5. Write a program which reads two dates in the format dd-mm-yyyy andsays which of them is earlier.

23

Chapter 3

Loops

In all the programs presented and written so far the statements were executed(possibly conditionally) in the order in which they occur in the program code.However, an important feature is the possibility of executing the same part ofthe code several times. This can be done using loops.

The while loop

The first kind of loops is the while loop. Its formal structure is

while (expression)

program statement

The loop operates as follows: the expression specifying a condition is evalu-ated, and if its is true the program statement (which can be a single, simplestatement or a compound statement in braces) is executed. After execution ofthis statement the expression is evaluated once again, and if the result is truethe program executes the program statement, and so on. The process contin-ues until the expression evaluates as false, which terminates execution of theloop. The above means that if the condition is false from the very beginning,the contents of the statement (the program statement) is never executed.

A simple program using this construction is presented in Listing 3.1.

#include<s t d i o . h>

int main (void ){

int a , n ;p r i n t f ( ”The program pr i n t s p o s i t i v e i n t e g e r s from 1 to a

bound you g ive . \n” ) ;p r i n t f ( ”How many numbers do you want to p r i n t ?” ) ;s can f ( ”%i ” ,&n) ;a=1;while ( a<=n){

p r i n t f ( ”%i , ” , a ) ;a = a+1;

24

IGL - Programming CHAPTER 3. LOOPS

}return 0 ;

}

Listing 3.1: Printing positive integers

The program reads an upper bound on positive integers to be printed (storedin n). The variable a storing the value to be printed currently is initially set to1. If the value stored in a does not exceed n, it is printed, and next the valueof a is increased by 1. If this vale does not exceed n as well it is printed, thevalue of the variable a is increased again, and so on. If in a step the value ofa becomes greater than n, the condition evaluates to false and the execution ofthe loop is finished.

The do-while loop

The structure of the previous loop caused that its inner instructions may beomitted if the condition was false from the very beginning. By contrast, theloop introduced in this section executes its inner instructions at least once, asthe condition is tested after the pass through the loop body.

The formal structure of this loop is

do

program statement

while (expression);

(notice the semicolon after the expression. Execution of the statements is asfollows: The program statement (being a simple or a compound statement)is executed first, and after that the expressionn is evaluated. If the evalua-tion is true the program statement is executed once again, the expression isevaluated again, and so on. If the evaluation of the expression after executingprogram statement gives the result true, the whole loop is terminated.

An example program with this construction is presented in Listing 3.2:

#include<s t d i o . h>

int main (void ){

int a , n ;p r i n t f ( ”The program pr i n t s p o s i t i v e i n t e g e r s from 1 to a

bound you g ive . \n” ) ;p r i n t f ( ”How many numbers do you want to p r i n t ?” ) ;s can f ( ”%i ” ,&n) ;a=1;do {

p r i n t f ( ”%i , ” , a ) ;a = a+1;

} while ( a<=n) ;return 0 ;

}

Listing 3.2: Printing positive integers - version 2

25

IGL - Programming CHAPTER 3. LOOPS

The for loop

The next kind of loop is the for loop. The statement is of the form

for ( init expr; loop condition; loop expr )

program statement

Similarly as before, the program statement is a simple or compound expression.The expressions in brackets specify respectively:

• an expression which sets up the initial values of the variables “responsible”for the loop execution (init expr). The values are set before the loopbegins,

• an expression specifying the condition(s) that are necessary for the loopto execute (loop condition). The condition is checked before executingthe loop body, so it is possible that the body is not executed at all (if thecondition is false before its first execution). The loop continues as long asthe condition holds,

• an expression which is evaluated each time after the body of the loopis executed (loop expr). In most cases the expression specifies how thevariables occuring in the previous two expressions change after each cycleof loop execution.

A simple example shows how the loop operates in practice. In Listing 3.3 wehave a program which prints all the integer from an interval [a, b] whose boundsare given by the user. The program prints also the sum of these numbers.

#include<s t d i o . h>int main (void ){

int i , a , b , sum ;p r i n t f ( ” type the ( i n t e g e r ) bounds o f an i n t e r v a l : ” ) ;s can f ( ”%i ” , &a ) ; s can f ( ”%i ” , &b) ;sum = 0 ;p r i n t f ( ” i n t e g e r s in the i n t e r v a l : \n” ) ;for ( i=a ; i<=b ; i++){

p r i n t f ( ”%i \n” , i ) ;sum = sum + i ;

}p r i n t f ( ” t h e i r sum equa l s %i ” , sum) ;return 0 ;

}

Listing 3.3: Printing all the integers from an interval, together with their sum

Consider first the part of the program realising printing all the integers from[a, b]. To do this, we apply the for loop. After reading a and b, the init expr

of the loop sets the variable i, representing an integer to be printed, to the valuebeing the lower bound of the interval, stored in a. Then it is checked whether theloop condition holds, i.e., whether i is not greater than the upper bound of theinterval (i.e., the value stored in b). If so, the body of the loop is executed, i.e.,the value of i is printed. Next we should go to printing a next value. A “receipe”

26

IGL - Programming CHAPTER 3. LOOPS

for the next integer if we have the “current” one says that the “next” one isequal to the current one plus 1; this “receipe” is written as the loop expr (i++,or, in other words, i = i+1). So, after executing the loop body the “receipe” isapplied, and the value of i increases. The loop, and the whole process, “returns”to the step in which it is checked whether the loop condition holds. If it holds,we print the current value of i, otherwise the whole process is finished. Noticethat the last value of i for which the loop condition holds is that equal to b,so the upper bound of the interval is the last value printed by the loop.

The sum of all the numbers printed is computed parallely to the printingprocess. To compute the sum we do not need to remember all the numbers: itis sufficient to add the value to the sum computed so far when we “deal with”(print) a given value; after this it can be forgotten. The approach describedis implemented in the program: we declare the variable sum aimed at storingthe sum of the numbers printed so far, which means that after printing allthe numbers if interest the variable will store the sum of all of them. Oncethe “current” number, stored in i, is printed by the loop, it is also added tothe “sum computed so far” (sum = sum + i). Obviously the first executionof the loop adds the current value of i to the value put to sum before theloop begins. As the declaration “int sum;” sets the variable to an “random”(unpredictable) value, we should remember to initialise it properly before we useit in the program code. The sum of zero numbers is 0, so the program initialisesthe sum this way (sum = 0) before entering the loop.

A common error while using the for loop is placing the semicolon after thebrackets containing expressions. The body of the loop of the form

for ( init expr; loop condition; loop expr ) ;program statement

(i.e., the one with the semocolon mentioned) consists only of the empty state-ment terminated with this semicolon. Thus, the empty statement is executedseveral times, while the program statement - only once, after executing theloop body (and this execution is independent on the conditions in the loop).

It should be also noticed that each for loop of the form

for ( init expr; loop condition; loop expr )

program statement

can be expressed using the while loop as follows:

init expr;

while ( loop condition )

{program statement

loop expr;

}

However, using the for loop is in some cases more convenient.

Variants of the for loop

The description above introduced a basic version of the for loop. However,some syntactic variations are possible. We discuss all of them below.

27

IGL - Programming CHAPTER 3. LOOPS

Multiple expressions

It is possible to include multiple expressions in any of the fields in brackets ofthe for loop. The expressions should be separated by commas. For example,the loop

for (i=1, j=0; i<=5; i++, j=j+2)

...

sets up two index variables: i initialised to 1 and j initialised to 0 (both ofthem before the loop begins). The condition “guarding” the loop executiontests only the value of i. After each execution of the loop body the value ofi is incremented by 1, while the value of j is incremented by 2. The loop weconsider can be used, for example, to deal with 5 subsequent even numbers: theeven numbers are represented by j, and their numbers in the sequence - by i.

Omitting fields

In some applications it is possible to omit some expressions, leaving empty somefields in braces of the for loop. For example, the loop of the form

for ( ; ; )

...

can execute infinitely many times (an infinite loop). The same can occur if weomit the loop condition, e.g., the loop

for (i=0; ; i++)

...

can execute theoretically for all the positive integers (obviously, we probablyencounter the problem following from going out of the range of the type). Inturn, we can omit the init expr if the variable used in the rest of expressionsof the loop is initialised by the earlier part of the program.

Declaring variables

The standards starting from C99 enable to declare the variables as a part of theinit expr. For example, instead of writing

int i;

...

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

...

which is the only possibility in the ANSI C standard, we can write

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

...

However, in such a case the variable i is known only inside the body of the loop)is its local variable) and cannot be used or accessed outside of the loop. Theinstruction

28

IGL - Programming CHAPTER 3. LOOPS

for (int i=0, j=0; i <=10; i++, j=j+2)

...

defines two local variables i and j and sets their values accordingly. Noticealso that in the construction

int j=0;

for (int j=2; j<6; j=j+2)

printf("local j: %i \n ", j);

printf("old j: %i \n ", j);

uses two different j’s: the first one is visible outside of the loop and its valueis still 0; the second one is visible inside the loop, hiding the “outer” j. Its valueincreases according to the expression in the loop. The result of executing thisfragment of the code is therefore of the form:

local j: 2

local j: 4

old j: 0

The break statement

Sometimes we need to stop executing the loop as soon as a certain condition issatisfied (for example an error occurs). To do this the statement of the formbreak;

can be used. The break statement causes the program to exit from the loopimmediately. The execution of the program is then continued starting from thefirst instruction following the loop. The statement can be used in each loop(for, while, do-while). When used in a set of nested loops, the innermostloop in which the break occurs is terminated.

The continue statement

The continue statement, of the formcontinue;

is similar to the break statement except for it does not cause the loop to ter-minate. Instead, the current execution of the loop body is finished, i.e., allthe statements that appear in the body after continue are skipped. Next, theloop continues in the normal way (for example with new values of the variablesappearing in the loop expr of the for loop). As before, the statement can beused in a loop of each kind.

The difference between the break and continue statements is presented inthe output of the program in Listing 3.4:

#include<s t d i o . h>int main (void ){

int i ;p r i n t f ( ”Using \” break \” : \n” ) ;for ( i =1; i<=5; i++){

29

IGL - Programming CHAPTER 3. LOOPS

i f ( i==3) break ;p r i n t f ( ”%i ” , i ) ;

}

p r i n t f ( ”\nUsing \” cont inue \” : \n” ) ;for ( i =1; i<=5; i++){

i f ( i==3) continue ;p r i n t f ( ”%i ” , i ) ;

}

return 0 ;}

Listing 3.4: Using break and continue

which looks as follows:

Using "break":

1 2

Using "continue":

1 2 4 5

Use examples

Below we present some examples of programs using loops and implemeting typ-ical problems.

Searching for a maximal value

The problem to be solved is: to write a program which reads integers fromthe input until the user decides to stop typing them, and outputs the maximalvalue.

The program realising this task looks as follows:

#include<s t d i o . h>int main (void ){

int a , max , int roduced = 0 ;char answer ;

p r i n t f ( ” are you going to type a number? (n − no ) ” ) ;s can f ( ”%c” ,&answer ) ;while ( answer != ’n ’ && answer != ’N ’ ){

p r i n t f ( ” type a number > ” ) ;s can f ( ”%i ” ,&a ) ; getchar ( ) ;int roduced++;i f ( int roduced == 1 | | a > max)

max = a ;p r i n t f ( ”do you want to cont inue ? (n − no ) ” ) ;s can f ( ”%c” ,&answer ) ;

}

30

IGL - Programming CHAPTER 3. LOOPS

i f ( int roduced > 0)p r i n t f ( ” the maximal va lue typed was %i ” , max) ;

return 0 ;}

Listing 3.5: Searching for the maximal value

The first part to be discussed is reading numbers until the user decides tostop. The user is asked whether he wants to input a number: the first timebefore the program enters the loop, which enables not to read the numbers atall, and next at the end of the loop body. Answering n (or N) results in finishingthe reading process.

It should be also explained why after reading an integer value using thestatement scanf("%i",&a), the function getchar() is called. If the call of thisfunction was removed, the program would not allow to answer the question “doyou want to continue”, going immediately to reading a next number. Roughlyspeaking, the reason for this is that when we input a number and confirm it bypressing Enter, the characters corresponding to all the keys pressed are put intothe keyboard buffer. The function scanf reading an integer “consumes” fromthis buffers all the numbers which form an integer value, and stops consuming onthe first “inappropriate” character1 (which corresponds here to pressing Enter),leaving it in the buffer (together with all the following characters). The nextcall of the scanf function, intending this time to read a character, checks thecontents of the buffer, and “consumes” the character left there, without caus-ing the program to wait for an input. Using getchar() overcomes the aboveproblem, as this function just consumes one character from the buffer.

Concerning searching for the maximal number typed, the algorithm is asfollows: we store a “current maximum”, i.e., the maximal value of the onestyped so far. If the value introduced recently is greater than the “current max”,the value becomes the current maximum. After reading all the numbers the“current maximum” is the maximum for all of them.

The crucial element of the algorithm is to initialise the maximum properly.To do this we introduce a variable counting the numbers typed (introduced).Reading the first number results in setting max to this value. The counter isalso used at the end of the program to prevent typing the maximal value if nonumbers were provided.

Printing ordered and unordered pairs

Consider a situation when we have a number of people (say n), and we want tolist all the possible pairs in which one person is a leader and the second is itssubordinate, and all the possible pairs being just groups of two persons withoutany “roles” assigned. The task is realised by the following program:

#include<s t d i o . h>int main (void ){

int n , i , j ;

1More precisely, reading a number omits white characters (i.e., spaces etc.) found at thebeginning of the contents of the buffer. These white spaces are “consumed” not influencingthe reading process and not causing any problems.

31

IGL - Programming CHAPTER 3. LOOPS

p r i n t f ( ” g ive the numer o f people : ” ) ;s can f ( ”%i ” , &n) ;p r i n t f ( ” p o s s i b l e ordered pa i r s ( pa i r s with a l e ade r ) : \n” ) ;for ( i = 1 ; i<=n ; i++)

for ( j =1; j<=n ; j++)i f ( i != j )

p r i n t f ( ” the l e ade r : %i , the subord inate : %i \n” , i , j ) ;

p r i n t f ( ”\n\ npo s s i b l e unordered pa i r s ( groups o f two persons ): \n” ) ;

for ( i = 1 ; i<=n ; i++)for ( j=i ; j<=n ; j++)

i f ( i != j )p r i n t f ( ” %i and %i \n” , i , j ) ;

return 0 ;}

Listing 3.6: printing ordered and unordered pairs of different elements

Each of the subtasks is done using nested for loops. When the ordered pairsare to be printed, the loops are:

for (i = 1; i<=n; i++)

for (j=1; j<=n; j++)

....

and operate as follows: first the outer loop sets the variable i to 1 and executesits body - i.e., starts the inner loop. Executing the inner loop results in changingthe value of j from 1 to n, which means that the resulting pairs (i, j) are (1, 1),(1, 2), ..., (1, n). When the body of the outer loop is executed, the value of ichanges to 2 and executing the body - i.e., the inner loop - starts again, whichagain results in changing the values of j from 1 to n, printing the pairs (2, 1),(2, 2), ..., (2, n), and so on. The process repeats until the pairs (n, 1), (n, 2), ...,(n, n) are printed, which corresponts to the last execution of the outer loop. Itshould be mentioned that in the program above the pairs (k, k) with k = 1, . . . , nare not printed due to considering pairs of people (it is impossible to create apair choosing twice the same person).

Concerning unordered pairs, printing them is done by the loops

for (i = 1; i<=n; i++)

for (j=i; j<=n; j++)

....

Comparing with the code above, the only difference is that the inner loop ini-tialises j to the value of i instead of to 1. This results in printing the pairs:

• for i equal to 1: (1, 1), (1, 2), ..., (1, n),

• for i equal to 2: (2, 2), ..., (2, n),

• and so on.

In the program in the listing the pairs consisting of two identical values areomitted, from the reason as above.

32

IGL - Programming CHAPTER 3. LOOPS

Exercises

1. Write a program which reads the integers from the input until the value0 is given, and prints these pairs of subsequent values whose average isgreater than an integer value G given as an input.

2. Write a program that calculates the sum of the digits of an integer (forexample, the sum of digits of 1234 is 1+2+3+4 = 10).

3. Write a program that reads a positive integer N, and ouptuts the infor-mation how many positive integers should be added to obtain a smallestnumber greater than N.

4. Write a program which prints the first K prime numbers (where K is givenby the user).

5. Write a program which prints the values of k! for k = 1, . . . , n, where n istyped by the user (where k! = 1 · 2 · . . . · (k − 1) · k, and 0! = 1! = 1).

33

Chapter 4

Arrays

Most programming languages offer a capability to define a set of ordered dataitems known as an array. Informally, a one-dimensional array can be seenas a box divided into parallel compartments, each of which contains a value ofthe same type and is labelled by unique “identifier” (index). In the case of theC language, the labelling (indexing) is done using a sequence of nonnegativeintegers starting with 0. An example of one-dimensional array storing integersis presented in Fig. 4.1. An array is a single variable despite storing manyelements.

Figure 4.1: An array

A one-dimensional array is declared in the following way:

type of array elements array name[ array length ];

For example,

int tab[5];

declares an array named tab capable to store 5 elements (i,e,. the array of thelength 5) each of which is an integer (i.e., of the type int). The next example:

float Dec2013Temps[31];

defines an array which can store the temperatures in December 2013, the valuefor each day in a separate cell. The declaration results in reservation in com-puter’s memory a space sufficient to store an appropriate number of values ofthe given type (e.g., 5 integers while declaring tab).

The i-th element in an array x is referred by x[i] (e.g., Dec2013Temps[3],tab[0]). Notice that due to the indexing starting with 0 the arrays do notcontain elements of the number given in brackets in the array definition - thelast element of the array tab is tab[4], and the last element of Dec2013Temps

34

IGL - Programming CHAPTER 4. ARRAYS

is Dec2013Temps[30]. An array element of a certain type can be used in all thecontexts in which a variable of this type can occur.

The capability to represent sets of related data items by a single array enablesus to develop concise and efficient programs. To see this, compare two fragmentsof a program dealing with December temperatures. In the case of using no arraysstoring the values would look as follows:

float Dt1, Dt2, Dt3, Dt4, Dt5, Dt6, Dt7, Dt8, Dt9, Dt10,

Dt11, Dt12, Dt13, Dt14, Dt15, Dt16, Dt17, Dt18, Dt19, Dt20,

Dt21, Dt22, Dt23, Dt24, Dt25, Dt16, Dt17, Dt28, Dt29, Dt30,

Dt31;

printf("type the temperatures in December days:\n ");

printf("1: "); scanf("%i", &Dt1);

printf("2: "); scanf("%i", &Dt2);

printf("3: "); scanf("%i", &Dt3);

...

while using arrays enables us to do the same in a more compact way:

float Dt[31];

int d;

printf("type the temperatures in December days:\n ");

for (d=0; d<31; d++)

{

printf("%i: ",d); scanf("%i", &Dt[d]);

}

Using for loops is a common way of manipulating arrays. The loop variableis usually used to go through the indices of array cells.

Arrays can be initialised when they are declared. This is done by listing thevalues of the elements of the array, separating them by commas and enclosingthe whole list in braces:

int a[3] = {1,2,3};

One can consider also multi-dimensional arrays. A declaration of a multi-dimensional array specifies its “lengths” for all the dimension, using the numbersin square brackets (one for each dimension). For example,

int d[2][7];

declares a two-dimensional array, consisting of two “rows” and seven “columns”.Its initialisation:

int d[2][7] = {{1,2,3,4,5,6,7},{11,12,13,14,15,16,17}};

35

IGL - Programming CHAPTER 4. ARRAYS

shows the fact that in C such an array is in fact a one-dimensional array eachof whose elements is an array (storing 7 elements in this case). We refer tothe elements of such an array by providing the array name and a pair of arrayindices, each from the appropriate range corresponding to the given dimension,and each given in its own square brackets:

d[0][0] = 1;

(the form with a comma-separated list of indices, i.e., d[0,0] is incorrect).Similarly as before, a convenient way of manipulating such arrays is to use(nested) for loops, one for each dimension.

Use examples

Below we present some examples of programs using arrays and implementingtypical problems

Searching for an element satisfying a condition

The problem to be solved is to write a program which creates an array of thelength provided by the user, reads its contents, displays it, and then prints amessage informing whether the array contains a positive number smaller thanthe arithmetic mean of all the elements of the array.

#include<s t d i o . h>

int main (void ){

int n , i , sum , e x i s t s ;f loat avg ;

p r i n t f ( ” prov ide the l ength o f the array : ” ) ;s can f ( ”%i ” , &n) ;i f (n>0){

int tn [ n ] ;

/∗ read ing the e lements o f the array ∗/p r i n t f ( ”Provide the e lements o f the array : \n” ) ;for ( i =0; i<n ; i++){

p r i n t f ( ”%d : ” , i ) ;s can f ( ”%d” , &tn [ i ] ) ;

}

/∗ p r i n t i n g the array ∗/p r i n t f ( ”Your array : \n” ) ;for ( i =0; i<n ; i++)

p r i n t f ( ”%d ” , tn [ i ] ) ;

/∗ computing the a r i t hme t i c mean ∗/sum = 0 ;for ( i =0; i<n ; i++)

36

IGL - Programming CHAPTER 4. ARRAYS

sum = sum + tn [ i ] ;avg = sum / ( f loat )n ;p r i n t f ( ”\nThe a r i thmet i c mean f o r the array i s %f \n” ,

avg ) ;

/∗ s earch ing f o r an element s a t i s f y i n g the cond i t i on ∗/e x i s t s = 0 ;for ( i =0; i<n ; i++)

i f ( tn [ i ]>0 && tn [ i ]< avg ){ e x i s t s = 1 ; break ; }

/∗ d i s p l a y i n g the f i n a l messages ∗/i f ( e x i s t s == 1)

p r i n t f ( ”There i s a p o s i t i v e va lue sma l l e r than theaverage \n” ) ;

elsep r i n t f ( ”There i s no p o s i t i v e va lue sma l l e r than the

average \n” ) ;}

return 0 ;}

Listing 4.1: searching for an array element satisfying a condition

In order to create an array of the length provided by the user we first readthis length (the variable n), and next if the value given is positive create thearray (tn). As in some C versions (e.g., in ANSI C) declarations should come atthe beginning of a block, we should surround the declaration of tn and all theoperations referring to tn with a pair of braces. However, in our program weneed no “additional” pair of them to obtain a block mentioned - the appropriateblock is “provided” by the if statement testing whether the value given as thelength of the array is nonnegative.

Searching whether the array contains an element satisfying a given conditionis done using an algorithm which can be coloquially called “an algorithm withan opinion”. The idea is as follows: we have an initial “opinion” expressed by avalue of a variable (here exists set to 0, i.e., denoting that no element of interestexist). Then, we search through the array (or, in general, a set of data), lookingfor something which changes this opinion (here for a positive element smallerthan the average). If such an element is found, we change the “opinion” (hereset exists to 1, i.e., to a value stating that an element satisfying the propertiesof interest exists); we can also finish the search as the opinion “it exists” is validafter finding one element satisfying the condition, and no further search shouldinfluence this opinion (if one element exists, an element exists). If searchingthrough the whole array causes no modification of the initial “opinion”, wereturn it “as it is” (here: if no element satisfying the condition is found, thefinal answer corresponds to exists equal to 0, i.e. to non-existence of anyelement of interest).

Counting disjoint pairs satisfying a condition

The next problem is to write a program which creates an array of integers of thelength provided by the user, reads its contents, displays it, and next displays the

37

IGL - Programming CHAPTER 4. ARRAYS

number of disjoint pairs consisting of neighbouring elements whose arithmeticmean is greater than an integer value L provided by the user (where the disjointpairs are chosen in such a way that the first element of the array is the firstelement of the first pair, and the last element of an array of the odd length doesnot belong to any pair). The solution looks as follows:

#include<s t d i o . h>

int main (void ){

int n , i , L , count ;

p r i n t f ( ” prov ide the l ength o f the array : ” ) ;s can f ( ”%i ” , &n) ;i f (n>0){

int tn [ n ] ;

/∗ read ing the e lements o f the array ∗/p r i n t f ( ”Provide the e lements o f the array : \n” ) ;for ( i =0; i<n ; i++){

p r i n t f ( ”%d : ” , i ) ;s can f ( ”%d” , &tn [ i ] ) ;

}

/∗ p r i n t i n g the array ∗/p r i n t f ( ”Your array : \n” ) ;for ( i =0; i<n ; i++)

p r i n t f ( ”%d ” , tn [ i ] ) ;

p r i n t f ( ”\nprovide the value o f L : ” ) ;s can f ( ”%d”,&L) ;

/∗ count ing the pa i r s and d i s p l a y i n g the r e s u l t ∗/count = 0 ;for ( i =0; i<n−1; i=i +2){

p r i n t f ( ” the pa i r cons ide r ed : %d , %d\n” , tn [ i ] , tn [ i+1]) ;

i f ( ( tn [ i ]+tn [ i +1]) /2 .0 > L )count ++;

}p r i n t f ( ”The number o f pa i r s whose average i s g r e a t e r

than L : %d\n” , count ) ;}

return 0 ;}

Listing 4.2: Counting disjoint pairs of elements satisfying a condition

The disjoint pairs tn[i], tn[i+1] are chosen using the loop statement:

for (i=0; i<n-1; i=i+2) ...

38

IGL - Programming CHAPTER 4. ARRAYS

The condition i<n-1 prevents us from referring to a non-existing element “be-yond the end of the array” (the greatest possible value of i is n-2, and thereforethe last possible pair is tn[n-2], tn[n-1]; recall that the index n-1 is the great-est one for the array of the length n). The step i = i+2 causes that the pairsconsidered are disjoint.

Using a two-dimensional array

In order to show how to manipulate multidimensional arrays we present a pro-gram computing the sum of two two-dimensional matrices. The program codeis as follows:

#include<s t d i o . h>

int main (void ){

int nc , nr , i , j ;

p r i n t f ( ” prov ide the number o f rows o f the matrix : ” ) ;s can f ( ”%i ” , &nr ) ;p r i n t f ( ” prov ide the number o f columns o f the matrix : ” ) ;s can f ( ”%i ” , &nc ) ;

i f ( nc>0 && nr>0){

int x [ nr ] [ nc ] ;int y [ nc ] [ nr ] ;

/∗ read ing the e lements o f the arrays ∗/p r i n t f ( ”Provide the e lements o f the f i r s t matrix : \n” ) ;for ( i =0; i<nr ; i++)

for ( j =0; j<nc ; j++){p r i n t f ( ” element %d,%d : ” , i , j ) ;s can f ( ”%d” , &x [ i ] [ j ] ) ;}

p r i n t f ( ”Provide the e lements o f the second matrix : \n” ) ;for ( i =0; i<nr ; i++)

for ( j =0; j<nc ; j++){p r i n t f ( ” element %d,%d : ” , i , j ) ;s can f ( ”%d” , &y [ i ] [ j ] ) ;}

/∗ p r i n t i n g ∗/p r i n t f ( ”Adding matr i ce s : \n” ) ;for ( i =0; i<nc ; i++){

for ( j =0; j<nr ; j++)p r i n t f ( ”%d ” , x [ i ] [ j ] ) ;

39

IGL - Programming CHAPTER 4. ARRAYS

i f ( i == nc /2) p r i n t f ( ” + ” ) ;else p r i n t f ( ” ” ) ;

for ( j =0; j<nr ; j++)p r i n t f ( ”%d ” , y [ i ] [ j ] ) ;

i f ( i == nc /2) p r i n t f ( ” = ” ) ;else p r i n t f ( ” ” ) ;

for ( j =0; j<nr ; j++)p r i n t f ( ”%d ” , x [ i ] [ j ]+y [ i ] [ j ] ) ;

p r i n t f ( ”\n” ) ;}

}return 0 ;

}

Listing 4.3: Computing the sum of matrices

Exercises

1. Write a program which creates an array of integers of the length providedby the user, reads its contents, displays it, and then prints a messageinforming:

• whether the numbers in the array are ordered (in the ascending orthe descending order)

• whether the array is symmetric

• whether a value occurs in the array more than once

2. Write a program which creates an array of integers of the length providedby the user, reads its contents, displays it, then moves its contents oneposition right (i.e,, the first element should move to the second position,the second to the third one etc., the last one should move to the firstposition, e.g., the array 1,2,3,4 should change to 4,1,2,3), and displays thearray once again.

3. Write a program which creates an array of integers of the length providedby the user, reads its contents, displays it, and next prints all these orderedpairs created from the elements of the array which consist of elements ondifferent positions and the distance between the arithmetic mean of thevalues in the pair ane the arithmetic mean of all the elements of the arrayis smaller than G, where G is an integer provided by the user (example:for the array 1,7,-2,3,0 and G=2 the program should print the pairs (1,3)(3,1), (1,0), (0,1), (7,-2), (-2,7), (7,0), (0,7), (3,0), (0,3), (-2,3), (3,-2) inan arbitrary order).

4. Write a program which creates an array of integers of the length providedby the user, reads its contents, displays it, and then, depending on thechoice of the user:

40

IGL - Programming CHAPTER 4. ARRAYS

• computes the number of odd elements (values) and the number ofeven elements in the array,

• displays the length of a longest fragment of the array containingpositive values only,

• reads the values L and R (the numbers should be positive and notgreater than the length of the array) and then reverses the order ofvalues in the fragment of the array starting at the L-th element andending at the R-th element.

41

Chapter 5

Pointers

Pointers, i.e., variables containing addresses of other variables (or just of mem-ory areas) are a very important element of programming languages. In C,pointers enable to efficiently represent complex data structures, to change val-ues passed as arguments to functions, to work with memory which is allocated“dynamically”, and to deal efficiently with arrays.

To deal with pointers we need two unary operators. The first one, &, gives theaddress of an object; the second one, *, called the indirection or dereferencingoperator, when applied to a pointer accesses the object the pointer points to.The way the operator works is shown in the description below.

Assume we have integer variables x and y, i.e.,

int x = 124, y;

Defining a pointer variable enabling to refer to x is done as follows:

int *ip;

The asterisk defines that the variable ip is of a type pointer to int, which meansthat ip can be used to refer to a memory area storing an integer. The statement

ip = &x;

causes ip to point to the memory area storing the variable x (see Fig. 5.1 fortwo alternative ilustrations).

Figure 5.1: A pointer (ip) pointing to (the memory area storing) a variable x

The assignment

y = *ip;

causes the integer variable y to be assigned a value which is stored in the memoryarea the pointer ip points to - in our case the value 124. In turn, the assignment

*ip = 3;

42

IGL - Programming CHAPTER 5. POINTERS

results in putting the value 3 to the memory area pointed by ip, i.e., to the onestoring the variable x. Therefore, x stores now the value 3 as well, despite no“explicit” assignment.

In turn, if we declare a second pointer variable, i.e.,

int *iq;

the assignment

iq = ip;

copies the contents of ip into iq, which means - due to the fact the pointer vari-ables store addresses of the memory areas they “point” to - that the assignmentmakes iq point to the same object the pointer ip points to.

An illustration of the above operation is the program

#include<s t d i o . h>

int main (void ){

int x=124 , y ;int ∗ ip , ∗ ip2 ;

ip = &x ;p r i n t f ( ”x : %d , ip−> %d \n” , x , ∗ ip ) ;

∗ ip = 3 ;p r i n t f ( ”x : %d , ip−> %d \n” , x , ∗ ip ) ;

ip2 = ip ;p r i n t f ( ”x : %d , ip−> %d , ip2−> %d \n” , x , ∗ ip , ∗ ip2 ) ;

∗ ip2 = 4 ;p r i n t f ( ”x : %d , ip−> %d , ip2−> %d \n” , x , ∗ ip , ∗ ip2 ) ;return 0 ;

}

Listing 5.1: Using pointers

whose result is

x: 124, ip-> 124

x: 3, ip-> 3

x: 3, ip-> 3, ip2-> 3

x: 4, ip-> 4, ip2-> 4

If ip points to the integer variable x, then *ip can be used in all the contextswhere x could - e.g.,

*ip = *ip - 3;

decrements the value stored in the area ip points to by 3.

Pointers can be used to manipulate arrays. If we declare an array of integers,i.e., a block of several consecutive blocks each of which contains an integer, anda pointer to integers, i.e.,

43

IGL - Programming CHAPTER 5. POINTERS

int a[10];

int *pa;

then by the assignment

pa = a;

we make the pointer to point to the beginning of this block, i.e., to the firstelement of the array. This follows from the fact that the value of an arrayvariable is in fact the address of zero element of this array. However, the samemay be achieved by the assigment

ip = &a[0];

If pa points to an element of an array, then from the “pointer arithmetic”pa+1 points to its next element (adding 1 to a pointer moves this pointer to thenext consecutive memory “block” of the size corresponding to the size of thetype the given pointer points to). Similarly, pa+i, for i being an integer, pointsto the i-th element beyond pa. The array a can therefore be “traversed” andread not only using the “traditional” loops

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

scanf("%d ",&a[i]); /* reading */

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

printf("%d ",a[i]); /*printing*/

but also loops of the form

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

scanf("%d", (pa+i)); /* reading */

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

printf("%d ", *(pa+i)); /* printing */

(the above comparison is a good oportunity to notice that scanf requires as aparameter the address of a variable whose value is read; due to this we use&a[i] in the “traditional” version of the code. However, pa+i stores an addressitself, so no “additions” are here needed).

Due to the fact that the name of an array is a synonym for the location ofits initial element, we can write not only pa = a as it was shown before, butalso can refer to a[i] using the notation *(a+i). Similarly, &a[i] is equivalentto a+i, i.e., a+i is the address of the i-th element beyond the beginning of a.Therefore, the third alternative version of reading and printing the array a is

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

scanf("%d", (a+i));

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

printf("%d ", *(a+i));

Exercises

1. Write the programs from the exercises in the previous section using point-ers.

44

Chapter 6

Functions

Functions are one of basic “building blocks” of C programs. There are many“standard” functions, collected in libraries and used in our programs, like forexample scanf or printf. Moreover, each C program contains a function calledmain, and this function can be seen as the program itself (i.e., running theprogram corresponds to executing this function). However, it is also possible towrite functions which are “non-main” and “non-standard”, i.e., neither belongto standard libraries provided with the language, nor are executed automaticallywhen the main function runs.

The structure of a function is

type of value returned function name ( list of parameters)

{declarations and statements

return returned value;

}

We will show working with functions by examples.

Simple functions

Consider the following program:

#include<s t d i o . h>#include<math . h>

void PrintAuthor (void ){

p r i n t f ( ”\n ∗∗∗∗ wr i t t en by ME ∗∗∗∗ \n” ) ;}

int Sum3( int a , int b , int c ){

return a+b+c ;}

f loat Tr iang l e ( int a , int b , int c ){

45

IGL - Programming CHAPTER 6. FUNCTIONS

f loat p , P;p = ( a+b+c ) / 2 . 0 ;P = sq r t (p∗(p−a ) ∗(p−b) ∗(p−c ) ) ;return P;

}

int TriangleS idesCheck ( int a , int b , int c ){

i f ( a+b>c && a+c>b && b+c>a )return 1 ;

elsereturn 0 ;

}

int main (void ){

int x1 , x2 , x3 , r ;f loat F;

p r i n t f ( ” g ive three i n t e g e r numbers : ” ) ;s can f ( ”%d %d %d” , &x1 , &x2 , &x3 ) ;r = Sum3(x1 , x2 , x3 ) ;p r i n t f ( ”Their sum equa l s %d \n” , r ) ;p r i n t f ( ”This sum plus 2013 p lus 10 equa l s %d \n” , Sum3( r

, 2013 ,10 ) ) ;

i f ( Tr iangleS idesCheck ( x1 , x2 , x3 )==1){

p r i n t f ( ”The numbers can be l eng th s o f t r i a n g l e s i d e s ; ” ) ;F = Tr iang l e ( x1 , x2 , x3 ) ;p r i n t f ( ” the area o f t h i s t r i a n g l e i s %f \n” , F) ;

}else

p r i n t f ( ”The numbers cannot be l eng th s o f t r i a n g l e s i d e s \n” ) ;

PrintAuthor ( ) ;return 0 ;

}

Listing 6.1: Using simple functions

Besides the function main the program contains four additional functions,named respectively PrintAuthor, Sum3, Triangle and TriangleSidesCheck.They are written in the same file, before the function main begins.

The function PrintAuthor aims at printing the author of the program. Todo this it needs no additional data, so the list of its parameters is empty (whichis indicated by the word void in the parameter list). It returns no result, andtherefore the type of the result is specifies as void as well. The list of statementsto be executed when the function is called contains one element only. Callingthe function in the function main consists in providing its name, followed by theempty brackets (i.e., brackets surrounding the empty list of parameters):

PrintAuthor();

46

IGL - Programming CHAPTER 6. FUNCTIONS

The function Sum3 computes and returns to the program the sum of threeintegers it obtains as the input data. The sum is an integer; due to this thetype of the result returned is given as int. The input data are “transferred”via function parameters (arguments). The list of parameters, given in bracketsjust after the function name, consists of comma-separated elements of the formparameter type parameter name. The names of the parameters are “local”, i.e.,we just need to “name” these data to write the function’s body - without namingthese parameters distinguishing between them and specifying operations whichare to be done to compute the function’s result would be impossible. Thefunction Sum3 has three integer-valued parameters, denoted respectively a, b

and c. The list of statements to be executed consists of one element only, i.e.,of

return a+b+c;

specifying a “prescription” on what the function is to return. The general syntaxof this construct is

return expression;

the value of the expression is returned to the calling routine. The type of itsresult has to be compatible with the type declared as the first element of thefunction (just before the function name). If a function returns a value (i.e., thetype of its result is not void) then each possible run of the function should leadto executing a return statement. Executing it ends the function, so all thestatements placed after it can be seen as an unreachable code.

Concerning function calls, the function Sum3 is called in the main functiontwo times. The first call is of the form

r = Sum3(x1,x2,x3);

which means that the function parameter a obtains the value taken from theprogram variable x1, the parameter b - from the variable x2, and the parameterc from the variable x3. The result returned by the function is stored in thevariable r. The second call is of the form

printf("This sum plus 2013 plus 10 equals %d \n", Sum3(r,2013,10));

i.e., Sum3 is run with the value of the parameter a taken from the variable r,and with b=2013 and c=10. The result of the function is immediately passed toprintf, without storing it in any program variable.

In general, a function call can be used in each such context (each such placeof the program) in which a value of the type returned by this function can occur.

The third function, Triangle, computes the area of a triangle whose sidesare given as the parameters of the function. The structure of this function issimilar to that of Sum3, besides the fact that the function declares two localvariables - p and P, used to compute the final result. Local variables are notseen outside of the function’s body, and disapear when the execution of thefunction finishes.

The last function, TriangleSidesCheck, aims at checking whether the threeintegers it obtains as the parameters can be sides of a triangle. The functionreturns either 0 or 1, denoting false or true respectively; each execution of itscode leads to a (different) return statement.

47

IGL - Programming CHAPTER 6. FUNCTIONS

More about parameters

By default, C passses arguments to functions by value. This means that thefunction creates a copy of a variable for each it is called, and operates on thiscopy without altering the original variable. This is not a problem if the functionneeds to read the values of its parameters to generate the result (as it was forexample in the case of the functions in Listing 6.1). However, if the role of afuction consists in modifying the values of its parameters, working on theircopies does not lead to the result desired. Consider for example the programshown in Listing 6.2

#include<s t d i o . h>

void swap VerWrong ( int x , int y ){

int tmp ;p r i n t f ( ” I n t e r n a l t e s t : b e f o r e swapping : x= %i y = %i \n” ,

x , y ) ;tmp = x ;x = y ;y = tmp ;p r i n t f ( ” I n t e r n a l t e s t : a f t e r swapping : x= %i y = %i \n” , x

, y ) ;}

int main (void ){

int a=4, b=8;

p r i n t f ( ” be f o r e swapping : a= %i b = %i \n” , a , b ) ;swap VerWrong (a , b ) ;p r i n t f ( ” a f t e r swapping : a= %i b = %i \n” , a , b ) ;

return 0 ;}

Listing 6.2: Incorrect function aimed at modifying its parameters

The program contains a (incorrect) function aimed at swapping the valuesof its parameters. However, the result of its execution is

before swapping : a= 4 b = 8

Internal test: before swapping : x= 4 y = 8

Internal test: after swapping : x= 8 y = 4

after swapping : a= 4 b = 8

i.e., in spite of swapping the values inside the function, the final values of a andb are the same as at the beginning (swapping was performed on copies of a andb, not on the variables itselves).

In order to deal with the problem we need to pass to the function the ad-dresses of the variables which are to be modified – working with copies of ad-dresses is equivalent to working with their originals. The parameters of thefunction shoud therefore be pointers, i.e., its specification should be

void Swap (int *px, int *py)

48

IGL - Programming CHAPTER 6. FUNCTIONS

Of course the body of the function should refer to the values pointed by px andpy, i.e., to *px and *py. In turn, calling the function required to provide it withaddresses of variables whose values are to be swapped, i.e., should be

Swap (&a, & b);

A program with this function is shown in Listing 6.3, its result is

before swapping : a= 4 b = 8

after swapping : a= 8 b = 4

#include<s t d i o . h>

void Swap ( int ∗px , int ∗py ){

int tmp ;tmp = ∗px ;∗px = ∗py ;∗py = tmp ;

}

int main (void ){

int a=4, b=8;

p r i n t f ( ” be f o r e swapping : a= %i b = %i \n” , a , b ) ;Swap(&a,&b) ;p r i n t f ( ” a f t e r swapping : a= %i b = %i \n” , a , b ) ;

return 0 ;}

Listing 6.3: Correct function aimed at modifying its parameters

Functions for arrays

It is possible to write functions that take arrays as their arguments. If we wantto obtain a function which works for arrays of a particular length, e.g. 10, wecan write this function in a way similar to the function below:

int minimalElem(int x[10])

{

int i;

int min = x[0];

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

if (x[i]<min) min = x[i];

return min;

}

and call it like this:

int a[10];

.....

printf("minimal value: %d \n", minimalElem(a));

49

IGL - Programming CHAPTER 6. FUNCTIONS

However, we usually need functions which are more “general-purpose” thanthese able to manipulate arrays of a fixed length. A solution is to pass to thefunction an array (without the number of its elements in the brackets), andseparately (using another, additional parameter) the number of elements of thearray:

int minimalElem2(int x[], int numberOfElements)

{

int i;

int min = x[0];

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

if (x[i]<min) min = x[i];

return min;

}

The function is then called like this:

int a[10];

int b[30];

.....

printf("minimal value in a: %d \n", minimalElem2(a,10));

printf("minimal value in b: %d \n", minimalElem2(b,30));

Alternatively, one can pass to the function a pointer to the array and thelength of this array:

int minimalElem3(int *x, int numberOfElements)

{

int i;

int min = x[0];

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

if (x[i]<min) min = x[i];

return min;

}

The function can be called analogously as in the previous case:

int a[10];

int b[30];

.....

printf("minimal value in a: %d \n", minimalElem3(a,10));

printf("minimal value in b: %d \n", minimalElem3(b,30));

It should be noticed also that in fact each of the above three methods passesto the function the address of the first element of the array. Therefore, no copyof the array is created, and the function is able to change the contents of thearray. A program illustrating this is presented in Listing 6.4.

#include<s t d i o . h>

void GetArr ( int ∗ tab , int dl ){

int i ;for ( i =0; i<dl ; i++)

50

IGL - Programming CHAPTER 6. FUNCTIONS

{p r i n t f ( ” g ive the element no . %i : ” , i ) ;s can f ( ”%d” , &tab [ i ] ) ;

}}

void PrintArr ( int ∗ tab , int dl ){

int i ;for ( i =0; i<dl ; i++)

p r i n t f ( ”%d ” , tab [ i ] ) ;

}

int minimalElem3 ( int ∗x , int numberOfElements ){

int i ;int min = x [ 0 ] ;for ( i =0; i<numberOfElements ; i++)

i f ( x [ i ]<min) min = x [ i ] ;return min ;

}

void MultiplyByMin ( int ∗a , int NumberOfElems ){

int min = minimalElem3 (a , NumberOfElems ) ;int i ;

for ( i =0; i<NumberOfElems ; i++)a [ i ] = a [ i ]∗min ;

}

int main (void ){

int tab [ 1 0 ] ;

GetArr ( tab , 1 0 ) ;p r i n t f ( ”\n\n” ) ;PrintArr ( tab , 1 0 ) ;p r i n t f ( ”elem minimalny : %d \n” , minimalElem3 ( tab , 1 0 ) ) ;

MultiplyByMin ( tab , 1 0 ) ;p r i n t f ( ” the array a f t e r mu l t ip ly ing i t s e lements by the

minimal one : \n” ) ;PrintArr ( tab , 1 0 ) ;

return 0 ;}

Listing 6.4: Functions working with arrays

Now it is time for a more complicated example - a function which returns adynamically created array. Consider the program in Listing 6.5. The function todiscuss is ArrayOfPositives, returning an array containing positive elements

51

IGL - Programming CHAPTER 6. FUNCTIONS

from the array given as a parameter.

#include<s t d i o . h>#include<s t d l i b . h>

void GetArr ( int ∗ tab , int dl ){

int i ;for ( i =0; i<dl ; i++){

p r i n t f ( ” g ive the element no . %i : ” , i ) ;s can f ( ”%d” , &tab [ i ] ) ;

}}

void PrintArr ( int ∗ tab , int dl ){

int i ;for ( i =0; i<dl ; i++)

p r i n t f ( ”%d ” , tab [ i ] ) ;

}

int∗ ArrayOfPos i t ives ( int ∗a , int dl , int ∗ r e s S i z e ){

int i , n , t ;int ∗ r e s ;

/∗ computing the s i z e o f the r e s u l t i n g array ∗/n=0;for ( i =0; i<dl ; i++)

i f ( a [ i ]>0) n++;

/∗ c r ea t i n g the array ∗/r e s = ( int ∗) mal loc (n∗ s izeof ( int ) ) ;i f ( r e s == NULL) return NULL;

/∗ copying va l u e s to the array ∗/t=0;for ( i =0; i<dl ; i++)

i f ( a [ i ]>0){

r e s [ t ] = a [ i ] ; t++;}

/∗ r e tu rn ing the r e s u l t s ∗/∗ r e s S i z e = n ;return r e s ;

}

int main (void ){

int tab [ 1 0 ] ;int ∗ tp ;

52

IGL - Programming CHAPTER 6. FUNCTIONS

int r ;

GetArr ( tab , 1 0 ) ;p r i n t f ( ”\n\n” ) ;PrintArr ( tab , 1 0 ) ;p r i n t f ( ”\n” ) ;

tp = ArrayOfPos i t ives ( tab ,10 ,& r ) ;p r i n t f ( ” the array conta in ing p o s i t i v e va lue s from the

o r i g i n a l array %d : \n” , r ) ;PrintArr ( tp , r ) ;f r e e ( tp ) ;

return 0 ;}

Listing 6.5: A function returning a dynamically created array

The function ArrayOfPositives returns the result of the type specified asint*, i.e., a pointer to an integer. It has two “input” parameters - an arraydenoted a, and its length denoted dl. The third parameter is a pointer to aninteger, and is an “output” parameter – it aims at returning the size of theresulting array. Without returning it we would not be able to pass the arrayto any function which requires an array and its size as the input – e.g. to thefunction PrintArray (unless we add in the main program a code computingthe size of the array of positives once again). The structure of the function isas follows: first we compute the size of the resulting array, i.e., count all thepositive values in the array a. The next steps are to allocate memory for theresulting array, to copy to that array all the positive elements from a, and finallyto return both the array and its length.

The array to be returned has to be created dynamically, e.g., using themalloc function – replacing the construct

int *res;

n=0;

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

if (a[i]>0) n++;

res = (int*)malloc(n*sizeof(int));

...

return res;

by a code like

n=0;

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

if (a[i]>0) n++;

{int res[n];

...

return res;

}

would result in creating a local array variable which would be destroyed whenthe execution of the function finishes, which in turn would make the array tp

53

IGL - Programming CHAPTER 6. FUNCTIONS

in the main program to be filled with random values.The statement

res = (int*)malloc(n*sizeof(int));

results in allocating a memory block of the size equal to the size of n blockseach of which stores an integer (the size is given then by n*sizeof(int)). Apointer to this block returned by the allocating function is of a “general” typevoid*, so it must be converted to a pointer to an integer (by using (int*)

before malloc). If allocating memory is unsuccesful (e.g., there is not enoughmemory) the function returns the special value NULL. A pointer of this valuepoints to no place in the memory.

The memory allocated by malloc remains allocated even if the function inwhich the allocation took place is finished. Due to this the array is destroyed“manually” at the end of the program, by using the statement

free(tp);

Using malloc and free is possible after inluding the library stlib at the be-ginning of the program.

Exercises

1. Write a program containing a function checking whether an integer givenas its parameter is a prime number.

2. Write a function which returns an array containing all the prime numbersfrom the array given as a parameter. Each prime number should appearin the resulting table only once.

3. Write a program containing a function which checks whether an arraygiven as its parameter is symmetric.

54

Chapter 7

Structures

Structures (called records in many programming languages) are collections ofone or more variables, possibly of different types, groupped together under asingle name.

A traditional example which justifies introducing structures are personaldata, for example of a student: in order to describe a student, we need his/hername, surname, personal identification number, date of birth, address and manyothers. The data are of different types, but it is convenient to keep all themin a single variable. Another example are points on the plane, represented bytheir coordinates.

A structure is defined as follows:

struct point {int x;

int y;

};

The word struct is obligatory as the first element of the declaration; it isfollowed by the structure name. Next, in braces, we give a list of declarationsof structure members. The definition of the structure is ended by a semicolon(after the right brace).

A structure declaration defines a type. We can declare variables of this typeas below:

struct point A;

The statement declares a variable A which is a structure of the type struct

point.Initialisation of a struct variable looks as follows:

struct point A = { 1, 1 };

(we provide a list of values, and their order in the list corresponds to the orderin the structure definition).

Referring to members of a structure is done using the “dot notation”, i.e.,by

structure variable.member name

55

IGL - Programming CHAPTER 7. STRUCTURES

(e.g., A.x). If we define a pointer to a structure, e.g.,

struct point *pp;

then we can refer to the members of the structure pointed by pp not only likethis

(*pp).x = 11;

but also by

pointer->structure member

i.e., by

pp->x = 11;

Members of structures can be structures itselves.

Example 1: manipulating a group of students

Working with simple and more involved structures can be seen in Listing 7.1:

#include <s t d i o . h>#include <s t r i n g . h>

struct date {int day , month , year ;

} ;

struct student {char name [ 2 1 ] ;char surname [ 3 1 ] ;struct date dateOfBirth ;int index ;int notes [ 6 ] ;

} ;

/∗ −−−−−− f unc t i on s f o r a date −−−−−− ∗/void GetDate ( struct date ∗d){

p r i n t f ( ”day > ” ) ; s can f ( ”%d” , &(d−>day ) ) ;p r i n t f ( ”month > ” ) ; s can f ( ”%d” , &(d−>month) ) ;p r i n t f ( ” year > ” ) ; s can f ( ”%d” , &(d−>year ) ) ;getchar ( ) ;

}

void PrintDate ( struct date d){

p r i n t f ( ”%d−%d−%d” ,d . year , d . month , d . day ) ;}

/∗ −−−−−− f unc t i on s f o r a s tuden t −−−−−− ∗/

char ∗ remove newl ine (char ∗ s )

56

IGL - Programming CHAPTER 7. STRUCTURES

{int l en = s t r l e n ( s ) ;

i f ( len>0 && s [ len−1]== ’ \n ’ ) /∗ i f t h e r e i s a newl ine ∗/s [ len −1] = ’ \0 ’ ; /∗ t runca t e the s t r i n g ∗/

return s ;}

void GetStudent ( struct student ∗ s ){

int i ;

p r i n t f ( ”name : ” ) ;f g e t s ( s−>name , 21 , s td in ) ;f f l u s h ( s td in ) ;/∗ a l t e r n a t i v e methods o f read ing a s t r i n g :g e t s ( s−>name) ; <− no con t r o l o f go ing beyond the end o f

the s t r i n gscan f (”%20s ” , s−>name) ; <− does not a l l ow to read a s t r i n g

con ta in ing spaces∗/

p r i n t f ( ”surname : ” ) ;f g e t s ( s−>surname , 31 , s td in ) ;f f l u s h ( s td in ) ;p r i n t f ( ” date o f b i r th : ” ) ;GetDate(&(s−>dateOfBirth ) ) ;p r i n t f ( ” index number ” ) ;s can f ( ”%d” , &(s−>index ) ) ;p r i n t f ( ” notes : ” ) ;for ( i =0; i <6; i++){

p r i n t f ( ”%d : ” , i +1) ;s can f ( ”%d” , &(s−>notes [ i ] ) ) ;

}getchar ( ) ;

}

void PrintStudent ( struct student s ){

int i ;p r i n t f ( ”name : %s \n” , remove newl ine ( s . name) ) ;p r i n t f ( ”surname : %s \n” , remove newl ine ( s . surname ) ) ;p r i n t f ( ” index : %d\n” , s . index ) ;p r i n t f ( ”born : ” ) ; PrintDate ( s . dateOfBirth ) ; p r i n t f ( ”\n” ) ;p r i n t f ( ” notes : ” ) ;for ( i =0; i <6; i++)

p r i n t f ( ”%d ” , s . notes [ i ] ) ;}

/∗ −−−−−− f unc t i on s f o r a group −−−−−− ∗/void GetGroup ( struct student gr [ ] , int n){

int i ;for ( i =0; i<n ; i++)

57

IGL - Programming CHAPTER 7. STRUCTURES

GetStudent(&gr [ i ] ) ;}

void PrintGroup ( struct student gr [ ] , int n){

int i ;for ( i =0; i<n ; i++){ PrintStudent ( gr [ i ] ) ; p r i n t f ( ”\n” ) ;}

}

/∗ −−−−−− THE MAIN PART −−−−−− ∗/int main (void ){

struct date today ;struct student He ;int gs ;

/∗ read ing and p r i n t i n g a s i n g l e date ∗/GetDate(&today ) ;p r i n t f ( ”\n” ) ;PrintDate ( today ) ;p r i n t f ( ”\n\n” ) ;

/∗ read ing and p r i n t i n g a s i n g l e s tuden t ∗/GetStudent(&He) ;p r i n t f ( ”\n” ) ;Pr intStudent (He) ;

/∗ read ing and p r i n t i n g a group o f s t uden t s ∗/p r i n t f ( ” g ive the s i z e o f your student group : ” ) ; s can f ( ”%d” ,

&gs ) ; getchar ( ) ;{

struct student gp [ gs ] ;GetGroup (gp , gs ) ;p r i n t f ( ”\n\n ∗∗∗ Your group ∗∗∗ \n” ) ;PrintGroup (gp , gs ) ;

}

return 0 ;}

Listing 7.1: Functions and structures

The first structure, date, aims at storing a date. It can be manipulated usingthe functions GetDate and PrintDate. The next structure, student, is moreinvolved - its members are, among others, two character strings (name, surname),a date (dateOfBirth) and an array of integers (notes). The structures ofthis type are manipulated using the functions GetStudent and PrintStudent.Each of these functions calls an above-mentioned function for a date. Finally,the program defines an array of students, and provides two functions aimed atreading/printing such an array (GetGroup, PrintGroup).

What should be commented is the use of character strings, in particular inthe function GetStudent. In order to read a student’s name and surname we use

58

IGL - Programming CHAPTER 7. STRUCTURES

here the function fgets. It has three parameters: the name of the string to beread, its size (in characters) and the source from which it is to be read (here thestandard input, i.e. stdin). The body of GetStudent presents two alternativemethods of reading the name: using the function gets, which, however, doesnot allow to provide any limit on the number of characters read, which canresult in going beyond the string declared, and scanf, enabling to limit thenumber of characters (%20s limits this size to 20 characters, leaving the 21stone for the \’0’ terminating strings in C), but stopping reading characters fromthe input if e.g. it meets a space (which makes reading names like Anne Marieimpossible).

Using fgets for s.name is followed by using fflush for stdin, which resultsin cleaning the input buffer if it is nonempty (which happens if the name typedis longer than the declared length of the string we read, minus one for storing theterminating \’0’). Without cleaning this buffer the remaining characters wouldbe used to fill the surname. A similar role is played by the getchar() function,but this function “consumes” one character only. It is used after scanf’s readingnumbers or single characters, to consume the newline character remaining in theinput buffer (without doing this we would not be able to read a string to beread next, e.g., the name of the first person in the group - it would be “filled”with the newline remaining after reading the group size, i.e., the variable gs).

Example 2: a dynamic queue of integers

A program implementing a dynamic data structure - a queue of integers (see forexample http://en.wikipedia.org/wiki/Queue_(abstract_data_type)) isprovided in Listing 7.2.

#include<s t d i o . h>#include<s t d l i b . h>

struct node{int data ;struct node ∗next ;

} ;

struct queue{struct node ∗head , ∗ t a i l ;

} ;

/∗ a func t i on to ensure t ha t a (new) queue i s empty ∗/void I n i t i a l i s eQu eu e ( struct queue ∗q ){

q−>head=NULL;q−>t a i l=NULL;

}

/∗ p r i n t i n g a queue ∗/void Print ( struct queue q ){

struct node ∗tmp ;tmp = q . head ;p r i n t f ( ”HEAD−>” ) ;

59

IGL - Programming CHAPTER 7. STRUCTURES

while (tmp!=NULL){

p r i n t f ( ”%i−>” , (∗tmp) . data ) ;tmp = (∗tmp) . next ;

}p r i n t f ( ”NULL” ) ;

}

/∗ adding an element at the end o f the queue ∗/void Add( struct queue ∗q , int x ){

struct node ∗new ;

new = ( struct node ∗) mal loc ( s izeof ( struct node ) ) ;i f (new == NULL){

p r i n t f ( ”not enough memory ! \n” ) ; return ;}

(∗new) . data = x ;(∗new) . next = NULL;

i f ( (∗ q ) . head == NULL){

(∗q ) . head = new ;(∗q ) . t a i l = new ;

}else{

(q−>t a i l )−>next = new ;/∗ a l t e r n a t i v e l y : (∗(∗ q ) . t a i l ) . next = new ; ∗/(∗q ) . t a i l = new ;

}

}

/∗ removing the f i r s t e lement o f the queue ,wi th r e tu rn ing i t s va lue to the program ∗/

int Remove( struct queue ∗q ){

int r e t v a l ;struct node ∗ k i l l ;r e t v a l = (∗ (∗ q ) . head ) . data ;k i l l = (∗q ) . head ;(∗q ) . head = (∗ (∗ q ) . head ) . next ;i f ( (∗ q ) . t a i l == k i l l ) (∗q ) . t a i l = NULL;f r e e ( k i l l ) ;

return r e t v a l ;}

int main (void )

60

IGL - Programming CHAPTER 7. STRUCTURES

{struct queue k ;int i , i l e , a ;

I n i t i a l i s eQu eu e (&k) ;p r i n t f ( ”how many queue e lements do you want to g ive ? ” ) ;s can f ( ”%i ” , &i l e ) ;for ( i = 1 ; i<= i l e ; i++){

p r i n t f ( ” type a value > ” ) ; s can f ( ”%i ” ,&a ) ;Add(&k , a ) ;Pr int ( k ) ; p r i n t f ( ”\n” ) ;

}p r i n t f ( ”Your queue : \n” ) ;Pr int ( k ) ;

p r i n t f ( ”\n Removing the e lements from the queue . . . : \n” ) ;for ( i =1; i<=i l e ; i++){

p r i n t f ( ”removed : %i \n” ,Remove(&k) ) ;Pr int ( k ) ;p r i n t f ( ”\n\n” ) ;

}

return 0 ;}

Listing 7.2: A dynamic queue of integers

An element of the queue is a structure (struct node) of two members: datacontaining a value to be stored (here int), and a pointer to the next element inthe queue. Notice that the pointer points to struct node as well. The wholequeue is represented by a structure of two members being pointers: to the firstelement of the queue (head), and to the last one (tail). The functions are:

• InitialiseQueue whose task is assign NULL to the head and the tail ofa queue, ensuring a proper representation of the fact that the queue isempty;

• Print printing the contents of the queue, which involves visiting all itsnodes using an additional pointer tmp, starting from the one pointed byhead;

• Add adding a new element at the end of the queue (which involves allo-cating memory for this element, and either making it the one pointed toby head and tail if the queue was empty, or joining it onto the queueafter its last element and making it the tail of the queue if the queue wasnonempty), and

• Remove which deletes the first node of the queue, returning to the programthe value which was stored in that node. Removing the element involves“catching” it by an additional pointer (here kill), changing the head ofthe queue to point to the element pointed to by next of the first one, andthen freeing the memory used by the element to be removed.

61

IGL - Programming CHAPTER 7. STRUCTURES

Exercises

1. Write a program defining a structure representing a vulgar fraction, andwrite a set of functions enabling managing these fractions. The functionsshould involve:

• CreateFraction returning a fraction created from two integers givenas the parameters of the function,

• Print printing the fraction being its parameter,

• Read reading a contents of the fraction being its parameter,

• Add returning a fraction being the sum of two fractions given as theparameters,

• Multiply returning a fraction being the product of two fractionsgiven as the parameters,

• IsGreater of two parameters being fractions, returning 1 if the firstfraction is greater than the second one, and 0 otherwise.

If it would be helpful to write some additional functions, wtite them aswell.

2. Write a program implementing a stack of integers

3. Write a program implementing a sorted list of integers.

A similar problem to implementing a queue is implementing a stack; we leavethe analysis of the program in Listing 7.3 to an interested reader (the programprovides a function adding an element to the stack only; removing an elementis analogous to removit it from a queue (besides modifying the tail which doesnot exist here).

#include<s t d i o . h>#include<s t d l i b . h>

struct node{int data ;struct node ∗next ;

} ;

struct s tack{

struct node∗ head ;} ;

typedef struct node node ;typedef struct s tack stack ;

void pr in tStack ( s tack s ){

node ∗pom;

62

IGL - Programming CHAPTER 7. STRUCTURES

pom = s . head ;while (pom!=NULL){

p r i n t f ( ”%i , ” , pom−>data ) ;pom = pom−>next ;

}}

void Add( stack ∗ s , int a ){

node ∗pom;pom = ( node ∗) mal loc ( s izeof ( node ) ) ;i f (pom == NULL){ p r i n t f ( ”not enough memory ! ! ! \ n” ) ; return ; }

pom−>data = a ;pom−>next = s−>head ;s−>head = pom;

}

int main (void ){

s tack s t ;s t . head = NULL;Add(&st , 3 ) ; Add(&st , 2 ) ; Add (&st , 0 ) ;p r in tStack ( s t ) ;return 0 ;

}

Listing 7.3: A dynamic stack of integers

63

Part II

Object-OrientedProgramming - C++

64

Chapter 8

Objects, data abstraction

The standard C structures (struct) consist only of members being variables(called fields).

struct TObj

{

int a, b;

};

In many modern object-oriented languages like C++ one can place functionsinside of structures. Those member functions (called methods) operate on aparticular structure. This kind of structure is called an abstract data type, andvariables created using this structure are called objects, or instances, of thattype.

Calling a member function for an object is called sending a message to thatobject. The primary action in object-oriented programming is sending messagesto objects. Whenever you are inside the member function of structure, you canrefer to any member (including another member function) by simply givingits name. The compiler will search through the local structure’s names beforelooking for a global version of that name, so a code is easier to write and it’s alot easier to read.

Packaging data and functions together (called encapsulation) is a significantbenefit for code organization and makes library use easier because it preventsname clashes by hiding the names - variable and function names inside a struc-ture do not clash with global variable names.

struct TObj //abstract data type

{

int a, b; //fields

void set(int _a, int _b) { a = _a; b = _b; } //method

};

TObj x, y; //objects

x.set(1, 2); //message

To make libraries easier to write and maintain, methods can be definedoutside the structure’s declaration, typically not in header but definition file.

65

IGL - Programming CHAPTER 8. OBJECTS, DATA ABSTRACTION

But then it s necessary to fully specify which one it is. To accomplish this fullspecification, C++ has an operator (::) called the scope resolution operator(named so because names can now be in different scopes: at global scope orwithin the scope of a structure.

//method outside the struct

void TObj::set(int _a, int _b) { a = _a; b = _b; }

Constructors and destructors

Many programming problems are caused by improper initialization of vari-ables. These kinds of bugs are hard to find, and similar issues apply to im-proper cleanup. Constructors and destructors guarantee proper initializationand cleanup (the compiler will not allow an object to be created and destroyedwithout the proper constructor and destructor calls), to get complete controland safety.

The constructor is a special method called automatically on every objectcreation. The name of the constructor is the same as the name of the type, sosuch a function can be called automatically on initialization.

The destructor is a special method called automatically by the compilerwhen the object goes out of scope. The name of the constructor is also thesame as the name of the type, but with the leading tilde (∼), so such a functioncan be called automatically on finalization, too.

The constructor is called by the point of explicit definition of the object,but the only evidence for a destructor call is the closing brace of the scope thatsurrounds the object. Both the constructor and destructor are very unusualtypes of functions: they have no return value (void keyword is also forbidden).

struct TObj

{

int a, b; //fields

TObj() { a = b = 0; } //constructor

~TObj() {} //destructor

};

{

...

TObj x; //constructor call

...

} //destructor call

Like any function, the constructor can have arguments to allow you to specifyhow an object is created, give it initialization values, and so on. The destructornever has any arguments because destruction never needs any options. Onecan define many constructors for a single structure (with different number ofparameters) but only one destructor.

A constructor that can be called with no arguments is called a default con-structor. Typically it has no parameters, as in the above example, but it can bedefined also as parametrized function with all parameters having default valuesgiven.

66

IGL - Programming CHAPTER 8. OBJECTS, DATA ABSTRACTION

struct TObj

{

int a, b; //fields

//default constructor which can be called

//as 1- or 2-parameter constructor

TObj(int _a = 0, int _b = 0)

{ a = _a; b = _b; }

};

//each object is created using default constructor

TObj x, y(1), z(1, 2), t[5];

The default constructor is so important that if (and only if) there are noconstructors for a structure, the compiler will automatically create one, butwithout any initialization of structure’s fields. If the memory allocated forthe fields should be initialized, one must do it himself by writing the defaultconstructor explicitly. If any constructors are defined, however, and theres nodefault constructor, the instances of object x and table t above will generatecompile-time errors.

The another important kind of constructor is so called copy-constructor. Ittakes a reference to an existing object of the same type as its argument, andit is used to create a new object from an existing one. The compiler automat-ically calls the copy-constructor when passing or returning an object by value.Although the compiler will automatically create a copy-constructor (if not de-fined explicitly), it should always be defined to ensure that the proper behaviouroccurs while copying structure’s fields. If a copy-constructor will be created ex-plicitly, the compiler will not perform a bit copy when creating a new objectfrom an existing one. It is very important for pointer fields for which memoryallocation is needed while copying.

#include <iostream>#include <c s t d l i b>

using namespace std ;

struct TObj{// f i e l d sint a , b ;

// con s t r u c t o r sTObj ( ){

cout << ”Parameter− l e s s con s t ruc to r \n” ;a = b = 0 ;

}TObj( int a , int b ){

a = a ;b = b ;cout << ”2−parameter con s t ruc to r o f ob j e c t ” << a << ” , ”

<< b << ”\n” ;

67

IGL - Programming CHAPTER 8. OBJECTS, DATA ABSTRACTION

}TObj( int x ){

a = b = x ;cout << ”1−parameter con s t ruc to r o f ob j e c t ” << x << ” , ” <<

x << ”\n” ;}TObj( const TObj &o ){

a = o . a ;b = o . b ;cout << ”Copying con s t ruc to r o f ob j e c t ” << o . a << ” , ” << o

. b << ”\n” ;}

// d e s t r u c t o r˜TObj ( ){

cout << ”Destructor o f ob j e c t ” << a << ” , ” << b << ”\n” ;a = b = 0 ;

}} ;

TObj & f (TObj p){

cout << ” f : s t a r t \n” ;TObj ∗n = new TObj(p) ;n−>a = 7 ;n−>b = 9 ;cout << ” f : stop \n” ;return ∗n ;

}

int main ( ){

cout << ”main s t a r t \n” ;{

TObj x ;//TObj y (1 ,3) , z (5) , u = y ;cout << ” Ca l l i ng f ( x ) \n” ;f ( x ) ;cout << ”OK, f done\n” ;

}cout << ”main stop \n” ;system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 8.1: A program with objects

When executed, it can be easily seen then the number of destructor callsis less then constructor calls. The reason is in call of function f. It allocatesa new object by new operator, then it returns a reference to the object, butthe reference is not overtaken in the main function, so we have a memory leak.

68

IGL - Programming CHAPTER 8. OBJECTS, DATA ABSTRACTION

To repair the code the returned value should be overtaken by pointer and thenfreed by delete operator.

TObj *p = &f(x);

delete p;

Exercises

1. Define an employer class with fields (a first name and a name and a posi-tion as dynamically allocated strings, a salary as floating value), default,parametrised and copy constructors, destructor and methods (printing allthe employer’s data, changing the position, getting and increasing thesalary). Test all class capabilities.

2. Implement a stack of pointers to strings as a linked list with two construc-tors (default and copy-constructor), destructor, and at least to methodspush and pop.

69

Chapter 9

Classes, data protection

Access control in C++ gives valuable control to the creator of a structure. Theusers of the structure can clearly see exactly what they can use and what toignore. More important, though, is the ability to ensure that no client pro-grammer becomes dependent on any part of the underlying implementation ofa structure. The creator of the structure can change the underlying implemen-tation with the knowledge that no client programmer will be affected by thechanges because they can’t access that part of the structure.

In many cases it is very important that the client programmer could notdirectly manipulate some of the members of a structure, in C there’s no way toprevent it. Everything’s naked to the world, so by writing an useful library itis easy ”to give a monkey a razor” instead of ”to heal the world”.

The most important is to keep the client programmer’s hands off tools theyshouldn’t touch, tools that are necessary for the internal machinations of thedata type, but not part of the interface the client programmer needs to solvetheir particular problems. This assumption is sometimes called by black boxsolution. It is very critical when pointers are members of a structure and internalmemory used is dynamically allocated.

Access specifiers

C++ introduces three new keywords to set the boundaries in a structure:public, private, and protected. These access specifiers are used only in astructure declaration, and they change the boundary for all the declarationsthat follow them.

struct TList

{

private: //hidden members internal implementation

int value;

TList *next;

static TList *head;

void printList();

public: //visible members shared interface

Tlist(int _value = 0);

~Tlist();

70

IGL - Programming CHAPTER 9. CLASSES, DATA PROTECTION

int peek();

static void print();

static void clear();

};

public means all member declarations that follow are available to everyone.The private keyword, on the other hand, means that no one can access thatmember except the creator of the type, inside function members of that type.private is a brick wall between the hidden structure and the client programmer;if someone tries to access a private member, they’ll get a compile-time error.The last access specifier is protected. protected acts just like private, butused only when ”inherited” structures (which cannot access private members)are granted access to protected members. This will become clearer in nextchapters.

Sometimes it is necessary to explicitly grant access for a function that isn’ta member of the current structure. This can be accomplished by declaring thatfunction a friend inside the structure declaration. This will also become clearerin the next chapter.

Instead of struct keyword it is more typical to use class keyword in C++.It’s identical to the struct keyword in absolutely every way except one: classdefaults to private, whereas struct defaults to public. So the above declara-tion can be replaced by the following one.

class TList

{

//private: //hidden members by default

int value;

TList *next;

static TList *head; //static field

void printList();

public: //visible members

Tlist(int _value = 0);

~Tlist();

int peek();

static void print(); //static method

static void clear();

};

Static members

In the examples one can see other new code elements static members of a classdeclared with the leading keyword static. There are times when it is a needof a single storage space to be used by all objects of a class. In C, one woulduse a global variable, but this is not very safe. Global data can be modified byanyone, and its name can clash with other identical names in a large project.It would be ideal if the data could be stored as if it were global, but be hiddeninside a class, and clearly associated with that class.

This is accomplished with static data members inside a class. There isa single piece of storage for a static data member, regardless of how manyobjects of that class you create. All objects share the same static storage

71

IGL - Programming CHAPTER 9. CLASSES, DATA PROTECTION

space for that data member, so it is a way for them to ”communicate” witheach other. But the static data belongs to the class; its name is scoped insidethe class and it can be public, private, or protected.

The declaration of a static field must appear inside a class, but initializationmust be outside, using the scope-resolution operator - it is common to put it inthe implementation file for the class, even though the field is private.

TList *TList::head = NULL;

It can be also possible to create static member functions that, like static

data members, work for the class as a whole rather than for a particular objectof a class. A static member function can be called in the ordinary way, withthe dot or the arrow, in association with an object. However, it’s more typicalto call a static member function by itself, without any specific object, usingthe scope-resolution operator for the class.

TList::print();

A static member function cannot access ordinary data members, onlystatic data members. It can call only other static member functions.

The code beneath implements a automatically linked list of objects, eachone containing here a single integer field. Each object is linked to the list whenits constructor fires.

#include <iostream>#include <c s t d l i b>

using namespace std ;

class TList{// p r i v a t e :int value ;TList ∗next ;stat ic TList ∗head ;void p r i n tL i s t ( ) ;

public :

TList ( int va lue = 0){

value = va lue ;next = head ;head = this ;cout << ”Constructor : ” ;p r i n tL i s t ( ) ;

}

˜TList ( ){

cout << ”Destructor : ” ;p r i n tL i s t ( ) ;

}

72

IGL - Programming CHAPTER 9. CLASSES, DATA PROTECTION

int peek ( ) ;stat ic void pr in t ( ) ;stat ic void c l e a r ( ) ;} ;

void TList : : p r i n tL i s t ( ){

cout << ”START” ;for ( TList ∗p = head ; p ; p = p−>next )

cout << ”−>” << p−>value ;cout << endl ;

}

int TList : : peek ( ){

return value ;}

void TList : : p r i n t ( ){

head−>p r i n tL i s t ( ) ;}

void TList : : c l e a r ( ){

for ( TList ∗p ; head ; delete p){

p = head ;head = p−>next ;

}}

TList ∗TList : : head = NULL;

int main ( ){

cout << ”Creat ing a l i s t o f ob j e c t s :\n” ;for ( int i = 0 ; i < 5 ; i++)

new TList ( i ) ;cout << ” Pr in t ing the l i s t :\n” ;TList : : p r i n t ( ) ;cout << ”Clear ing the l i s t :\n” ;TList : : c l e a r ( ) ;system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 9.1: Automatically linked list

In the code it is necessary to take the address of the entire object inside amember function of the class, in the constructor here. There’s a special keyword,called this, which produces the address of the struct/class.

73

IGL - Programming CHAPTER 9. CLASSES, DATA PROTECTION

Exercises

1. Implement a student class with two private fields (an index and an averagegrade), convenient methods for getting and setting the values of the fields,and a static field and function for counting the number of students createdby a programmer.

2. Define a banking account class with private fields (owner’s first name andname and an account number as strings, a balance as floating value), de-fault, parametrised and copy constructors, destructor and methods (print-ing all the account’s data, getting, increasing and decreasing the accountbalance). Test all class capabilities.a, getting, increasing and decreasingthe account balance). Test all class capabilities.

74

Chapter 10

Operator overloading

In C++, it’s possible to redefine operators that work with classes (operatoroverloading). This definition is just like an ordinary function definition exceptthat the name of the function consists of the keyword operator followed by theoperator. Thats the only difference, and it becomes a function like any otherfunction, which the compiler calls when it sees the appropriate pattern.

But remember it’s only syntactic sugar, another way of calling a function.Looking at it this way, it is no reason to overload an operator except if it willmake the code involving a class easier to write and especially easier to read.

The number of arguments in the overloaded operator’s argument list dependson two factors:

1. Whether it’s a unary operator (one argument) or a binary operator (twoarguments).

2. Whether the operator is defined as a global friend function (one argumentfor unary, two for binary) or a member function (zero arguments for unary,one for binary the object becomes the left-hand argument).

class TVect

{

...

//unary operator as a constant method

TVect operator-() const;

//binary operator as a friend function

friend TVect operator+(const TVect&, const TVect&);

};

Typically, unary operators should be member functions which guaranties nochanging an object called for. It is guaranteed by the keyword const after themethod declaration.

Typical binary operators, like arithmetic or logical ones, should be definedas a global functions with full access rights to object being arguments friend

functions. Ordinary such operators will not change their arguments, so passingthe arguments by const reference (&) is suggested.

75

IGL - Programming CHAPTER 10. OPERATOR OVERLOADING

Conversions

Thanks to global definition and in consequence two explicitly defined arguments,there is possibility to use such operator commutatively and to pass objects ofother types as the arguments by the way of automatic conversion. If it isdefined a constructor that takes as its single argument an object (or reference)of another type, that constructor allows the compiler to perform an automatictype conversion.

TVect(int d = 0); //constructor converting int to TVect

The above constructor (called conversion constructor) can be executed bypassing a single integer value, thus it enables an automatic conversion frominteger values to the class objects constructed by it. As the consequence anyoverloaded as a friend function binary operator can pass integer value as itsfirst or second argument.

obj1 = obj2 + 1;

obj1 = 1 + obj2;

Special operators

Several additional operators have a special requirements for overloading. Theunary indexing operator[ ] must be a member function and it requires a singleargument. Because operator[ ] implies that the object it’s being called foracts like an array, it should return a reference, so it can be conveniently usedon the left-hand side of an assignment.

double& operator[](int i) const;

Sometimes there is need the left-hand operand to be an object of some otherclass. A common place for this is when the operators << and >> are overloadedfor input/output streams.

//should be friends

friend ostream& operator<<(ostream&, const TVect&);

friend istream& operator>>(istream&, TVect&);

It’s important that the overloaded shift operators pass and return by ref-erence, so the actions will affect the external objects. Once all the actions areperformed on the istream or ostream, it is returned so it can be used in a morecomplicated expression like cascade operator call.

cout << obj1 << << obj2 << endl;

Another very important operator is assignment (=). Because assigning anobject to another object of the same type is an activity most people expect tobe possible, the compiler will automatically create a type::operator=(type)

if not made explicitly. The behaviour of this operator mimics that of the auto-matically created copy-constructor; if the class contains objects the operator=

for those objects is called recursively. With classes of any sophistication (espe-cially if they contain pointers) it is suggested to explicitly create an operator=

or declare operator= as a private function to forbid the assignment operation.

76

IGL - Programming CHAPTER 10. OPERATOR OVERLOADING

TVect& operator=(const TVect&); //assignment

The operator must be a member function and, like a shift operator, shouldreturn a reference to assigned object using the dereference of this pointer. Italso must copy all of the necessary information from the right-hand object intothe current object (that is, the object that operator= is being called for) toperform whatever considered as assignment for the class (e.g reallocating thememory for internal pointers).

#include <iostream>#include <c s t d l i b>

using namespace std ;

class TVect{

int dim ;double ∗v ;

public :TVect ( ) : dim (0) , v (0 ) {}TVect ( int d , double ∗p = NULL){v = new double [ dim = d ] ;i f (p) for ( int i = 0 ; i < dim ; ++i ) v [ i ] = p [ i ] ;

}TVect ( const TVect & r ) //copy−cons t ruc t o r{v = new double [ dim = r . dim ] ;

for ( int i = 0 ; i < dim ; ++i ) v [ i ] = r . v [ i ] ;}˜TVect ( ){

i f ( v ) delete [ ] v ;v = 0 ;dim = 0 ;

}TVect& operator=(const TVect& r ) // assignment{i f ( this != &r )

{this−>TVect : : ˜ TVect ( ) ; // e x p l i c i t d e s t r u c t o r c a l l

v = new double [ dim = r . dim ] ;for ( int i = 0 ; i < dim ; i++) v [ i ] = r . v [ i ] ;

}return ∗ this ;

}double& operator [ ] ( int i ) const { return v [ i − 1 ] ; } //

index ing opera tor must be a methodTVect operator−() const ; //unary opera tor as a cons tant

methodfriend TVect operator+(const TVect&, const TVect&) ; // b inary

opera tor as f r i e nd func t i onfriend TVect operator−(const TVect&, const TVect&) ;friend double operator ∗( const TVect&, const TVect&) ;

77

IGL - Programming CHAPTER 10. OPERATOR OVERLOADING

friend TVect operator ∗(double , const TVect&) ;friend ostream& operator<<(ostream&, const TVect&) ; // stream

opera tor shou ld be a f r i e nd func t i onfriend i s t ream& operator>>(i s t ream&, TVect&) ;int getDim ( ) const { return dim ; }

} ;

//methods

TVect TVect : : operator−() const{

TVect tmp(dim) ;for ( int i = 1 ; i <= dim ; i++) tmp [ i ] = −v [ i ] ;return tmp ;

}

// f r i e nd f unc t i on sTVect operator+(const TVect &a , const TVect &b){

i f ( a . dim != b . dim) e x i t (EXIT FAILURE) ;TVect c ( a . dim) ;for ( int i = 1 ; i <= a . dim ; i++) c [ i ] = a [ i ] + b [ i ] ;return c ;

}

TVect operator−(const TVect &a , const TVect &b){

i f ( a . dim != b . dim) e x i t (EXIT FAILURE) ;TVect c ( a . dim) ;for ( int i = 1 ; i <= a . dim ; i++) c [ i ] = a [ i ] − b [ i ] ;return c ;

}

double operator ∗( const TVect &v1 , const TVect &v2 ){

double sum = 0 . 0 ;i f ( ! ( v1 . dim == v2 . dim) ) e x i t (EXIT FAILURE) ;for ( int i = 1 ; i <= v1 . dim ; i++) sum += v1 [ i ] ∗ v2 [ i ] ;return sum ;

}

TVect operator ∗(double m, const TVect &a ){

TVect tmp( a . dim) ;tmp = a ;for ( int i = 1 ; i <= a . dim ; i++) tmp [ i ] ∗= m;return tmp ;

}

ostream& operator<<(ostream &os , const TVect &a ){

os << ’ [ ’ ;for ( int i = 1 ; i <= a . dim ; i++) os << a [ i ] << ’ ’ ;os << ’ ] ’ ;

78

IGL - Programming CHAPTER 10. OPERATOR OVERLOADING

return os ;}

i s t ream& operator>>(i s t ream &is , TVect &a ){

for ( int i = 1 ; i <= a . dim ; i++) i s >> a [ i ] ;return i s ;

}

//main func t i onint main (void ){

double tab0 [ ] = {−1.0 , 2 . 3 , 3 . 0} , tab1 [ ] = {3 .73 , −5.0 ,9 . 8 } ;

TVect a (3 , tab0 ) , b (3 , tab1 ) ;double c = 2 . 0 ;cout << endl << endl ;cout << ”Sum : ” << a << ’+ ’ << b << ’= ’ << a + b << endl ;cout << ”Opposite vec to r : ” << −a << endl ;cout << ” Sca l a r mu l t ip ly ing : ” << a ∗ b << endl ;cout << a << endl << a [ 1 ] << endl ;

cout << ”Mult ip ly ing by constant value : ” << c ∗ a <<endl ;

system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 10.1: Vectors

Exercises

1. Implement a class of clocks with hours, minutes and seconds. Overloadoperators: =, +, -, * (by scalar integer value), ==, <<, >>.

2. Implement a class of 2-dimensional matrices of integers with overloadedoperators: =, +, -, * (two versions by scalar value and by matrix), ==,<<, >>.

79

Chapter 11

Inheritance andcomposition

One of the most compelling features about modern object languages like C++is code reuse. Both inheritance and composition allow you to create a new typefrom existing types, and both embed subobjects of the existing types inside thenew type.

The inheritance forces the new type to be the same type as the base class(type equivalence guarantees interface equivalence). A new class is created asa type of an existing class. One can take the form of the existing class andadd code to it, without modifying the existing class. This magical act is calledinheritance, and most of the work is done by the compiler. Inheritance is oneof the cornerstones of object-oriented programming.

The inheritance is done in code by giving the name of the class as usual,but before the opening brace of the class body, putting a colon and the name ofthe base class (or base classes, separated by commas, for multiple inheritance).Doing this, the new derived class automatically gets all the data members andmember functions in the base class.

class TVehicle

{

protected: //accessed by inheriting classes

int num;

int wheels;

public:

TVehicle(int n = 0, int w = 4): num(n), wheels(w) //initializer list

{}

void print()

{

cout << "\t number : " << num << endl;

}

};

class TCar: public TVehicle //public inheritance

{

protected:

80

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

string brand; //composition nested object

float capacity;

public:

TCar(int n = 0, float c = 1.5, const string &b = "Toyota"):

TVehicle(n, 4), brand(b), capacity(c) //initializer list

{}

void print() //overridden method

{

TVehicle::print(); //call of inherited method

cout << "\t brand : " << brand << endl;

cout << "\t capacity : " << capacity << endl;

}

};

In the example the base class is inherited by public. During inheritance,everything defaults to private (if no keyword given after the colon). If thebase class were not preceded by public, it would mean that all of the public

members of the base class would be private in the derived class. Typical choiceis to keep all the public members of the base class public in the derived class,just by using the public keyword during inheritance.

The purpose of private inheritance is rather rare, because the alternativeof using composition to create a private object in the new class seems moreappropriate. Typically, the composition reuses existing types as part of theunderlying implementation of the new type. It is possible by simply nestingobjects of your existing class inside the new class. This is called compositionbecause the new class is composed of objects of existing classes.

Composition is generally used to include the features of an existing classinside a new class, but not its interface. That is, an object id embedded toimplement features of a new class, but the user of the class sees the interfacenewly defined rather than the interface from the original class. The is-a rela-tionship is expressed with inheritance, and the has-a relationship is expressedwith composition.

The protected keyword inside the class definition for some fields means thatthey are private as far as the class user is concerned, but available to anyonewho inherits from this class.

Constructor initializer list

The most important issue is to properly construct inherited or nested subob-jects. C++ provides a special syntax for this, the constructor initializer list.The form of the constructor initializer list echoes the act of inheritance. Withinheritance, there is the base classes after a colon and before the opening braceof the class body. In the constructor initializer list, there are the calls to sub-object class constructors after the constructor argument list and a colon, butbefore the opening brace of the function body. For composition, there shouldbe the names of the objects instead of the class names.

The idea is that the constructors of inherited subobjects and nested objectsare all called before you get into the body of the new class’s constructor. There’sno way to get to the opening brace of the constructor without some construc-tor being called for all the member objects and base-class objects, even if the

81

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

compiler must make a hidden call to a default constructor. This is a furtherenforcement of the C++ guarantee that no object (or part of an object) can getout of the starting gate without its constructor being called.

TCar(int n = 0, float c = 1.5, const string &b = "Toyota"):

TVehicle(n, 4), //base class constructor must be the first

brand(b), //nested object copy-constructor

capacity(c) //member initialization

{ ... }

Although it is required to make explicit constructor calls in the initializerlist, there is no need to make explicit destructor calls because there’s only onedestructor for any class, and it doesn’t take any arguments. However, thecompiler still ensures that all destructors are called, and that means all of thedestructors in the entire hierarchy of inheritance, starting with the most-deriveddestructor and working back to the root (the main base class).

If one want to call the base-class version of a normal member function that isoverridden, one must do it explicitly (as the printing function in the example).

#include <iostream>#include <c s t d l i b>#include <s t r i ng>

using namespace std ;

class TVehicle{protected :int num;int wheels ;

public :TVehicle ( int n = 0 , int w = 4) : num(n) , wheels (w) {}void pr in t ( ){

cout << ”\tnumber : ” << num << endl ;}} ;

class TCar : public TVehicle{protected :s t r i n g brand ;f loat capac i ty ;

public :TCar( int n = 0 , f loat c = 1 . 5 , const s t r i n g &b = ”Toyota” ) :

TVehicle (n , 4) , brand (b) , capac i ty ( c ) {}void pr in t ( ){

TVehicle : : p r i n t ( ) ;cout << ”\ tbrand : ” << brand << endl ;cout << ”\ t capac i t y : ” << capac i ty << endl ;

}} ;

82

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

class TPersonCar : public TCar{protected :int persons ;

public :TPersonCar ( int n = 0 , f loat c = 1 . 5 , const s t r i n g &b = ”

Toyota” , int p = 5) : TCar(n , c , b ) , persons (p) {}void pr in t ( ){

cout << ”Person car :\n” ;TCar : : p r i n t ( ) ;cout << ”\ tpe r sons : ” << persons << endl ;

}} ;

class TTruck : public TCar{protected :f loat load ;

public :TTruck ( int n = 0 , f loat c = 1 . 5 , const s t r i n g &b= ”Toyota” ,

int l = 10) : TCar(n , c , b ) , load ( l ) {}void pr in t ( ){

cout << ”Truck :\n” ;TCar : : p r i n t ( ) ;cout << ”\ t l oad : ” << load << endl ;

}} ;

class TBicycle : public TVehicle{protected :f loat rad iu s ;

public :TBicycle ( int n = 0 , f loat r = 20) : TVehicle (n , 2) , r ad iu s ( r )

{}void pr in t ( ){

cout << ” B i cyc l e :\n” ;TVehicle : : p r i n t ( ) ;cout << ”\tWheel rad iu s : ” << rad iu s << endl ;

}} ;

class TPlane : public TVehicle{protected :int range ;f loat wingspan ;

public :TPlane ( int n = 0 , int r = 10000 , f loat w = 10) : TVehicle (n ,

4) , range ( r ) , wingspan (w) {}void pr in t ( )

83

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

{TVehicle : : p r i n t ( ) ;cout << ”\ trange : ” << range << endl ;cout << ”\ twingspan : ” << wingspan << endl ;

}} ;

class TAir l ine r : public TPlane{protected :int persons ;

public :TAi r l i ne r ( int n = 0 , int r = 10000 , f loat w = 10 , int p =

300) : TPlane (n , r , w) , persons (p) {}void pr in t ( ){

cout << ” A i r l i n e r :\n” ;TPlane : : p r i n t ( ) ;cout << ”\ tpe r sons : ” << persons << endl ;

}} ;

class TFighter : public TPlane{protected :int f o r c e ;

public :TFighter ( int n = 0 , int r = 10000 , f loat w = 10 , int f =

1000) : TPlane (n , r , w) , f o r c e ( f ) {}void pr in t ( ){

cout << ” Fighter :\n” ;TPlane : : p r i n t ( ) ;cout << ”\ t f o r c e o f f i r e : ” << f o r c e << endl ;

}} ;

int main ( ){

TFighter f i g h t e r ;f i g h t e r . p r i n t ( ) ;system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 11.1: Vehicles

static member functions act the same as non-static member functions - theyinherit into the derived class. Constructors and destructors don’t inherit andmust be created specially for each derived class. In addition, the operator=

doesn’t inherit because it performs a constructor-like activity. According tothe inheritance, these functions are synthesized by the compiler if not createdexplicitly. As introduced earlier, one can’t create any constructors in order forthe compiler to synthesize the default constructor and the copy-constructor.

84

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

The compiler synthesizes the copy-constructor (since that is one of the fourfunctions it will synthesize, along with the default constructor if you don’tcreate any constructors the operator= and the destructor) by calling the baseclass copy-constructor and copy-constructors for all member objects. Whenexplicitly written, the copy-constructor must properly call the base-class copy-constructor (as the compiler does).

TCar(const TCar& c):

TVehicle(c),

brand(c.brand),

capacity(c.capacity)

{}

Upcasting

TCar is inherited from TVehicle, so a TCar reference is a TVehicle reference.The base class copy-constructor call upcasts a reference to TCar to a referenceto TVehicle and uses it to perform the copy-construction. The act of convertinga reference or pointer to a derived class into a base class reference or pointer iscalled upcasting.

void repair(TVehicle &v)

{

...

v.print();

}

TCar myCar;

repair(myCar); //upcasting

Of course, any upcast loses type information about an object. The compilercan deal with argument v only as a TVehicle pointer and nothing else. Thatis, it cannot know that it actually happens to point to a TCar object, so insidethe repair function only the base-class version of printing method will be called.The solution for the problem will be explained in the next chapter.

Virtual inheritance

Typically, the inheritance from one class is used, so it would seem to makesense to inherit from more than one class at a time. It can be simply doneby adding other classes in the base-class list during inheritance, separated bycommas. However, multiple inheritance introduces a number of possibilities forambiguity (duplicated subobjects). To avoid this problem some base classes(which in fact inherit directly or indirectly from one common base class) shouldbe inherited virtually (with leading keyword virtual in the base-class list).

// To choose the convenient t e s t// use the d i r e c t i v e #de f i n e wi th the t e s t name TEST1−TEST5// Remark : choose on ly one t e s t !#define TEST5

85

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

#include <iostream>#include <c s t d l i b>

using namespace std ;

class A {public : int i [ 1 0 0 ] , j ;A( ) { cout << ”−A−” ; }} ;

class B {public : int i , j ;B( ) { cout << ”−B−” ; }} ;

#define s i z ( x ) endl << #x << ” : ” << s izeof ( x ) << endl

//−−− TEST nr 1 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−#i f de f ined (TEST1)class C : public B, public A {public : int j ;C( ) { cout << ”−C−” ; }} ;

class D : public A {public : int j ;D( ) { cout << ”−D−” ; }} ;

class E : public D, public C {public : int j ;E( ) { cout << ”−E−” ; }} ;

int func (void ) { cout << endl ; E e ; cout << s i z (E) ; return 0 ;}

int r = func ( ) ;

#endif

//−−− TEST nr 2 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−#i f de f ined (TEST2)class C : public B, public A {public : int j ;C( ) { cout << ”−C−” ; }} ;

class D : virtual public A {public : int j ;D( ) { cout << ”−D−” ; }} ;

class E : public D, public C {

86

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

public : int j ;E( ) { cout << ”−E−” ; }} ;

int func (void ) { cout << endl ; E e ; cout << s i z (E) ; return 0 ;}int r = func ( ) ;

#endif

//−−− TEST nr 3 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−#i f de f ined (TEST3)class C : public B, virtual public A {public : int j ;C( ) { cout << ”−C−” ; }} ;

class D : virtual public A {public : int j ;D( ) { cout << ”−D−” ; }} ;

class E : public D, public C {public : int j ;E( ) { cout << ”−E−” ; }} ;

int func (void ) { cout << endl ; E e ; cout << s i z (E) ; return 0 ;}int r = func ( ) ;

#endif

//−−− TEST nr 4 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−#i f de f ined (TEST4)

class D : public A {public : int j ;D( ) { cout << ”−D−” ; }} ;

class F : virtual public A, public D {public : int j ;F( ) { cout << ”−F−” ; }} ;

int func (void ) { cout << endl ; F f ; cout << s i z (F) ; return 0 ;}int r = func ( ) ;

#endif

//−−− TEST nr 5 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−#i f de f ined (TEST5)

87

IGL - Programming CHAPTER 11. INHERITANCE AND COMPOSITION

class D : virtual public A {public : int j ;D( ) { cout << ”−D−” ; }} ;

class F : virtual public A, public D {public : int j ;F( ) { cout << ”−F−” ; }} ;

int func (void ) { cout << endl ; F f ; cout << s i z (F) ; return 0 ;}int r = func ( ) ;

#endif

int main ( ){system ( ”PAUSE” ) ;return EXIT SUCCESS ;}

Listing 11.2: Virtual inheritance

Exercises

1. Make all constructors ”talking” and add ”talking” destructors to all classesdefined for vehicles to see the order of constructor calls and automaticdestructor calls.

2. Implement a class for 3-dimensional box (width, length, height) and derivetwo other classes from it: coloured box and weighted box. Test the classesallowing a user to make any type of boxes.

88

Chapter 12

Polymorphism

Polymorphism is the third essential feature of an object programming language,after data abstraction and inheritance it makes language object-oriented. Poly-morphism means ”different forms”. In object-oriented programming, you havethe same face (the common interface in the base class) and different forms usingthat face (derived classes) with the different versions of the same function.

In the previous chapter, there was introduced the problem of proper methodcall when upcasting. By default a reference or a pointer to a base class can callonly base class methods. The problem in the program (vehicles) was causedby early binding (choosing a proper body for function call in compile-time),because the compiler cannot know the correct function to call when it has onlya TVehicle address.

The solution is called late binding, which means the binding occurs at run-time, based on the type of the object. To cause late binding to occur for a par-ticular function, C++ requires the use of the virtual keyword when declaringthe function in the base class. Late binding occurs only with virtual functions,and only when using an address (pointer or reference) of the base class wherethose virtual functions exist.

class TVehicle

{

protected:

int num;

int wheels;

public:

TVehicle(int n = 0, int w = 4): num(n), wheels(w)

{}

virtual void print() //virtual function

{

cout << "\t number : " << num << endl;

}

};

class TCar: public TVehicle

{

protected:

89

IGL - Programming CHAPTER 12. POLYMORPHISM

string brand;

float capacity;

public:

TCar(int n = 0, float c = 1.5, const string &b = "Toyota"):

TVehicle(n, 4), brand(b), capacity(c)

{}

void print() //overridden virtual function

{

TVehicle::print(); //call of inherited version of the function

cout << "\t brand : " << brand << endl;

cout << "\t capacity : " << capacity << endl;

}

};

void repair(TVehicle &v)

{

...

v.print(); //polymorphic call

}

TCar myCar;

repair(myCar); //upcasting

To create a member function as virtual, one should simply precede the dec-laration of the function with the keyword virtual. Only the declaration needsthe virtual keyword, not the definition. If a function is declared as virtual

in the base class, it is virtual in all the derived classes. The redefinition of avirtual function in a derived class is usually called overriding. Now, inside therepair function the derived class version of printing method will be called.

Polymorphism can be begun at an arbitrary level of inheritance.

// Compilat ion va r i an t s :

// No v i r t u a l f unc t i on s// #de f i n e VIRTA// #de f i n e VIRTB// #de f i n e VIRTC

// V i r t u a l i t y b eg in s in c l a s s C// #de f i n e VIRTA// #de f i n e VIRTB// #de f i n e VIRTC v i r t u a l

// V i r t u a l i t y b eg in s in c l a s s B// #de f i n e VIRTA// #de f i n e VIRTB v i r t u a l// #de f i n e VIRTC

// V i r t u a l i t y b eg in s in c l a s s A// #de f i n e VIRTA v i r t u a l// #de f i n e VIRTB// #de f i n e VIRTC

90

IGL - Programming CHAPTER 12. POLYMORPHISM

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

#define VIRTA#define VIRTB#define VIRTC

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

#include <iostream>#include <c s t d l i b>#include <s t r i ng>

using namespace std ;

class A{

int i ;public :A( ) : i ( 0 ) { }A( int n) : i (n ) { }VIRTA s t r i n g className ( ) const { return ”A” ; }VIRTA int va l ( ) const { return i ; }

} ;

class B : public A{

int j ;public :B( ) : j ( 0 ) { }B( int n) : A(0) , j (n ) { }VIRTB s t r i n g className ( ) const { return ”B” ; }VIRTB int va l ( ) const { return j ; }

} ;

class C : public B{

int k ;public :C( ) : k (0 ) { }C( int n) : B(0 ) , k (n) { }VIRTC s t r i n g className ( ) const { return ”C” ; }VIRTC int va l ( ) const { return k ; }

} ;

ostream& operator<< ( ostream &out , const A &o ){return out << o . className ( ) << o . va l ( ) ;}

int main ( ){A a (1) ; B b (2 ) ; C c (3 ) ;cout << endl << a . className ( ) << a . va l ( )

<< ” − ” << b . className ( ) << b . va l ( )

91

IGL - Programming CHAPTER 12. POLYMORPHISM

<< ” − ” << c . className ( ) << c . va l ( ) << endl ;

cout << a << ” − ” << b << ” − ” << c << endl ;A ∗p ;p = &a ; cout << p−>className ( ) << p−>va l ( ) << ” − ” ;p = &b ; cout << p−>className ( ) << p−>va l ( ) << ” − ” ;p = &c ; cout << p−>className ( ) << p−>va l ( ) << endl ;B ∗ t ;

cout << ’ ’ << ’ ’ << ” − ” ;t = &b ; cout << t−>className ( ) << t−>va l ( ) << ” − ” ;t = &c ; cout << t−>className ( ) << t−>va l ( ) ;

system ( ”PAUSE” ) ;return EXIT SUCCESS ;}

Listing 12.1: Virtual functions

Abstract classes

Often in a design, the base class is defined to present only an interface for itsderived classes. That is, no to create an object of the base class, but only toupcast to it so that its interface can be used. This is accomplished by makingthat class abstract, which happens when it has at least one pure virtual func-tion. A pure virtual function uses the virtual keyword and is followed by =

0. If anyone tries to make an object of an abstract class, the compiler preventsthem. When an abstract class is inherited, all pure virtual functions must beimplemented, or the inherited class becomes abstract as well.

//Autocontainer LIFO#include <iostream>#include <c s t d l i b>#include <s t r i ng>

using namespace std ;

class TContainer{virtual void pr in t ( ) = 0 ; // pure v i r t u a l f unc t i onstat ic TContainer ∗haed ;stat ic int count ;TContainer ∗nast ;void ∗data ;int n ;

public :TContainer (void ∗d) : nast ( haed ) , data (d) , n ( count++){

haed = this ;}

stat ic void p r i n tA l l ( ){

92

IGL - Programming CHAPTER 12. POLYMORPHISM

for ( TContainer ∗p = haed ; p ; p = p−>nast ){

p−>pr in t ( ) ; // v i r t u a l f unc t i on c a l l}

}} ;

class TInt{int id ;

public :TInt ( int n = 0) : id (n) {}

void show ( ){

cout << ’ [ ’ << id << ’ ] ’ << endl ;}} ;

class TString{s t r i n g ∗ s t r ;

public :TString ( const s t r i n g &s ){

s t r = new s t r i n g ( s ) ;}

˜TString ( ){

delete s t r ;}

void show ( ){

cout << ∗ s t r << endl ;}} ;

class TIK : public TInt , public TContainer{public :TIK( int n) : TInt (n) , TContainer ( this ) {}

void pr in t ( ){

show ( ) ;}} ;

class TSK: public TString , public TContainer{

93

IGL - Programming CHAPTER 12. POLYMORPHISM

public :TSK( const s t r i n g &s ) : TString ( s ) , TContainer ( this ) {}void pr in t ( ){

show ( ) ;}} ;

TContainer ∗TContainer : : haed = NULL;int TContainer : : count = 0 ;

int main ( ){TIK a (1) ;TSK b( ” txt1 ” ) ;TIK c (7 ) ;TSK d( ” txt2 ” ) ;

TContainer : : p r i n tA l l ( ) ;

system ( ”PAUSE” ) ;return EXIT SUCCESS ;}

Listing 12.2: Abstract autocontainer

Virtual destructors

It is forbidden to use thevirtual keyword with constructors, but destructorscan and often must be virtual. Normally, the action of the destructor is quiteadequate. But the problem occurs when it is necessary to delete a derivedobject (created early with new) pointed by a pointer to base class type. Thecompiler can only know to call the base-class version of the destructor duringdelete. This is the same problem that virtual functions were created to solvefor the general case. Fortunately, virtual functions work for destructors as theydo for all other functions except constructors.

class TVehicle

{

protected:

int num;

int wheels;

public:

TVehicle(int n = 0, int w = 4): num(n), wheels(w)

{}

virtual void print() //virtual function

{

cout << "\t number : " << num << endl;

}

};

94

IGL - Programming CHAPTER 12. POLYMORPHISM

class TCar: public TVehicle

{

protected:

string* brand; //field changed to pointer

float capacity;

public:

TCar(int n = 0, float c = 1.5, const string &b = "Toyota"):

TVehicle(n, 4), capacity(c)

{

brand = new string(b); //memory allocation

}

~TCar()

{

delete brand; //memory freeing

}

void print() //overridden virtual function

{

TVehicle::print(); //call of inherited version of the function

cout << "\t brand : " << brand << endl;

cout << "\t capacity : " << capacity << endl;

}

};

TVehicle *myCar = new TCar; //constructing and upcasting

delete myCar; //destructor call

Class TVehicle has automatically generated destructor which is called forthe pointer myCar pointing in fact to the object of TCar class memory for fieldbrand won’t be freed. To solve the problem TVehicle should contain the virtualdestructor.

virtual ~TVehicle() {}

As a guideline, any time a class has a virtual function, it should be immedi-ately added a virtual destructor (even if it does nothing). This is way to ensureagainst any surprises later.

Exercises

1. Implement an abstract class for mammals containing a name, weight, ageand add two derived classes (cat and whale) with overridden virtual func-tion of making voice. Make an array of pointers to mammals, insert to itsome animals of any type and let them sound their voices.

2. Implement a geometry library with the abstract class figure and some de-rived classes (circle, point, polygon, etc.) with overridden virtual drawingfunction. Make a class of canvas with figures having a method to redrawall its elements.

95

Chapter 13

Exception handling

One of the major features in C++ is exception handling. Error-handling codeis not hard to write, and it doesn’t become mixed up with your ”normal” code -in a separate section one can write the code to cope with the problems. Errorscannot be ignored. If a function needs to send an error message to the caller ofthat function, it ”throws” an object representing that error out of the function.If the caller doesnt ”catch” the error and handle it, it goes to the next enclos-ing dynamic scope, and so on until the error is either caught or the programterminates because there was no handler to catch that type of exception.

Exception handlers immediately follow the try block and are denoted bythe keyword catch.

try

{

// code that may generate exceptions

}

catch(type1 id1)

{

// handle exceptions of type1

}

catch(type2 id2)

{

// handle exceptions of type2

}

catch(type3 id3)

{

// etc...

}

catch(typeN idN)

{

// handle exceptions of typeN

}

catch(...)

{

// handle other exceptions

throw;

96

IGL - Programming CHAPTER 13. EXCEPTION HANDLING

}

// normal execution resumes here...

When an exception is thrown, the exception-handling system looks throughthe ”nearest” handlers in the order they appear in the source code. When itfinds a match, the exception is considered handled and no further searchingoccurs. An ellipsis (...) catches any exception, so must be put at the end ofthe list of handlers to avoid pre-empting any that follow it. Because the ellipsisgives no possibility to have an argument, you cant know anything about theexception or its type. It’s a ”catchall”. Such a catch clause is often used toclean up some resources and then rethrow the exception (throw).

C++ exception handling guarantees that as you leave a scope, all objects inthat scope whose constructors have been completed will have destructors called.Constructors that aren’t completed don’t have the associated destructors called.

#include <iostream>#include <c s t d l i b>using namespace std ;

class Trace {stat ic int counter ;int ob j id ;

public :Trace ( ) {

ob j id = counter++;cout << ”Constructor Trace Nr ” << ob j id << endl ;i f ( ob j id == 3) throw 3 ;

}˜Trace ( ) {

cout << ”Destructor Trace Nr ” << ob j id << endl ;}

} ;

int Trace : : counter = 0 ;

int main ( ) {try {

Trace n1 ;// throws excep t i onTrace array [ 5 ] ;Trace n2 ; // i n a c c e s s i b l e code

} catch ( int i ) {cout << ” caught ” << i << endl ;

}system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 13.1: Cleaning up

The general difficulty is allocating resources in constructors. If an exceptionoccurs in the constructor, the destructor doesn’t get a chance to deallocate theresource. To prevent such resource leaks, one must catch exceptions inside theconstructor and then release the resource.

97

IGL - Programming CHAPTER 13. EXCEPTION HANDLING

Because destructors are called in the process of throwing other exceptions,never throw an exception in a destructor or cause another exception to be thrownby some action performed in the destructor.

Standard exceptions

The set of exceptions used with the Standard C++ library is also available foruse. Generally it’s easier and faster to start with a standard exception class thanto try to define own exception. If the standard class doesn’t do exactly what isneeded, one can derive from it. All standard exception classes derive from theclass exception, defined in the header <exception>. The two main derivedclasses are logic error and runtime error, which are found in <stdexcept>(which itself includes <exception>).

#include <stdexcept>#include <iostream>#include <c s t d l i b>using namespace std ;

class MyError : public runt ime e r ro r{public :

MyError ( const s t r i n g& msg = ”” ) : runt ime e r ro r (msg) {}} ;

int main ( ) {try{

throw MyError ( ”Error message” ) ;}catch (MyError &x){

cout << x . what ( ) << endl ;}system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 13.2: Standard exceptions

Both runtime error and logic error provide a constructor that takes astring argument for storing a message in the exception object and extract itlater with virtual function what(). To avoid making yet another copy of theexception object, it is always better to catch an exception by reference insteadof by value.

Exercises

1. Implement two classes: a car and a garage containing a pointer to a car.Throw an exception when inserting too large car into a garage (in con-structor and in inserting method).

98

IGL - Programming CHAPTER 13. EXCEPTION HANDLING

2. Implement FIFO queue of integers with push and pop methods throw-ing errors OutOfMemory (derived from runtime error) and EmptyQueue(derived from logic error) subsequently.

99

Chapter 14

Templates and standardlibrary

Inheritance and composition provide a way to reuse object code. The templatefeature in C++ provides a way to reuse source code.

The template keyword tells the compiler that the class definition that fol-lows will manipulate one or more unspecified types. At the time the actual classcode is generated from the template, those types must be specified so that thecompiler can substitute them.

#include <iostream>#include <c s t d l i b>using namespace std ;

template<class T, int s i z e = 100> // temp la te c l a s sclass Array{

class RangeError {} ;T array [ s i z e ] ; //T and s i z e are temp la te parameters

public :T& operator [ ] ( int index ) ;int l ength ( ) const { return s i z e ; }

} ;

template<class T, int s i z e>T& Array<T, s i z e > : : operator [ ] ( int index ) // temp la te method{

i f ( index < 0 | | index >= s i z e )throw RangeError ( ) ;

return array [ index ] ;}

int main ( ){

Array<int> i a ;Array<f loat , 50> f a ; // temp la te i n s t a n t i a t i o n sfor ( int i = 0 ; i < 20 ; i++) {

i a [ i ] = i ∗ i ;

100

IGL - ProgrammingCHAPTER 14. TEMPLATES AND STANDARD LIBRARY

f a [ i ] = f loat ( i ) ∗ 1 . 4 1 4 ;}for ( int j = 0 ; j < 20 ; j++)

cout << j << ” : ” << i a [ j ]<< ” , ” << f a [ j ] << endl ;

system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 14.1: Generic array

T is the substitution parameter, and that it represents a type name. Also,T is used everywhere in the class where it would normally be the specific typethe array holds. Size is another parameter for array maximal size with defaultvalue 100 and inside the class methods it can be used as a member. In the mainfunctions the compiler expands the Array template (this is called instantiation)twice, to create two new generated classes, which can be thought of as an arrayof integers and an array of floats.

Standard Template Library

The Standard C++ containers and algorithms (STL Standard Template Li-brary) are built exclusively with templates and are relatively easy for the pro-grammer to use. The main and most popular STL libraries are <vector>,<list> (sequence containers), <stack>, <queue> (container adapters), <set>and <map> (associative containers).

It’s common to see polymorphism, dynamic object creation, and containersused together in a true object-oriented program. Containers and dynamic objectcreation solve the problem of not knowing how many or what type of objectsis needed. And if the container is configured to hold pointers to base-classobjects, an upcast occurs every time when putting a derived-class pointer intothe container.

#include <vector>#include <iostream>#include <c s t d l i b>using namespace std ;

class Shape // a b s t r a c t c l a s s{public :

virtual void draw ( ) = 0 ;virtual void e r a s e ( ) = 0 ;virtual ˜Shape ( ) {}

} ;

class Ci r c l e : public Shape{public :

C i r c l e ( ) {}˜ C i r c l e ( ) { std : : cout << ” C i r c l e : : ˜ C i r c l e \n” ; }void draw ( ) { std : : cout << ” C i r c l e : : draw\n” ;}void e r a s e ( ) { std : : cout << ” C i r c l e : : e r a s e \n” ;}

101

IGL - ProgrammingCHAPTER 14. TEMPLATES AND STANDARD LIBRARY

} ;

class Rectangle : public Shape{public :

Rectangle ( ) {}˜Rectangle ( ) { std : : cout << ”Rectangle : : ˜ Rectangle \n” ; }void draw ( ) { std : : cout << ”Rectangle : : draw\n” ;}void e r a s e ( ) { std : : cout << ”Rectangle : : e r a s e \n” ;}

} ;

class Line : public Shape{public :

Line ( ) {}˜Line ( ) { std : : cout << ”Line : : ˜ Line\n” ; }void draw ( ) { std : : cout << ”Line : : draw\n” ;}void e r a s e ( ) { std : : cout << ”Line : : e r a s e \n” ;}

} ;

int main ( ){

vector<Shape∗> s ; // polymorphic conta iners . push back (new Rectangle ) ; // upcas t ings . push back (new Ci r c l e ) ;s . push back (new Line ) ;vector<Shape ∗> : : i t e r a t o r i ; // i t e r a t o rfor ( i = s . begin ( ) ; i != s . end ( ) ; i++){

(∗ i )−>draw ( ) ; //polymorphism}for ( i = s . begin ( ) ; i != s . end ( ) ; i++){

delete ∗ i ; // polymorphic d e s t r u c t i on}system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 14.2: Figures

If a container holds pointers or references to base-class objects, then whenthe virtual destructors are called for those objects everything will be properlycleaned up.

Iterators

The solution for flexible element access is an iterator, an object whose job isto select the elements within a container and present them to the user of theiterator. The container type name must be used to produce the appropriateiterator.

vector<Shape*>::iterator i;

102

IGL - ProgrammingCHAPTER 14. TEMPLATES AND STANDARD LIBRARY

Iterators can be initialised (i.begin()) incremented (i++), dereferenced toproduce the object they’re currently selecting (*i), and tested to see if they’reat the end of the sequence (i.end()). The iterator lets to traverse that sequencewithout worrying about the underlying structure - that is, whether it’s a vector,a linked list, a set, or something else.

Thanks to this it is easy to change the type of container that this pro-gram uses with few lines. Including for example <list> instead of including<vector> and using container type list instead of a vector in the declara-tions.

list<Shape*> s;

...

list<Shape*>::iterator i;

Everything else goes untouched. Developers can easily switch between vector

and list or any other container that supports the same interface and see whichone works fastest for their needs.

Template interface

An important idea is that a template implies an interface. That is, even thoughthe template keyword says ”I’ll take any type”, the code in a template definitionactually requires that certain operators and member functions be supported -thats the interface. So in reality, a template definition is saying, ”I’ll take anytype that supports this interface”. Thus some of the containers require propermethods to be defined for a class being a template parameter, like operator<

necessary for the priority queue.

#include <iostream>#include <queue>#include <s t r i ng>#include <c s t d l i b>using namespace std ;

class ToDoItem{

s t r i n g item ;char primary ;int secondary ;

public :ToDoItem( s t r i n g td , char p r i =’A ’ , int s ec =1)

: item ( td ) , primary ( p r i ) , secondary ( s ec ) {}friend bool operator<(

const ToDoItem& x , const ToDoItem& y){

i f ( x . primary > y . primary )return true ;

i f ( x . primary == y . primary )i f ( x . secondary > y . secondary )

return true ;return fa lse ;

}

103

IGL - ProgrammingCHAPTER 14. TEMPLATES AND STANDARD LIBRARY

friend ostream&operator<<(ostream& os , const ToDoItem& td ){

return os << td . primary << td . secondary<< ” : ” << td . item ;

}} ;

int main ( ){

pr i o r i t y queue<ToDoItem> toDoList ;toDoList . push (ToDoItem( ”Empty t ra sh ” , ’C ’ , 4) ) ;toDoList . push (ToDoItem( ”Feed dog” , ’A ’ , 2) ) ;toDoList . push (ToDoItem( ”Feed b i rd ” , ’B ’ , 7) ) ;toDoList . push (ToDoItem( ”Mow lawn” , ’C ’ , 3) ) ;toDoList . push (ToDoItem( ”Water lawn” , ’A ’ , 1) ) ;toDoList . push (ToDoItem( ”Feed cat ” , ’B ’ , 1) ) ;while ( ! toDoList . empty ( ) ){

cout << toDoList . top ( ) << endl ;toDoList . pop ( ) ;

}system ( ”PAUSE” ) ;return EXIT SUCCESS ;

}

Listing 14.3: Priority queue

Exercises

1. Extend the programme of shapes with proper structures for each derivedclass (for the line use a vector of points). Add a new class for point.Implement constructors, destructors and all necessary methods (drawing,erasing).

2. Use the priority queue container to implement a class which sorts strings.Read all lines from input text file and make output file with all the linessorted alphabetically.

104