2. 2
Πως λύνουμε ένα πρόβλημα ;
Σχεδιασμός
Αλγορίθμου
Κώδικας
(υλοποίηση)
Profit !
3. 3
Test Driven Development
●
Σπάω το πρόβλημα σε
μικρότερα και απλούστερα
●
Βεβαιώνομε πως κάθε τι
είναι σωστό
●
Ο αλγόριθμος προκύπτει
●
Προκύπτει κώδικας
εύκολος στην κατανόηση
και την αλλαγή
4. 4
Μια λύση της Εργασίας
●
Με μοντέρνα C
– Χρησιμοποιώντας πράγματα που ίσως δεν ξέρετε
– Άρα δεν είναι αυτό που θα έπρεπε να παραδώσετε
●
Με χρήση μικρών συναρτήσεων
●
Μια καλή επανάληψη
5. 5
Ο λογικός τύπος bool
// Old C
typedef int bool;
#define true 1
#define false 0
// C99
#include <stdbool.h>
6. 6
Μεταβλητές και δείκτες
Όνομα Τύπος Μέγεθος Διεύθυνση
Μνήμης
Τιμή
var int sizeof(int)
4
0x7FF..02C 42
pVar int * sizeof(int*)
8
0X7FF..030 0x7FF..02C
int main() {
int var = 42;
int *pVar = &var;
assert(*pVar == 42);
}
Symbol Table
● Η τιμή μιας μεταβλητής: var, pVar
● Η διεύθυνση μνήμης : &var, &pVar
● Η τιμή, που δείχνει ένας δείκτης: *pVar
● Όλοι οι δείκτες πιάνουν το ίδιο χώρο στη μνήμη
sizeof(int *) == sizeof(void *)
Ένας δείκτης σε δείκτη που
δείχνει στην μνήμη της var
όπου ή var έχει την τιμή 42
H εικόνα φτιάχτηκε με την βοήθεια του ddd (GNU data display debugger)
7. 7
Η αναπαράσταση ενός πίνακα
●
Ένας πίνακας έχει
– Ένα μέγιστο μήκος Ν
– Ένα μέγεθος
– Τιμές
●
Αν ξέραμε δομές
#define TRUE 1
#define FALSE 0
const int N = 10;
int main() {
int a[N];
int aSize = 0;
for(int i=0; i<aSize; i++) a[i]=-99;
fillArray_partial(a, &aSize);
assert(aSize <= N);
printf("%4s", "A:");
printArray(a, aSize);
printf(" (size=%d)n", aSize);
return 0;
}
struct partialArray {
int *arr;
int size;
int maxSize;
};
8. 8
Ο Πίνακας στην μνήμη
●
Σύνολο στοιχείων: 10 / 20, size == 10
●
Βασική συνθήκη (αναλλοίωτο): (size <= maxSize) && (size >= 0) size <= maxSize) && (size <= maxSize) && (size >= 0) size >= 0)
●
Πρώτη τιμή : array[0]
●
Τελευταία τιμή : array[size-1]
●
Δεν “υπάρχει” το : array[size] !
●
Συνολικό μέγεθος σε bytes: maxSize * sizeof(size <= maxSize) && (size >= 0) int)
876543210 9
maxSize = 20
size
Κάπου στην στοίβα ο πίνακας int array[maxSize]
assert(a == &a[0]);
a[4] = 42;
assert(a[4] == *(a + 4));
assert(&a[4] == a+4);
*(a+4) = 24;
assert(a[4]==24);
sizeof(int)
1 2 4 5 42 9 10 12 13 22 ?? ??
To index 4, η τιμή 42
Η διεύθυνση μνήμης
(size <= maxSize) && (size >= 0) a+4) ή &a[4]
9. 9
Μια πρώτη συνάρτηση
●
Μικρές συναρτήσεις κάνουν τον κώδικα μας καλύτερο
int getRandom() {
const int maxValue = 12;
int random_value = rand() % (maxValue + 1);
return random_value;
}
Τύπος εξόδου
ή void
Κενή είσοδος
Τιμή Επιστροφής
10. 10
Μια συνάρτηση με είσοδο
●
Πριν η C χρησιμοποιήσει μια συνάρτηση θα πρέπει να ξέρει την “υπογραφή” της σε μια δήλωση (declaration).
– Το όνομα της
– Τον τύπο δεδομένων των εισόδων της (αν υπάρχουν)
– Τον τύπο δεδομένων της μιας μοναδικής εξόδου ή τίποτα (void)
●
Ο κώδικας της συνάρτησης υπάρχει στον ορισμό της (definition).
●
Τα Header Files περιέχουν τα declarations των συναρτήσεων που χρησιμοποιούμε.
– Τα ενσωματώνουμε με την εντολή #include include του προ-επεξεργαστή
#include <stdlib.h>
int getRandomMax(int maxValue);
int getRandomMax(int maxValue) {
maxValue++;
int random_value = rand() % maxValue ;
return random_value;
}
Declaration
Definition
Είσοδος με τιμή
Θα περάσει αντίγραφο της
τιμής στην στοίβα.
Τοπική αλλαγή !
11. 11
Τυχαίοι αριθμοί (κλειστό διάστημα)
#include <stdlib.h>
int getRandom() {
// Values at closed [min,max]
const int minValue = 10;
const int maxValue = 19;
// range = 10
int range = maxValue - minValue + 1;
// From 0 ... 9
int random_value = rand() % range ;
int result = random_value + minValue;
return result;
}
12. 12
Πίνακες, δείκτες και συναρτήσεις
●
Στενή σχέση μεταξύ πίνακα και δείκτη
●
Κατάρρευση τύπου από πίνακα σε δείκτη.
●
Οι πίνακες περνάνε σαν αναφορά και όχι σαν
αντίγραφο των στοιχείων τους.
– Μικρή χρήση μνήμης
– Αλλά μπορεί ο πίνακας να αλλάξει
●
Ακόμα και αν δεν θέλουμε
– H C99 υποστηρίζει const
void printArray(int const* arr, int size)
●
Ο τύπος που δείχνει ένας δείκτης είναι
σημαντικός για την αριθμητική των δεικτών.
●
Δεν ξέρω πόσο μεγάλος είναι ο πίνακας!
– Περνάω το size σαν τιμή ή δείκτη (αναφορά).
void printArray(int* arr, int size) {
for (int pos = 0; pos < size; pos++) {
char* comma = ( (pos == (size – 1) ) ? "" : ",");
printf("%2d%s", arr[pos], comma);
}
}
Πέρασμα πίνακα σαν
διεύθυνση μνήμης
(δείκτης)
Χρήση δείκτη
σαν να είναι
πίνακας
Τριαδικός τελεστής
Περιττές παρενθέσεις ;
14. 14
Περιττές παρενθέσεις;
comma = pos == size - 1 ? "" : ", ";
●
Θα κάνει αυτό που πρέπει να κάνει
Εκτός βέβαια αν δεν το κάνει
●
Βάζουμε παρενθέσεις
●
Για τεκμηρίωση
●
Για σιγουριά
●
Αλλά συνήθως θα κάνει αυτό που πρέπει να κάνει
●
C idioms από βιβλία
15. 15
Κόλπο: Διαβάζοντας δηλώσεις
Από τα δεξιά προς τα αριστερά!
const int *pci;
Είναι ακριβώς το ίδιο
const int *p;
const int* p;
int const *p;
int * const p;
The p is a constant pointer to an int
17. 17
Πέρασμα τιμής με αναφορά
●
Οι μικρές συναρτήσεις κάνουν τον αλγόριθμο ευανάγνωστο
void fillArray_partial(int* a, int* aLoc) {
for (int pos = 0; pos < N; pos++) {
int newValue, isValueExist;
newValue = getRandom();
isValueExist = findInArray(a, *aLoc, newValue);
if (isValueExist == FALSE) {
insertValue(a, aLoc, newValue);
}
}
}
fillArray_partial(a, &aSize);
Δίνω την διεύθυνση μνήμης μου
Η τιμή μου μπορεί να αλλάξει
Η τιμή (*aLoc) που έχει
η διεύθυνση μνήμης
που δείχνει o δείκτης aLoc
Χρήση
ᅠΠέρασμα σαν διεύθυνση μνήμης
ώστε η τιμή να μπορεί να αλλάξει
18. 18
Πέρασμα τιμών σε συναρτήσεις
●
Σαν τιμή
– Θα δημιουργηθεί ένα αντίγραφο της τιμής στην στοίβα
– Δεν βολεύει όταν τα δεδομένα είναι μεγάλα σε χώρο μνήμης
●
Σαν αναφορά μνήμης
– Θα περάσει η διεύθυνση της μνήμης σαν ένας δείκτης (*var)
– Οι δείκτης αυτός μπορεί να είναι κενός (size <= maxSize) && (size >= 0) NULL)
– Η συνάρτηση μπορεί να αλλάξει την τιμή (πχ scanf)
– Εκτός αν ο δείκτης δηλωθεί σαν const (C99)
– Στην C++ μια τιμή μπορεί να περάσει και σαν reference (&var)
●
Όταν λέμε πέρασμα με αναφορά εννοούμε είτε pointers είτε αυτό
●
Οι περισσότερες γνωστές γλώσσες προγραμματισμού περνάνε τιμές μόνο με αναφορά
19. 19
Βρόχοι
void fillArray_full(int* a, int* aLoc) {
for (int pos = 0; pos < N; pos++) {
int newValue, isValueExist;
// Get unique Value
do {
newValue = getRandom();
isValueExist = findInArray(a, *aLoc, newValue);
} while (isValueExist == TRUE);
insertValue(a, aLoc, newValue);
}
}
Σπάζοντας τον κώδικα σε συναρτήσεις οι
αλλαγές είναι εύκολες
Τα ονόματα των μεταβλητών λένε ιστορίες
Θα τρέξω
τουλάχιστον
μια φορά
21. 21
Συναρτήσεις: Επιστρέφοντας τιμές
// Find if a value exist on Array
int findInArray(const int a[], int size, int value) {
int found = FALSE;
for (int sPos = 0; sPos < size; sPos++) {
if (a[sPos] == value) {
found = TRUE;
break;
}
}
return found;
}
for (int sPos = 0; sPos < size; sPos++) {
if (a[sPos] == value) {
return true;
}
}
return false;
Εναλλακτική υλοποίηση
Πολλαπλά σημεία εξόδου
22. 22
Ας διαχωρίσουμε αρμοδιότητες
●
Μια συνάρτηση πρέπει να
κάνει ένα πράγμα μόνο
●
Μια συνάρτηση να παράγει
πολλαπλές τιμές
●
Μια να τις εισάγει στον πίνακα.
●
Θέλω η insert να
– Βάζει την τιμή στην σωστή θέση
– Αν η τιμή δεν υπάρχει
– Η πιθανά κάποιο άλλο κριτήριο
που μπορεί να προκύψει στο
μέλλον
void fillArray_simple(int* a, int* aLoc) {
for (int pos = 0; pos < N; pos++) {
int newValue = getRandom();
insertValue(a, aLoc, newValue);
}
}
void insertValue(int* a, int* aLoc, int value) {
a[*aLoc] = value;
*aLoc = *aLoc + 1;
}
fillArray_simple(a, &aSize);
printArray(a, aSize);
printf(" (size=%d)n", aSize);
23. 23
Μια πρώτη προσπάθεια
Τα assert σώζουν ζωές !
void insertValue(int* a, int* aLoc, int value) {
assert(*aLoc < N);
if( findInArray(a, *aLoc, value) == FALSE) {
a[*aLoc] = value;
*aLoc = *aLoc + 1;
}
assert(*aLoc <= N);
}
Η θέση είναι λάθος
Αρκεί να μπει στην
σωστή θέση
Προϋπόθεση συμβολαίου (pre condition)
Αναλλοίωτο
Post Condition
24. 24
Και τέλος η insertValue
void insertValue(int value, int* array, int* size) {
// assert(*size < N);
bool exists = isValueExist(value, array, *size);
if (exists == false) {
int pos = findPosInArray(value, array, *size);
if (pos != *size) {
// Make space
for(int i=*size; i>pos; i--) {
array[i]=array[i-1];
}
}
array[pos]=value;
*size = *size + 1;
}
// assert(*size <= N);
}
25. 25
Και τέλος η insertValue
void insertValue(int value, int* array, int* size) {
// assert(*size < N);
bool exists = isValueExist(value, array, *size);
if (exists == false) {
int pos = findPosInArray(value, array, *size);
if (pos != *size) {
// Make space
for(int i=*size; i>pos; i--) {
array[i]=array[i-1];
}
}
array[pos]=value;
*size = *size + 1;
}
// assert(*size <= N);
}
Έχει νόημα να γίνει
μια χωριστή συνάρτηση
arrayShiftRightAt(..)
Πέρασμα του πίνακα 2 φορές;
26. 26
Παίρνοντας και την θέση εισαγωγής
●
Προσοχή στις τιμές
των φρουρών
●
Υπάρχουν πολλοί
άλλοι τρόποι να
δηλώσεις αποτυχία
#include <limits.h>
const int guard1 = -9999999;
const int guard = INT_MIN;
// Return the position of a value in an array
int findPosInArray(int value, const int* a, int size) {
for (int sPos = 0; sPos < size; sPos++) {
if (a[sPos] == value) {
return sPos;
}
}
return guard;
}
Άσκηση για το σπίτι
Υλοποιήστε αυτή την λύση κάνοντας
τις απαραίτητες τροποποιήσεις
27. 27
Ο αλγόριθμος βήμα βήμα
Insert value: 13 at position 0, that is the end of array
-> 13 (size=1)
Insert value: 16 at position 1, that is the end of array
-> 13, 16 (size=2)
Insert value: 17 at position 2, that is the end of array
-> 13, 16, 17 (size=3)
Insert value: 15 at position 1, making space
-> 13, 15, 16, 17 (size=4)
Insert value: 13, value already exists skipping.
Insert value: 15, value already exists skipping.
Insert value: 16, value already exists skipping.
Insert value: 12 at position 0, making space
-> 12, 13, 15, 16, 17 (size=5)
Insert value: 19 at position 5, that is the end of array
-> 12, 13, 15, 16, 17, 19 (size=6)
Insert value: 11 at position 0, making space
-> 11, 12, 13, 15, 16, 17, 19 (size=7)
A: 11, 12, 13, 15, 16, 17, 19 (size=7)
13
16
17
13
16
17
17
13
16
16
17
13
15
16
17
115
size=3
size=4
28. 28
Συνδέοντας τους πίνακες
●
Έκπληξη!! το merge έχει ήδη φτιαχτεί !!!
int a[N], b[N], c[N+N];
int aSize = 0, bSize=0, cSize=0;
fillArray(a, &aSize); fillArray(b, &bSize);
for(int i=0; i<aSize; i++) {
insertValue(a[i], c, &cSize);
}
for(int j=0; j<bSize; j++) {
insertValue(b[j], c, &cSize);
}
31. 31
Μια “καλή” συνάρτηση
●
Χωράει σε ένα slide
●
Έχει ένα καλό περιγραφικό όνομα (δύσκολο)
●
Κρατάει τα αναλλοίωτα
●
Κάνει ένα μόνο πράγμα (και το κάνει σωστά)
●
Δεν έχει παρενέργειες (side effects)
– Ίδιες τιμές εισόδου δίνουν την ίδια τιμή εξόδου
– Δεν χρησιμοποιεί global μεταβλητές
●
Είναι εύκολο να ελεγχθεί μόνη της
●
Γενική: Μπορεί να χρησιμοποιηθεί σε ένα άλλο πρόγραμμα
rand() ???
34. 34
bool isValueExist(int value, const int *a, int size)
bool isValueExist(int value, const int a[], int size) {
for (int sPos = 0; sPos < size; sPos++) {
if (a[sPos] == value) {
return true;
}
}
return false;
}
35. 35
int findPosInArray(int value, const int array[], int size);
// Return the position of a value in an array
int findPosInArray(int value, const int array[], int size) {
int position;
for (position = 0; position < size; position++) {
if (array[position] >= value) {
break;
}
}
return position;
}
Βρες το
λάθος
36. 36
void printRevArray(int const* arr, int size);
// Print array in reverse order
void printRevArray(int const* arr, int size) {
for (int pos = size - 1; pos >= 0; pos--) {
char* comma = pos == 0 ? "" : ", ";
printf("%2d%s", arr[pos], comma);
}
}