Pai Reader CPP 2018

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 32

Programming in AI - C++ and openFrameworks

In the programming course of module 6 of CreaTe we will use openFrameworks as programming


environment. Reasons to do that are, first, that you learn a second programming language, C++.
Even if there are many similarities with Java (Processing), learning a second language helps for a
better understanding of programming concepts. Second, C++/openFrameworks allows to write
more efficient code: execution is faster, more objects can be created than in Processing. Third,
another advantage is that with openFrameworks we have access to a huge amount of existing
libraries on all sorts of hardware, vector graphics, physics libraries etc. etc. The advantages come
for the disadvantage that openFrameworks is somewhat more tedious than Processing (please do
not underestimate this!).

The reader here is meant as a small intro to the topics of C++ and openFrameworks we certainly
will need in the course. It is not a substitute for other sources. 

For example http://www.cplusplus.com/doc/tutorial/ gives a good overview.

For openFrameworks there is on http://openframeworks.cc/learning/ a start for tutorials.

Also on youtube there are numerous videos that explain concepts nicely.

1 C++ syntax 3
1.1 comments 3
1.2 basic types 3
1.3 constants 3
1.4 variables 4
1.5 expressions 5
1.6 advanced types 5
1.6.1 enumeration types 5
1.6.2 arrays 6
1.6.3 two-dimensional arrays 6
1.6.4 struct type 7

1.7 control structures 7


1.7.1 if-then 8
1.7.2 if-then-else 8
1.7.3 switch 8
1.7.4 counted loop 9
1.7.5 conditional loop 10

1.8 functions 10
1.8.1 parameter list, result type and body 10

1
1.8.3 expressions and functions 12

1.9 program structure and functions 13


1.10 classes 14
1.10.1 encapsulation, private and public variables 15

1.11 program structure and classes 17


2. compiling C++ programs 18
2.1 compiling a single program 18
2.2 compiling with multiple files 18
3. program structure in openFrameworks 20
4. pointers 23
4.1 definition of pointers: what is a pointer? 23
4.2 how to declare a pointer? 23
4.3 how to use a pointer? 24
4.4 the NULL pointer 25
4.5 why pointers? (life is already complicated enough.) 25
5. parameters in function calls 26
5.1 call by value 26
5.2 call by reference 26
5.4 what was a side effect again? 28
5.5 reference (pointer) to an object 29
5.6 pointers and arrays 30
5.7 pass a reference to an object as parameter for a function. 31

2
1 C++ syntax
Originally, C++ is C extended by object oriented concepts. (Modern C++ is different from C with
object oriented concepts. Here, however, we choose the simple way to start, and consider that as
a stepping stone to C++ programming.)
It is recommended to have syntax references at hand, as we all have to program in different
languages all the time.

1.1 comments
include headers in your programs explaining what the code is about & name & date.
explain what variables do contain and what they are used for.
explain for fragments of code what the purpose here is, what the result.
when you use meaningful names you can reduce the amount of comment!

// this line is a comment

a=0; // here, the end of the line is in comments

/* here
a
whole
block
is
a
comment
*/

Comments are necessary to explain a program, make it human readable.



A different way to make a program human readable is to choose meaningful names for
variables,

functions and classes, and write well structured code: in this case the code is often already
self-explaining (depending on your ability to write your code in this way). Then less
comment is needed, and the code looks also more compact.

Use meaningful names and find a balance with comments needed.

1.2 basic types


int, bool, char, float, double (meaning double float)...
typical ranges for variables are in the table below.

in the tutorial you will create a table that is valid for your laptop/processor.

For unsigned short int and signed short int, what should be the range in the table above?

1.3 constants

3
const float ThisIsAConstant = 23.0f;
const int MazeSize = 40;

Constants cannot be changed during a program execution. They are useful as global variables.
Actually, they do not increase expressiveness wrt variables, but constants allow a safer style of
writing programs.
The name convention for constants is as in the example: start with an upper case letter, start each
new word within the name with an upper case letter. (For java the name convention would be:
THIS_IS_A_CONSTANT)

1.4 variables

simple declaration
int a; 

float myNumber;

multiple declaration
int a, b, c;

declaration with initialization


int a = 0;
float myNumber = 2.7f; // note the difference between
double yourNumber = 16.23; // values of float and double

4
The name convention for variables is to start with a small letter. Capital letters for sub-words in the
name make names more readable.

1.5 expressions

Also expressions have a type. We also say the expression evaluates to type int or float…

int a,b;
(4*a+b)-3 is an expression

float mynumber = 3.14f;
float r = 2.0f;

(2 * r) * 3.14f is an expression

The result of an expression can be assigned to a variable:

float area;
area = (2 * r) * 3.14f;

Evaluation of assignments goes from right to left:


first evaluate the expression, then perform the assignment.

1.6 advanced types

1.6.1 enumeration types

Enumeration types allow to define a set of values. This is mainly useful for writing easy-to-read
code.


enum stoplight { red, yellow, green};

enum fruit {apple, banana, grapes, orange};

In our search assignments there are the directions listed as enum type: this makes code more
readable and prevent faults introduced when coding (“east” is more intuitive than “3” for a
direction.)

enum Direction {
DirectionNorth = 0,
DirectionEast = 1,
DirectionSouth = 2,
DirectionWest = 3
};

5
1.6.2 arrays

simple declaration

int useless [7]; more of the same

declaration with initialization


int useless [7] = {1,2,3,4,5,7,8};

note that an array of length n has indices from 0 to n-1


useless[0]=1;
useless[1]=2;
useless[6]=8;

1.6.3 two-dimensional arrays

int a [3][4];

declaration with initialization


int a[3][4] = {
{0, 1, 2, 3} , /* initializers for row indexed by 0 */
{4, 5, 6, 7} , /* initializers for row indexed by 1 */
{8, 9, 10, 11} /* initializers for row indexed by 2 */
};

or, equivalently

int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

6
1.6.4 struct type

collect heterogeneous 

aspects of one “instance”.

A very simple (pre-object oriented) data-structure is the struct-construction,


allowing to pack a number of different types into one bigger object. In C++ the
difference to a class doing precisely the same thing is very little.

struct product { // this is the definition of a composed


type
int weight;
float price;
} ;

product apple; // here we declare variables of the


composed type
product banana, melon;

apple.weight = 100; // see, how we can access the components!


banana.price = 1.30;

1.7 control structures

in general, a program is a sequence of instructions,


and the execution mechanism is that one instruction is executed after the other.
control structures allow to change this simple sequential order
by either conditional executions,

7
or by loops allowing to execute the same code many times.
in principle, also the structures of this section are not at all new: they are the same in Java….

1.7.1 if-then
if (boolean_expression){
// statement(s) will execute if the boolean expression is true
}

example:
if (a<=20) {

cout << “a is smaller than 20” << endl;
}

1.7.2 if-then-else

if(boolean_expression){
// statement(s) will execute if the boolean expression is
true
}
else{
// statement(s) will execute if the boolean expression is
false
}

example:
if (a<=20) {

cout << “a is smaller than 20” << endl;
}
else {
cout << “a is not smaller than 20” << endl;
}

1.7.3 switch

A switch statement is useful, if we have more than two cases to distinguish,


and if the condition for the switch is, e.g., an input letter.

switch(expression){
case constant-expression :
statement(s);
break; //optional
case constant-expression :
statement(s);

8
break; //optional

// you can have any number of case statements.


default : //Optional
statement(s);
}

an example

// the first attempt of neighbour determination in a maze



// could look like this

mazeDim=40; //dimension of the maze


direction search = south; //direction to search for a next cell
int actualCell=67; //number of the actual cell
int neighbour;

switch(search){
case north :
neighbour = actualCell - mazeDim;
break;
case east :
neighbour = actualCell + 1;
break;
case south :
neighbour = actualCell + mazeDim;
break;
case west :
neighbour = actualCell - 1;
// we do not need a break-statement in the last case: why?
}

1.7.4 counted loop

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


//do something
}

or, differently

int i;
for (i=0; i<30; i=i+5) {
//do something
}

9
1.7.5 conditional loop

while (boolean_condition) {
//do something
}

example

int i=0, j=37, k=7;


while (j>0) {
j=j-k;
i=i+1;
}

what does this program fragment do?


what values are in j and i after the loop?
can you achieve the same result without using a loop, but using the modulo (%) or devision (/)
operators?

do
{
//do something
} while (boolean_condition)

example

int i=0, j=37, k=7;


do 

{
j=j-k;
i=i+1;
} while (j>0)

what is the difference between this loop and the one above?

in which cases do they give a different result?
(see tutorial questions)


1.8 functions

1.8.1 parameter list, result type and body


parameters with types, result type and implementation….

10
Internally, a function with a defined functionality can be implemented in different ways.
The outside determines how it can be used and called.

return_type function_name( parameter list ){


body of the function
}

example 1:

int maximum (int a , int b){


int result; // declare a local variable

if (a>=b) // calculate something


result = a;
else
result = b;

return result; // give the result back


}

example 2:

bool is_greater (int a , int b){


bool result; // declare a local variable

if (a>=b) // calculate something


result = true;
else
result = false;

return result; // give the result back


11
}

The name convention for function names and method names is to start with a lower case letter.

Splitting of function/method declaration and definition:


For functions there is also a different syntactic form that we will use for openFrameworks.
Basically, the function declaration (result type, name and list of parameters) can be separated from
the body of the function. An advantage of this syntax is at this point not obvious. Later, function
declarations and function bodies go into different files. See the following example, where the
enumeration type Direction is as in 1.1.6 above:

In one file ending with the suffix .hpp the declaration of the function is done, i.e. only result type,
name and list of parameters:

Direction oppositeDirection(Direction); // function declaration

In another file, with the same name as the first, but with the suffix .cpp we have the definition of
the function, including the body:

//function definition
Direction oppositeDirection(Direction direction) {
switch (direction) {
case DirectionNorth:
return DirectionSouth;
case DirectionEast:
return DirectionWest;
case DirectionSouth: main does not need parameters,

return DirectionNorth; but has a result type int.
case DirectionWest: 0 as result indicates that the program
return DirectionEast; was successful,
}
1, or different indicates that there 


1.8.3 expressions and functions

expressions are composed from:


• constants
• variables
• functions
• operators of the type of the expression
int a,b;

//expression on integers, if the result type of f is also int


(4*a+b)+ f(a,b)-3

12
the result of an expression can be assigned to a variable:

float area;
area = (2.0f * radius(x,y)) * 3.14f;

if the result type of the function radius is of type float


(and x and y have the type that the function radius requires)
evaluation of assignments goes from right to left:
first, if functions appear in the expression,
evaluate these, then evaluate the expression, then perform the assignment.

1.9 program structure and functions

In Processing we have a setup part and a draw part that is executed over and over again.
This structure will re-appear in OpenFrameworks, that we will treat later.
Here, for the moment, we look at plain C++ programs.
It is better if you have first an idea what plain C++ is and how it works,
before we enter some advanced C++ based framework, offering a lot of extra possibilities.

function
#include <iostream>
definition
using namespace std;
 

void other_function( parametertype parametername){


// here the body of the other_function
}

int even_another_function(....){
....
}
function
definition

int main(){
/* the function main is the only function that

is executed automatically.
every other function to be executed has to be
called (directly or indirectly from here
*/

other_function(bladibla); // function call


}

13
1.10 classes

An object is a group of variables, equipped with a group of operations that access these variables.
Often, we need to create many similar objects. That leads in a natural way to the concept of a
class.
A class is a "blueprint" for (similar objects. All instances of a class (objects) have the same
variables 
(probably with different values) and are equipped with the same operations as defined in the
"blueprint".


In object oriented terminology, an object's variables are variously called instance variables or
member variables, and its operations are called constructors and methods.

A constructor is an operation that creates and initializes a new object of a class. In both Java and
C++, a constructor is always named after the class to which it belongs.
■ Constructors have the same name as the class.
■ Constructors do not return any values.
■ Constructors are invoked first when a class is initialized. Any initializations for the class
members, memory allocations are done at the constructor.

example:

// classes example
#include <iostream>
using namespace std;

// class definition
class CRectangle {
int x, y;
public:

// the constructor
CRectangle (int a,int b)
{x=a; y=b;};

// a method
int area () {return (x*y);}
};

// the main function is called by default


int main () {
// declaration of an object with initialization
CRectangle rect (3,4);
cout << "area: " << rect.area();

// in C/C++ a function call always has a result, indicating whether it


was 

// successful or not:
// therefore main has type int and a return value has to be passed
return 0;
}

14
This is the easiest version for a class definition: the constructor is inside. Below is another version
where within the class definition only the declaration of the constructor is located. The actual
definition (body) of the constructor is outside the declaration of the class.
(Later, in OpenFrameworks this is the way to define classes, where the declaration of a constructor
and the definition are separated as below, and even in different files.)

// example: class constructor When you have designed 



#include <iostream>
your nice class and objects you
using namespace std;
want to make position and size to be
//class definition changeable “from outside”. 

class Rectangle {
int width, height;
public:
Rectangle (int,int); // for the constructor just the 

// parameter&name declaration

//a method You do not want


int area () {return (width*height);} that internal details of your
};
objects are accessible
// definition of the class constructor: 
 or can be changed.
// assigns the parameters to the instance variables These are private.
Rectangle::Rectangle (int a, int b) {
width = a;
height = b;
}

// the main function is executed by default


int main () {
// definition of two objects of the class Rectangle with
initialization
Rectangle rect (3,4);
Rectangle rectb (5,6);

// output functions
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;

return 0;
}

1.10.1 encapsulation, private and public variables


A core concept of object oriented programming is encapsulation. Only some members of an
object are visible and accessible from outside, others are only internally accessible. Reasons are
that programming becomes more clear and safe, and it also supports a way of decomposition, or
separation of concerns:
■ more clear and safe: only a couple of operations are allowed from outside, and when using
only these operations the whole object remains consistent. When accessing other variables

15
in an unintended way, the whole object may be messed up, i.e. inconsistent states could
be created.
■ separation of concerns: often there are different ways to realize a method, different
algorithms to implement the same thing (as you will experience in this course, hopefully).
And, first, it is not necessary to see from outside how a method or object is realized - we
are only interested how it works (disclaimers later in the course). And, second, at some later
moment, it could be the case that there is some better implementation of a method - then
we simply can replace the "internal" part of a class, and do not need to change the rest of
the program.

class Clown{

public:
float x_position;
float y_position;
float size;

private:
float x_pos_nose;
float y_pos_nose;
...

public:
// constructor
Clown(){
....
}

// methods
void draw_me(){
}
...
}

If the position of the nose is not encapsulated (private), then it is possible to 



mess up the clown by changing the position of the nose from outside in an unintended way.

16
1.11 program structure and classes

In Processing we have a setup part and a draw part that is executed over and over again.
This structure will re-appear in OpenFrameworks, that we will treat later.
Here, for the moment, we look at plain C++ programs.
It is better if you have first an idea what plain C++ is and how it works,
before we enter some advanced C++ based framework, offering a lot of extra possibilities.

#include <iostream>
using namespace std;
 

void other_function( parametertype parametername){


// here the body of the other_function
}

int even_another_function(....){
....
}

class Defrobnicator {
.....
}

int main(){
/* the function main is the only function that

is executed automatically.
every other function to be executed has to be
called (directly or indirectly from here
*/

other_function(someParameter); // function call


}

So far, the programming concepts are pretty much the same as we had in Processing. Even if
the syntax is sometimes slightly different.

Even if it is going into more depth, the course here will not cover all concepts of C++. In fact, it is
not necessary to know everything about C++ to write good C++ programs. Even worse, there are
a number of concepts that allow/invite to write very unclear programs. For our purposes and for
learning a clear way of thinking and programming, we aim at simple and clear structures. In the
end, complexity and unclear constructions pop up uninvitedly anyway - so the art is much more to
bring simplicity in thinking, structures and programs than to introduce spaghetti-friendly concepts.
It will certainly not demonstrate you intellectual brilliance if you write unclear programs.


17
2. compiling C++ programs

2.1 compiling a single program


The C++ programs we consider will use code that is already written somewhere else, standard
functions for input and output, and libraries. Also, later, our C++ programs will be distributed
over a couple of files. To organize this properly and generate a single consistent executable
program in the end, compilers have to do a number of different things, related to the structure of
the program.
Typically, the first part of a C++ program concern header files, like

#include <iostream>

When we compile a program, first a preprocessor gets active. The preprocessor copies textually
the content of iostream into our program, resulting in an object file, that typically has the same
name as out original file, but the extension .o or .obj. Note, that iostream contains program
fragments in C++.
After preprocessing, the compilation starts. From the extended program in the object file a
program in machine language is created.
It is also typical, that we use a lot of functions from libraries. The code in the libraries is already
compiled, we do not need to compile it each time again. What we have at a certain moment is
therefore pieces of machine code in different files that has to be glued together to a single
program. To solve this problem, the compiler creates tables of the objects and methods
contained in each file. After compilation, the linking and "binding" has to take place. From the
information in all these compiler-generated tables the linker can find the loose ends that need to
be put together, like a method call in one file, where the method is defined in another file.
The picture below illustrates the phases of a compilation process.

2.2 compiling with multiple files


Typically, your programs will be distributed over a number of files. The programming
environments we will use later will help with the easy organization of this. For here, to understand
18
the concepts, we show a little example how to deal with programs distributed over multiple files
for compiling without a programming environment.
See here two programs, main.cpp and add.cpp. main.cpp uses a function add that is defined in
add.cpp. In order to do that, it needs at least a forward declaration within main.cpp of add.
main.cpp:

#include <iostream>
using namespace std;

int add( int x, int y); // forward declaration using function prototype

int main()
{
cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
return 0;
}

add.cpp:

// definition of the function add

int add(int x, int y)


{
return x + y;
}

For compilation, we may compile each file individually, running the compiler with the option that
the code is not complete. Then, we run the compiler with both object files. In Unix (on my mac)
the resulting file is by default called a.out. I can call this file to let it run. See below the command
line steps to compile, link and run the program main.cpp

19
3. program structure in openFrameworks
One of the challenges using openFrameworks is to understand how code is distributed over
files within the programming environment. Lot of what you see in openFrameworks is also present
in Processing, but hidden.
Best is to look at the example you get for the assignment, the maze generator.

• In principle you start each new project in openFrameworks with the project generator. This
results already in a structure of three files given: main.cpp, ofApp.cpp, ofApp.h
• Leave the function main.cpp untouched (except perhaps for windowsize, but that can be
done more elegantly). main() just starts the whole machinery of an openFrameworks project.
• All class definitions are split in two parts: one including all the headers, which here means all
the declarations. This one ends with .hpp or .h. The other one contains the definitions of the
methods etc., this is where the actual implementation of the methods/functions is done.This
one ends with .cpp.
• In App.h there are already a number of variables and functions/methods declared. You
declare all your “global” variables, like the variables you would put above setup() in
Processing. In our maze generation example the App.h the only object declaration added
here is the last: Grid grid; (also # include Grid.hpp is added more on this below).The rest is
already given by the project generator:

App.h:

#pragma once

#include "ofMain.h"

#include "Grid.hpp"

class ofApp : public ofBaseApp {


public:
void setup();
void update();
void draw();

void keyPressed(int key);


void keyReleased(int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void windowResized(int w, int h);
void dragEvent(ofDragInfo dragInfo);
void gotMessage(ofMessage msg);

Grid grid;
};

The (beginning of) ofApp.cpp looks as below. Into the part setup go all the initialisations, similar
to Processing. In openFrameworks there are two cyclically executed functions: update() and
draw(), first always update(), then draw().

20
The code added here to the structure given is the initialisation of the random generator and call
of the method generateMaze() for the object grid. In draw() the method draw() for the object grid
is called. In the keyReleased() method a menu to start different methods is added.

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
ofBackground(255);

// init random generator


std::srand(std::time(0));

grid.generateMaze();
}

//--------------------------------------------------------------
void ofApp::update(){

//--------------------------------------------------------------
void ofApp::draw(){
grid.draw();
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
switch(key) {
case 'm':
grid.reset();
grid.generateMaze();
break;
case 'g':
grid.generateMaze();
break;
case 'd':
grid.depthFirstSearch();
break;
case 'b':
grid.breadthFirstSearch();
break;
}
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

All files other than main.cpp, ofApp.cpp and ofApp.h are added after project generation.

We look at them one for one:


21
in C++ we have constants, i.e. values that do not change throughout the whole program. It is
good style to put them into one file, here constants.h

Gridelement.cpp, Gridelement.hpp and also Grid.cpp and Grid.hpp contain class definitions.
Direction.cpp and Direction.hpp contains a type definition and a function.

In Gridelement.hpp you see the class definition as usual, only the “bodies” of the methods are
missing: these are contained in Gridelement.c.

By looking at the example you will understand how the syntax works.

GridElement.hpp:

#ifndef GridElement_hpp
#define GridElement_hpp

#include "Direction.hpp"

class GridElement {
public:
GridElement();

void reset();

void draw(int x, int y);


void drawWall(int x, int y, Direction direction);

void removeWall(Direction direction);

GridElement *neighbours[N_DIRECTIONS];
bool walls[N_DIRECTIONS];

// variables for maze generation and path finding


bool visited;
bool marked;
GridElement *parent;
};

#endif /* GridElement_hpp */

Last point before starting is to understand how #include has to be used:



In principle, it is always the header files (.h or .hpp) that have to be included, if methods or
variables/constants from this other file are used. In our example here it is that case that 

the class GridElement needs the definitions of Directions, therefore in GridElement.hpp we need
a #include “Direction.hpp”, etc.

In ofApp the class grid is used, therefore ofApp.h contains an include: #include “Grid.hpp"

22
4. pointers

4.1 definition of pointers: what is a pointer?

A pointer is a variable that contains an address. The analogy we use here is the number of a coat
as you get it, e.g., from the checkroom at the theater.

The checkroom number is an address. .

It points at your jacket at the checkroom.

In our analogy the checkroom represents the memory.


4.2 how to declare a pointer?
As all variables, a pointer has to be declared. 

As all variables, a pointer has a type. 

The type of the pointer depends on the type of what it is pointing at: 

this can be a variable of type int or char or bool or any other.

In our example, assuming that coat is a type, 

the type of the checkroom number is a pointer to coat, 

and the declaration of a pointer variable my_checkroom_number would be:

coat * my_checkroom_number;

In a proper C/C++ program the declaration of a pointer would look as follows:

23
int * number_address; 

char * character_address;

The first is a pointer with the name number_address that points at a variable of type int. 

The second is a pointer named character_address that carries the address of a variable of type
char.

4.3 how to use a pointer?

When using pointers, we basically need two operations. In our analogy, these are:
1. For the checkroom number we want to get the coat. 

For this purpose we use the dereference operator * . Read * as "points at".
2. For the coat in the checkroom we want to know what its checkroom number is. For this
purpose we use the reference operator &. Read & as "the address of”.

real life. how to use pointers here?

For the following program sequence find out what the values of the
variables firstValue and secondValue are in the end!

int main (){

int firstValue = 5, secondValue = 15;


int * p1, * p2;

p1 = &firstValue;
p2 = &secondValue;
24
*p1 = 10;
*p2 = *p1;
p1 = p2;
*p1 = 20;

return 0;
}

4.4 the NULL pointer

int * p;
p = 0; // p has a null pointer value

or, equivalently

int * p;
p = NULL; // NULL is the null pointer

It is useful to have a pointer that points to a dedicated value NULL.


Often, we use it at the end of a chain of pointers, just to indicate that this is the end of the chain.

There are different opinions about whether to use “0” for the null pointer, as in the first example,
or the “NULL” as in the second example.

From compiler point of view the first, a “0” is safer.

From a program clarity point of view, the NULL is nicer, because this NULL is

exclusively used for the null pointer, and it visualises directly the context of pointers.

4.5 why pointers? (life is already complicated enough.)

Basically, there are the following advantages of pointers:


1. The first is evident from our analogy: instead of carrying the jacket into the theater, we only
have to carry a tiny checkroom number. In the case of a program, you can have huge data
structures, e.g. defined by a struct (record). Assume we have a data structure dinosaurus.
A variable of type dinosaurus may be parameter of a function. Calling the function the
whole variable of type dinosaurus has to be copied. The program may getting faster, when
we give only the pointer to this variable as a parameter of the function. If the dinosaurus is
needed, it can be found using the pointer.

But it does not need even a dinosaur, even arrays may not be passed as argument for a
function, instead a pointer to an array has to be passed.

25
2. We can build dynamic data structures, as, e.g., trees, or lists. The point here is that we
often do not know how long a list will be when we write a program - it may be different for
each run of the program - or we do not know how big a tree is. Without pointers we have
to declare an array that is big enough for the worst case. This may be a waste of memory,
and, if we add on all memory wastes for oversizing memory space, we may get slow
programs or programs that do not fit into the memory.

3. Next, we can build data structures that efficiently support the operations we need. For
example, adding an element in a sorted list implemented as an array will probably cost
many shift-operations. With a sorted list implemented by pointers, there are basically two
assignments needed.

5. parameters in function calls

5.1 call by value

Call by value refers to the “classical” way to do function calls.


When a function is called, the value of all parameters is copied and assigned to the parameters in
the parameter list of the function.

An example:
For the function call, the values of a and b, 

{
which are 3 and 4, are copied and assigned to the
int a=3;
int b=4; parameters of the function maximum.
int c; The actual variables a and b cannot be changed
by the function called.
c= maximum(a,b);

.....
}

int maximum(int x, int y){


if (x>y) At function call, the parameters x and y, 

return x;
get a value assigned and in the body of the
else
return y; function, whenever it is referred to

} x and y, the values of x and y are used.

5.2 call by reference

When a function is called by reference, the parameters are not copied,


but a reference (pointer) to the calling variables is passed.
The difference to “call by value” is, that the calling parameter can be changed by the
called function (side effect).

26
example:

void increment(int *x){


*x = *x + 1;
}

int main() { After the function call


int a=3;
a has the value 4.
increment(&a);
}

( small side remark:

in C++ there is made a difference between pointers and references, which does not refer to the
concepts described above.

when we have

int i = 3;

then:

int *point = &i;

declares point as a pointer to an integer variable and initialises it with the address of i.
On the other hand:

int &ref = i;

declares a variable ref as a reference to an integer variable, initialised with the address of i.
ref is called a reference in contrast to the pointer point above. One difference is an optical
one, which is about the use of the dereference operator:
*point=4; does the same as ref=4;

// illustration of the difference between a pointer and a reference

#include <iostream>
using namespace std;

int main(){

int i = 3;

// this is the declaration of a pointer to an int that is initialised with the address
of i int *point = &i;

// this is a declaration of a reference to an int which is i in the example


int &ref = i;

// the main difference is that a pointer needs a dereference symbol, the reference
not.

27
// the both statements below do the same
*point=4;
ref=4;

cout << "pointer's content is : " << *point <<endl;


cout << "reference's content is : " << ref <<endl;

// another difference is that a pointer may point to another integer later,


// a reference cannot: after assigning ref=j, the reference is still to variable i

{ int j=5; ref=j; cout << "reference's content is : " << ref <<endl;
cout << "i's content is : " << i <<endl;}

return 0;
}

(this is just for completeness, we will not go further into this.)

)
5.4 what was a side effect again?

When we have a function without side effect we see what is


going in and what is coming out, and we know that the rest of
the world, resp. our variables, is not changed.
This supports a clear programming style and helps to write
correct programs.

When we have a function


with side effect, variables/objects that are not mentioned as
parameters may still be changed.
It is necessary to use side effects very carefully, because we can
easily loose overview about what a function is doing.

The side effect of our make-a-cake function is that it has a dirty kitchen as result, which is not
indicated as a result of the function.

Sometimes side effects are necessary:


- when writing something on the screen, then the screen changes obviously. This can only be
realized by a function with side effect.
- when there is only one huge object in the program, as eg a maze in our maze generation
program.
- when having functions that have more than one result.

28
5.5 reference (pointer) to an object

class Chip { // the class from the tutorial


public:
float length; // object parameters
float breadth;

Chip(float _length, float _breadth) { // Constructor!!!!


length = _length;
breadth = _breadth;
}

float getPrice() { // method(s)


return breadth*length;
note
}
}; the difference to
one_chip.length !

int main() {
Chip *one_chip = new Chip(2.7f, 3.8f);

cout << "the chip is " << one_chip->length << " long" << endl;
cout << "the chip is " << one_chip->breadth << " broad" << endl;

return 0;
}

an equivalent alternative to

Chip *one_chip = new Chip(2.7f, 3.8f);

would be

Chip *one_chip;
one_chip = new Chip(2.7f, 3.8f);

compare to the static solution to declare an object:

Chip another_chip = Chip(2.0f, 3.7f);

... another_chip.length = .....

29
5.6 pointers and arrays

Arrays and pointers are closely related.

int numbers [15];


int * my_number_pointer;

my_number_pointer = numbers; // the array name is actually a pointer

Alternatives to address array fields.

int numbers[5];
int * p = numbers; // pointer p points at the first array element

*p = 1; // is the same as numbers[0]=1;
p++; *p = 2 // has the same effect as numbers[1]=2;
p = &numbers[2]; // p points now to numbers[2]
*p = 3; // has the same effect as numbers[2]=3
p = numbers + 3; // p points now to numbers[3]
*p = 4;
p = numbers;
*(p+4)= 5; // has the same effect as numbers[4]=5

Pointer arithmetic

A pointer contains an address.


An address is a number.
We can do some arithmetic with pointers, but we have to be careful.

Assume we have a pointer p.

When we do p++ it points to the next element.


So, in this case it is not the memory address +1,
but it adds a number of bytes.
The number of bytes it adds, depends on the type of object it points at.
When we have a char a pointer p points at, p++ will increase p with one byte.
If we have

char *mychar; // char needs one byte


short *myshort; // short takes 2 bytes
long *mylong; // long takes 4 bytes

the following incrementations have the effect illustrated in the 



graphics to the right.

mychar++;
myshort++;
mylong++;


30
5.7 pass a reference to an object as parameter for a function.

Arrays are not passed to functions as parameters, only a pointer to an array.

#include <iostream>
using namespace std;

// function with a pointer to an array as parameter


double getAverage(int arr[], int size){
int i, sum = 0;
double avg;

for (i = 0; i < size; ++i){


sum += arr[i];
};

avg = double(sum) / size;


return avg;
};

int main ()
{
// an int array with 5 elements.
int ages[5] = {10,47,18,80,81};
double average;

// pass pointer to the array as an argument.


average = getAverage( ages, 5 ) ;

// output the returned value


cout << "Average age is: " << average << endl;

return 0;
}

31
example: Pass a reference to an object as parameter for a function.

// definition of the class Chip as in the examples above


class Chip {

}

// example for a function with pointers to objects as arguments

bool is_longer (Chip * c1, Chip * c2){

if ( c1->length >= c2->length)


return true;
else
return false;
}

int main() {

// declare two pointers to objects of the class Chip


// and initialise them with two new objects
Chip * one_chip = new Chip(2.7f,3.8f);
Chip * second_chip = new Chip(5.7f,3.8f);

// here a function call with pointers to objects


if ( is_longer(one_chip,second_chip) )
cout << "The first chip is longer than the second \n";
else
cout << "The second chip is longer than the first \n";
}

32

You might also like