You are on page 1of 24

Ελληνικό Μεσογειακό Πανεπιστήμιο

Σχολή Μηχανικών
Τμήμα Ηλεκτρονικών Μηχανικών
ΠΜΣ: Ηλεκτρονικά Συστήματα Τηλεπικοινωνιών & Αυτοματισμών

Τεχνικές Προγραμματισμού
και Αλγόριθμοι
(ΠΜΣ 03 )
Δρ. Μηχ. Νικόλαος Πετράκης,
Λέκτορας
nik.s.petrakis@hmu.gr
Διάλεξη 3ης εβδομάδας.

Ιστοσελίδα Μαθήματος:
https://eclass.chania.teicrete.gr/courses/EL342/

Εξάμηνο: Εαρινό 2019-20


Κατανομή μνήμης
Η μνήμη που καταλαμβάνει ένα πρόγραμμα
διαχωρίζεται σε διάφορα μέρη. RAM

• Το τμήμα που φορτώνεται ο κώδικας του stack

προγράμματος (text segment)


• Το τμήμα που βρίσκονται οι στατικές dynamic
μεταβλητές data
(heap)
• Την στοίβα (stack) όπου αποθηκεύονται όλες
static
οι τοπικές μεταβλητές, οι παράμετροι των data
συναρτήσεων, προσωρινές μεταβλητές, κ.τ.λ.
text
• Τον σωρό (heap) όπου είναι μνήμη που
μπορούμε να την δεσμεύσουμε ή να την reserved

ελευθερώσουμε κατά την διάρκεια της


εκτέλεσης του προγράμματος.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 2
Δυναμική διαχείριση μνήμης
Την διαχείριση μνήμης την αναλαμβάνει το
λειτουργικό σύστημα. Η C μας παρέχει διάφορες
συναρτήσεις που είναι υπεύθυνες ώστε να μας
παραχωρήσει το λειτουργικό σύστημα μνήμη.
Η μέγιστη μνήμη που μπορεί να λάβει ένα πρόγραμμα
σε κάποια χρονική στιγμή έχει να κάνει με:
• την συνολική μνήμη του συστήματος,
• πόση μνήμη είναι διαθέσιμη για προγράμματα
χρηστών,
• πόσα άλλα προγράμματα τρέχουν ταυτόχρονα και
πόση μνήμη έχουν δεσμεύσει.

08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 3


Η συνάρτηση sizeof()
Για να ζητήσουμε μνήμη από το σύστημα πρέπει να γνωρίζουμε
πόση μνήμη χρειαζόμαστε. Η C μας παρέχει τον τελεστή sizeof()
ώστε να μπορούμε να μάθουμε πόση μνήμη σε bytes
καταναλώνουν οι διάφοροι τύποι (απλοί ή σύνθετοι) στην C, π.χ.:
typedef struct {
int ar_mitrou ;
char onoma[30] ;
} StudentType ;
Αν το εκτελέσουμε θα δούμε τα παρακάτω:
Ο τύπος StudendType απαιτεί 36 bytes.
main ( ) Press any key to continue . . .
{
printf ("Ο τύπος StudendType \
απαιτεί %d bytes.\n", sizeof (StudentType ) ) ;
}
Προσέξτε ότι το μέγεθος της δομής δεν είναι το αναμενόμενο. Συχνά ο
μεταγλωττιστής προσθέτει επιπλέον bytes για λόγους στοίχισης της μνήμης.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 4
Η συνάρτηση malloc()
Η συνάρτηση malloc() είναι ορισμένη στο αρχείο stdlib.h και είναι
υπεύθυνη για την δέσμευση μνήμης στον σωρό (heap), παίρνει ως
παράμετρο το μέγεθος μνήμης που θέλουμε να δεσμεύσουμε σε
ψηφιολέξεις (bytes). Το πρωτότυπο της συνάρτησης είναι:
void* malloc(size_t size)
• Σε περίπτωση επιτυχίας επιστρέφει έναν δείκτη διεύθυνσης που
δείχνει στην αρχή της μνήμης που δεσμεύτηκε. Επειδή η
malloc() επιστρέφει έναν δείκτη σε τύπο void πρέπει να τον
μετατρέψουμε (casting) στον κατάλληλο τύπο, π.χ. για double:
double *x;
x = (double *) malloc(sizeof(double));
• ενώ εάν δεν έγινε η δέσμευση (π.χ δεν υπάρχει τόση ελεύθερη
μνήμη διαθέσιμη στο σύστημα) επιστρέφει NULL.
if (x==NULL) // ή αλλιώς if (!x)
printf("Not enough memory!\n");
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 5
Η συνάρτηση free()
Ο προγραμματιστής είναι υπεύθυνος να ελευθερώσει την μνήμη
που δέσμευσε νωρίτερα όταν δεν την χρειάζεται πια. Αυτό μπορεί
να το κάνει με την χρήση της συνάρτησης free() η οποία
ελευθερώνει την μνήμη που δείχνει ο δείκτης ptr και έχει το εξής
πρωτότυπο:
void free(void *ptr);
Για κάθε κλήση της malloc() πρέπει να υπάρχει και μία κλήση της
free(). Συνεπώς, για την αποδέσμευση της μνήμης που δεσμεύσαμε
στην προηγούμενη διαφάνεια για τον double θα γράφαμε:
free(x);
Σημείωση: Απαιτείται μεγάλη προσοχή στην δέσμευση/αποδέσμευση μνήμης διότι
εάν ξεχάσουμε να ελευθερώσουμε την μνήμη που δεσμεύσαμε αυτή θα παραμείνει
δεσμευμένη (και άχρηστη). Αυτό το φαινόμενο είναι γνωστό ως διαρροή μνήμης
(memory leak) και είναι πηγή πολλών προβλημάτων προγραμματισμού, τα οποία
εντοπίζονται δύσκολα και οδηγούν συνήθως σε πρόωρο τερματισμό.

08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 6


π.χ.: Δημιουργία δυναμικού πίνακα
Μπορούμε να δημιουργήσουμε ένα δυναμικό πίνακα ζητώντας
από την malloc() ένα πολλαπλάσιο σε bytes του τύπου που
θέλουμε.
main ( )
{ int n, i;
float *pin;
printf("Δώστε το μέγεθος του πίνακα : ");
scanf("%d", &n);
pin = (float*)malloc(n * sizeof(float));
if (!pin)
{printf("Not enough memory!"); exit(1);}
for(i=0; i<n; i++)
pin[i] = 100.0f/(1+i*i);
for(i=0; i<n; i++)
printf("%.3f ", pin[i]);
free (pin);
return 0;
}
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 7
Δείκτης σε δείκτη
Όταν γράφουμε:
float *p;
δηλώνουμε ένα δείκτη διεύθυνσης (pointer) με όνομα p που
δείχνει σε πραγματικό αριθμό απλής ακρίβειας (float).
Αντίστοιχα, για να δηλώσουμε ένα δείκτη με όνομα p που να
δείχνει σε τύπο δείκτη σε πραγματικό (float) γράφουμε:
float **p;
Για να φτιάξουμε δυναμικά έναν πίνακα με δείκτες χρειαζόμαστε
ένα δείκτη που να δείχνει σε τύπο δείκτη.
float **ptrpin = (float**)malloc(15 * sizeof(float*));

Δηλαδή, ζητάμε από την malloc() να μας επιστρέψει ένα πίνακα


με μέγεθος 15 επί το μέγεθος που απαιτεί ένας δείκτης σε
πραγματικό αριθμό (float). Κάθε στοιχείο του πίνακα είναι ένας
δείκτης διεύθυνσης σε float.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 8
Δυναμικός διδιάστατος πίνακας
Για να φτιάξουμε ένα δυναμικό διδιάστατο πίνακα φτιάχνουμε
πρώτα ένα πίνακα με δείκτες. Αυτός ο πίνακας αντιστοιχεί στις
γραμμές του διδιάστατου πίνακα.
int **a = (int**) malloc(100 * sizeof(int *));

Στην συνέχεια φτιάχνουμε ένα πίνακα για κάθε γραμμή.


for (i=0; i<100; i++)
a[i] = (int *) malloc (50 * sizeof(int));

Γράφοντας a[i] μας επιστρέφεται ένας δείκτης.


Γράφοντας a[i][j] μας επιστρέφεται ο ακέραιος που αντιστοιχεί
στην θέση i, j του πίνακα.
Ο μεταγλωττιστής μετατρέπει το a[i] σε *(a+i). Οπότε η έκφραση
a[i][j] μετατρέπεται σε *(*( a+i)+j).
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 9
Δημιουργία και καταστροφή
διδιάστατου πίνακα.
#include <stdlib.h>
main()
{ int i, **a;
a = (int **) malloc(100 * sizeof(int *));
for (i=0; i<100; i++)
a[i]=(int*) malloc(50 * sizeof(int));

Εδώ μπορεί να χρησιμοποιηθεί ο πίνακας με τις 100 γραμμές και τις 50


στήλες. Το στοιχείο a[i][j] βρίσκεται στην i γραμμή και στην j στήλη.

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


free(a[i]);
free(a);
return 0;
}
Όπως παρατηρείτε η μνήμη αποδεσμεύεται με την αντίστροφη σειρά.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 10
Συναρτήσεις με μεταβλητό
πλήθος ορισμάτων
Είναι εφικτό να δημιουργήσουμε συναρτήσεις με
απροσδιόριστο αριθμό παραμέτρων, όπως ακριβώς
συμβαίνει με την printf() η οποία έχει πρωτότυπο:
int printf(const char *format, … );
όπου τα αποσιωπητικά «…» (ellipsis) δείχνουν ότι η
συνάρτηση δέχεται μεταβλητό αριθμό από ορίσματα
οποιουδήποτε τύπου δεδομένων.
Η δυνατότητα αυτή μας παρέχεται από τους ορισμούς
και τις μακροεντολές (macros) που υπάρχουν stdarg.h
Σημείωση: Να θυμάστε ότι τα αποσιωπητικά μπαίνουν
πάντα στο τέλος της λίστας των παραμέτρων.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 11
Τα εργαλεία που διαθέτουμε
Καταρχήν πρέπει να συμπεριλάβουμε στον κώδικά μας
τον Variable Argument Header με τον γνωστό τρόπο:
#include <stdarg.h>
Ο οποίος περιέχει:
Ένα τύπο για να διατηρήσει την απαιτούμενη
va_list πληροφορία από τις ακόλουθες μακροεντολές.
Πρέπει να δηλωθεί αντικείμενο αυτού του τύπου

va_start
Μια μακροεντολή που αρχικοποιεί το αντικείμενο
που δηλώθηκε με την va_list

va_arg
Μια μακροεντολή που επεκτείνεται σε μια έκφραση
της τιμής και του τύπου του επόμενου ορίσματος
Μια μακροεντολή που διευκολύνει την κανονική
va_end επιστροφή από μια συνάρτηση που προσπελάστηκε
με την va_start
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 12
Παράδειγμα: Υπολογισμός μέσης τιμής με …
#include <stdio.h>
#include <stdlib.h>
#include <greek.h>
#include <stdarg.h> // Variable Argument Header
double average (int, ...); // ... ellipsis
int main(int argc, char *argv[])
{ double x=3.5, y=5.3, z= 13.7, v=17.548;
SetGreek();
printf("%s%8.3f\n%s%8.3f\n%s%8.3f\n%s%8.3f\n\n",
"x =",x,"y =",y,"z =",z,"v =",v);
printf("%s%8.3f\n%s%8.3f\n%s%8.3f\n%s%8.3f\n\n",
"(x)/1 =", average (1,x),
"(x+y)/2 =", average (2,x,y),
"(x+y+z)/3 =", average (3,x,y,z),
"(x+y+z+v)/4 =", average (4,x,y,z,v));
system("PAUSE");
return 0;
} 08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 13
… με απροσδιόριστο πλήθος ορισμάτων.
double average (int n, ...)
{ double total=0.0;
int i;
va_list ap;
va_start(ap,n);
for (i=1; i<=n; i++)
total = total + va_arg(ap, double);
va_end(ap); x = 3.500
return total/n; y = 5.300
} z = 13.700
v = 17.548
(x)/1 = 3.500
(x+y)/2 = 4.400
(x+y+z)/3 = 7.500
(x+y+z+v)/4 = 10.012
Press any key to continue . . .
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 14
Δομές Δεδομένων
Η στοίβα (stack)
Στοίβα είναι μια αριθμημένη
συλλογή από θέματα (αντικείμενα, E top

items) στην οποία μπορούν να D

εισαχθούν νέα καθώς και να C

διαγραφούν μόνο από το ένα άκρο Β

που ονομάζεται κορυφή (top) της Α bottom

στοίβας. STACK

Είναι μια αφηρημένη δομή δεδομένων της


οποίας το μέγεθος μεταβάλλεται δυναμικά.
Λέγεται και LIFO list (Last-In, First-Out list),
ή ακόμα και FILO (First-In, Last-Out).
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 15
Βασικές λειτουργίες με στοίβες (1)
Δύο είναι οι βασικές πράξεις push(s, i) και pop(s) όπου s – stack
και i – item.
Θεωρητικά, δεν υπάρχει ανώτερο όριο θεμάτων που μπορούν να
φυλαχτούν σε μια στοίβα.
Αν από μια στοίβα που περιέχει ένα μόνο θέμα και αποσυρθεί
τότε η στοίβα δεν θα περιέχει κανένα θέμα και ονομάζεται κενή
στοίβα.
Η πράξη της απόσυρσης δεν εφαρμόζεται στην κενή στοίβα. Έτσι
λοιπόν, πριν την εφαρμογή της απόσυρσης (pop) σε μια στοίβα
πρέπει να εξασφαλιστεί ότι η στοίβα δεν είναι κενή.
Η πράξη empty(s) καθορίζει αν η στοίβα s είναι κενή ή όχι,
επιστρέφοντας την τιμή FALSE ή TRUE αντίστοιχα.

08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 16


Βασικές λειτουργίες με στοίβες (2)
Μια άλλη πράξη που μπορεί να πραγματοποιηθεί σε μια στοίβα
είναι ο καθορισμός του κορυφαίου θέματος της στοίβας χωρίς την
εξαγωγή του. Αυτή η πράξη γράφεται stacktop(s) και επιστρέφει
το στοιχείο που βρίσκεται στην κορυφή της στοίβας s.
Η πράξη stacktop(s) θα μπορούσε να διασπαστεί σε μια απόσυρση
και μια ώθηση στην στοίβα, δηλ.:
i = stacktop(s); ισοδύναμο με i = pop(s);
push(s,i);

Όπως η απόσυρση έτσι και η stacktop δεν ορίζεται για μια κενή
στοίβα. Το αποτέλεσμα μιας παράνομης προσπάθειας για
απόσυρση ή ανάγνωση στοιχείου από κενή στοίβα ονομάζεται
underflow (υποχείλιση ή ανεπάρκεια) η οποία όμως μπορεί να
αποφευχθεί ελέγχοντας με την εντολή empty(s).
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 17
Υλοποίηση στοίβας
Υπάρχουν πολλοί τρόποι αναπαράστασης ή υλοποίησης μιας
στοίβας.
• Ο ευκολότερος είναι με την βοήθεια ενός πίνακα.
• Παρότι ένας πίνακας δεν μπορεί να γίνει στοίβα μπορεί να
φιλοξενήσει μια στοίβα.
• Ο πίνακας πρέπει να δηλωθεί αρκετά μεγάλος ώστε να
υπερκαλύπτει το μέγιστο αναμενόμενο μέγεθος της στοίβας.
• Στην διάρκεια της εκτέλεσης του προγράμματος η στοίβα
μπορεί να αυξάνει ή να συρρικνώνεται στον χώρο που της
έχει αφιερωθεί. Το ένα άκρο του πίνακα είναι η σταθερή
βάση της στοίβας (πυθμένας) ενώ η κορυφή μετακινείται
συνεχώς καθώς στοιχεία ωθούνται ή αποσύρονται.
• Είναι προφανής η ανάγκη ύπαρξης ενός άλλου πεδίου το
οποίο θα διατηρεί την τρέχουσα θέση της κορυφής της
στοίβας.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 18
Υλοποίηση στοίβας (2)
Σύμφωνα με τα παραπάνω λοιπόν μια στοίβα μπορεί να δηλωθεί
σαν μια δομή με δυο μέλη: έναν πίνακα για να διατηρεί τα
στοιχεία της στοίβας και έναν ακέραιο για να δεικνύει την
τρέχουσα θέση της κορυφής της στοίβας μέσα στον πίνακα. Για
μια στοίβα ακεραίων θα είχαμε τις εξής δηλώσεις:
#define STACKSIZE 100
struct stack {
int top;
int items[STACKSIZE];
};
struct stack s;
Φυσικά δεν υπάρχει λόγος να περιοριστεί μια στοίβα στο να
περιέχει μόνο ακέραιους αριθμούς. Θα μπορούσε το ίδιο εύκολα
να είχε δηλωθεί για στοιχεία τύπου πραγματικού (float) ή
χαρακτήρα (char) ή ό,τι άλλο τύπο επιθυμούμε να δώσουμε στα
στοιχεία της στοίβας. Για παράδειγμα:
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 19
Υλοποίηση στοίβας (3)
#define STACKSIZE 100
#define INTGR 1 Αρκετές φορές εμφανίζεται η ανάγκη μια
#define FLT 2 στοίβα να περιέχει αντικείμενα διαφορετικού
#define STRING 3 τύπου χρησιμοποιώντας ενώσεις (unions).
struct stackelement {
struct stack s; Οπότε, για την
int etype;
union { εμφάνιση του
struct stackelement se; κορυφαίου στοιχείου
int ival;
float fval; της στοίβας θα
se =s.items[s.top]; είχαμε:
char sval[10];
switch (se.etype) {
} element;
case INTGR :printf("%d\n",se.ival); break;
};
case FLT :printf("%f\n",se.fval); break;
case STRING:printf("%s\n",se.sval); break;
struct stack{
}
int top;
struct stackelement items[STACKSIZE];
}; Στην συνέχεια, για λόγους απλότητας, θα θεωρήσουμε ότι η
στοίβα μας μπορεί να αποθηκεύσει στοιχεία ενός μόνο τύπου,
έτσι ώστε να μην είναι απαραίτητη η ένωση (union).
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 20
Υλοποίηση στοίβας (4)
Υλοποίηση της πράξης ελέγχου για κενή λίστα (empty):
int empty(struct stack *ps ) struct stack s;
{ if (ps -> top == -1)
return(TRUE); if (empty(&s))
else /* empty stack */
return(FALSE); else
} /* not empty stack */
Υλοποίηση της πράξης απόσυρσης ή εξώθησης (pop) στοιχείου:
int pop(struct stack *ps )
{ if (empty(ps))
{ ptintf("stack underflow"); exit(1); }
return (ps->items[ps->top--]);
}
Υλοποίηση της πράξης ώθησης (push) στοιχείου:
void push(struct stack *ps, int x)
{ ps->top +=1; // ή πιο περιεκτικά
ps->items[ps->top]=x; // ps->items[++(ps->top)]=x;
return ;
} 08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 21
Υλοποίηση στοίβας (5)
Επειδή, στην πράξη η στοίβα είναι πεπερασμένη πρέπει να
συμπληρώσουμε την προηγούμενη συνάρτηση με μια εντολή
ελέγχου για την παγίδευση της περίπτωσης της υπερχείλισης
(overflow), ως εξής:
void push(struct stack *ps, int x)
{ if (ps -> top ==STACKSIZE-1)
{ printf("stack overflow"); exit(1); }
else
ps->items[++(ps->top)]=x;
return ;
}
Η τελευταία λειτουργία είναι η stacktop(s) η οποία εμφανίζει το
κορυφαίο στοιχείο της στοίβας χωρίς την εξαγωγή του:
int stacktop (struct stack *ps)
{ if (empty(ps))
{ printf("stack underflow"); exit(1); }
else
return (ps->items[ps->top]);
}08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 22
Παράδειγμα – Εφαρμογή (1)
Τώρα που έχουμε ορίσει μια στοίβα και τις πράξεις που μπορούν
να πραγματοποιηθούν σ’ αυτή έφτασε η στιγμή να δείξουμε πως
μπορούμε να χρησιμοποιήσουμε τη στοίβα στην επίλυση
προβλημάτων. Ένα από τα κλασικά παραδείγματα είναι η
εξακρίβωση του αν είναι σωστά φωλιασμένες οι παρενθέσεις σε
μια μαθηματική έκφραση με κάμποσα ζεύγη από φωλιασμένες
παρενθέσεις, όπως η παρακάτω:
5 + (( a * (( b - c ) / ( 3 + a )) – d ) / ( c – 3.14 + a ))
Για να γίνει αυτό πρέπει να ελεγχθεί ότι υπάρχει ίσος αριθμός
δεξιών και αριστερών παρενθέσεων και ότι πριν από κάθε δεξιά
παρένθεση έχει προηγηθεί μια αριστερή, δηλαδή το ζευγάρι της.
Το πρόβλημα θα μπορούσε να λυθεί με την χρήση ενός απλού
μετρητή (αρχικοποιημένου στο 0) ο οποίος θα αυξανόταν όποτε
συναντούσαμε αριστερή παρένθεση και θα μειωνόταν όποτε
συναντούσαμε δεξιά παρένθεση.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 23
Παράδειγμα – Εφαρμογή (2)
Μια μαθηματική έκφραση είναι έγκυρη από πλευράς
παρενθέσεων όταν μετά τον έλεγχο ο μετρητής έχει τιμή μηδέν,
ενώ κατά την διάρκεια του ελέγχου δεν έλαβε ποτέ αρνητική
τιμή.
Αν αλλάξουμε όμως λίγο το πρόβλημα υποθέτοντας ότι
υπάρχουν τρία διαφορετικά είδη διαχωριστικών παρενθέσεων
(π.χ. παρενθέσεις ( ) , αγκύλες [ ] και άγκιστρα { } ) θα πρέπει όχι
μόνο να παρακολουθούμε πόσες έχουν ανοίξει αλλά και το είδος
τους. Σ’ αυτήν τη περίπτωση η χρήση της στοίβας θα ήταν
περισσότερο αναγκαία.
Άσκηση 2 (για το σπίτι): Να γίνει πρόγραμμα που να δέχεται
μαθηματικές εκφράσεις από το πληκτρολόγιο με παρενθέσεις ( ),
αγκύλες [ ] και άγκιστρα { } και να επιστρέφει το κατάλληλο
μήνυμα ανάλογα με το αν είναι έγκυρες ή όχι. Η εισαγωγή της
έκφρασης θα τερματίζεται με CTRL + Z.
08/04/2020 "Τεχνικές Προγραμματισμού και Αλγόριθμοι", Ν. Πετράκης, ΕΛ.ΜΕ.ΠΑ. 24

You might also like