Saturday, September 30, 2017

Lecture Notes 01

Object Oriented Programming: Lecture notes 1

Author: A. El-Gadi/Tripoli University

1) The evolution of computer programming

The history of computer programming can be summarized as the quest to make programming computers more natural to human beings. In that sense, the story of the evolution of programming is the story making programming more compatible with the natural way people think about the world around them.
At the dawn of computing, we were content with being able to enter programs into computers in binary format. Where each machine instruction is represented by a unique binary code. Writing a program in machine code was indeed a tedious and error-prone task which constituted a barrier in the way of building larger and more complex program. Listing 1 shows an example of machine code in binary along with the corresponding assembly code and the c language code from which the machine code was produced.
The next step in the evolution of programming was the invention of assembly language, which is basically a set of mnemonics that correspond to machine instructions. Those mnemonics were chosen to be descriptive of what an instruction did. Simple as it is, the principle upon which assembly was based, greatly simplified the task of programmers making them more productive and more capable of tackling more complex tasks. However, assembly suffered from a serious shortcoming, namely that its syntax is an almost direct mapping of the machine architecture. This meant that programming in assembly involved following closely the way the machine operated; see listing 1 to appreciate what adding 4 numbers and storing them in a variable looks like. This also meant that assembly programmers had to think about the specifics of the machine they were programming in order to accomplish their tasks.
High level programming languages sought to overcome this limitation by separating the logic part from the machine part. This is called abstraction -i.e. Abstracting the language from the details of the machine. High level language design philosophy revolves around making the programmer thing about the problem at hand -its logical, relational, structural components- and forgetting about the hardware at hand. The details of converting (compilation) a high level code to the proper machine code is left to the compiler which ”knows” the architecture of the machine in which it is installed.

#include<stdio.h>
void main(){int h=15,i=31,j=63,k=127,m=255,n=511;
h=i+j+k+m;}

01) 1100 0111 0100 0101 1110 1000 0000 1111 0000 0000 0000 0000 0000 0000     mov    [bp-0x18],15
02) 1100 0111 0100 0101 1110 1100 0001 1111 0000 0000 0000 0000 0000 0000     mov    [bp-0x14],31
03) 1100 0111 0100 0101 1111 0000 0011 1111 0000 0000 0000 0000 0000 0000     mov    [bp-0x10],63
04) 1100 0111 0100 0101 1111 0100 0111 1111 0000 0000 0000 0000 0000 0000     mov    [bp-0xc],127
05) 1100 0111 0100 0101 1111 1000 1111 1111 0000 0000 0000 0000 0000 0000     mov    [bp-0x8],255
06) 1100 0111 0100 0101 1111 1100 1111 1111 0000 0001 0000 0000 0000 0000     mov    [bp-0x4],511
07) 1000 1011 0100 0101 1111 0000                                             mov    ax,[bp-0x10]
08) 1000 1011 0101 0101 1110 1100                                             mov    dx,[bp-0x14]
09) 0000 0001 1100 0010                                                       add    dx,ax
10) 1000 1011 0100 0101 1111 0100                                             mov    ax,[bp-0xc]
11) 0000 0001 1100 0010                                                       add    dx,ax
12) 1000 1011 0100 0101 1111 1000                                             mov    ax,[bp-0x8]
13) 0000 0001 1101 0000                                                       add    ax,dx
14) 1000 1001 0100 0101 1110 1000                                             mov    [bp-0x18],ax 

Listing 1: A C language code along with the corresponding machine and assembly code.
This big step is what enabled programmers to write statements like h=i+j+k+m without ever thinking of how this line is going to actually be executed in the machine.
Carrying on with motif of making programming languages even more natural, object oriented programming OOP exploited the fact that the most natural way a human being thinks about the world is thinking about it as being made up of objects with properties and behavior and the relations that govern how these objects interact. By enabling programmers to model the way of everyday thinking, the object oriented methodology avoids many of the problems inherent in purely procedural programming languages.

2) C struct’s as a stepping stone to OOP

In the world of C programming the closest one can get to object oriented programming is through using structs. Let us say that one wishes to write a program to compute the area of a rectangle. Let us compute the the area of three of them in our code. A purely procedural approach without using structs would look something like this: 
#include<stdio.h>

float area(float side1, float side2){return side1*side2;}

void main(){float r1s1=10,r1s2=20,r2s1=30,r2s2=100,r3s1=200,r3s2=300;
float temp;
temp=area(r1s1,r1s2);
temp=area(r2s1,r2s2);
temp=area(r3s1,r3s2);

In the above code we have to take special care not to mix up subscripts and every time we invoke the area function we would have to pass the two sides as parameters.
The following example illustrates the problem even more clearly. Let us write a code to compute the circumference of a heptagon. 

#include<stdio.h>

float circumference(float side1, float side2, float side3, float side4, float side5, float side6, float side7){return side1+side2+side3+side4+side5+side6+side7;}

void main(){
float p1s1=10,p1s2=20,p1s3=30,p1s4=15,p1s5=25,p1s6=16,p1s7=13;
float p2s1=5,p2s2=3,p2s3=5,p2s4=6,p2s5=7,p2s6=2,p2s7=3;
float p3s1=100,p3s2=130,p3s3=140,p3s4=70,p3s5=80,p3s6=50,p3s7=50;

float temp;
temp=circumference(p1s1,p1s2,p1s3,p1s4,p1s5,p1s6,p1s7);
temp=circumference(p2s1,p2s2,p2s3,p2s4,p2s5,p2s6,p2s7);
temp=circumference(p2s1,p2s2,p2s3,p2s4,p2s5,p2s6,p2s7);
}
 

 
A solution that uses structs avoids the problem of having to pay special attention to each passing parameter encapsulating all the information about our object, the heptagon, in one struct.
#include<stdio.h>

struct Heptagon{float side1;float side2;float side3;float side4;float side5;float side6;float side7;};

float circumference(struct Heptagon p){return p.side1+p.side2+p.side3+p.side4+p.side5+p.side6+p.side7;}
/*struct here is passed by value to avoid introducing several new ideas in the same time*/

void main(){struct Heptagon p1,p2,p3;

p1.side1=10,p1.side2=20,p1.side3=30,p1.side4=15,p1.side5=25,p1.side6=16,p1.side7=13;
p2.side1=5,p2.side2=3,p2.side3=5,p2.side4=6,p2.side5=7,p2.side6=2,p2.side7=3;
p3.side1=100,p3.side2=130,p3.side3=140,p3.side4=70,p3.side5=80,p3.side6=50,p3.side7=50;

float temp;
temp=circumference(p1);
temp=circumference(p2);
temp=circumference(p3);

Another advantage of struct’s is that of abstracting from the details of that which it encapsulates. What this means is that after preparing the object we can forget about its details. Let’s say we have a pyramid with rectangular base. We want write a code to compute its volume as well as the area of its base. We will first implement a solution without structs. A pyramid of a rectangular base can be determined by the side lengths of the base rectangle and its height from the base. Let us assume that we want to be able to calculate both its volume and the base area. A solution that does not employ structs looks like this:
#include<stdio.h>

float volume(float height, float baseside1, float baseside2){return height*baseside1*baseside2/3;}
float basearea(float baseside1, float baseside2){return baseside1*baseside2;}

void main(){float h=7,bs1=4,bs2=5;
float temp;
temp=volume(h,bs1,bs2);
temp=basearea(bs1,bs2);

The shortcoming of this approach is that there is no check on whether the parameters passed really represent the correct entities. For example, basearea can be mistakenly called like this basearea(bs1,h) leading to disastrous consequences and a debug nightmare.
A better approach is to employ structs let each function pick out the right set of struct variables from the struct. This way, we are protected from mistakes of the kind mentioned above. 

#include<stdio.h>

struct Rectpyramid{float height; float baseside1; float baseside2;};

float volume(struct Rectpyramid pyr){return pyr.hight*pyr.baseside1*pyr.baseside2/3;}
float basearea(struct Rectpyramid pyr){return pyr.baseside1*pyr.baseside2;}

void main(){struct Rectpyramid pyr1;
pyr1.height=7; pyr1.baseside1=4; pyr1.baseside2=5;
float temp;
temp=volume(pyr1);
temp=basearea(pyr1);
}