You are on page 1of 177

Προγραμματισμός Ι

Γλώσσα C

Πανεπιστήμιο Πελοποννήσου
Τμήμα Πληροφορικής & Τηλεπικοινωνιών

Νικόλαος Δ. Τσελίκας
Προγραμματισμός Ι

Power Shell σε Windows 10 (1/7)


1

Προγραμματισμός Ι
Power Shell σε Windows 10 (2/7)
 Στη συνέχεια κατεβάζουμε/εγκαθιστούμε από το Windows Store
κάποια LTS (long term support) έκδοση Ubuntu

Προσοχή!!!
6 Κατά την εκκίνηση θα σας
ζητηθεί να δώσετε ένα
username κι ένα password
(2 φορές, για επιβεβαίωση).
 ...και ξεκινάμε τα Ubuntu Μπορείτε να
πληκτρολογήσετε ό,τι
7 θέλετε, αλλά δεν θα πρέπει
να το ξεχάσετε ποτέ (να το
σημειώσετε κάπου)

Προγραμματισμός Ι

Power Shell σε Windows 10 (3/7)


 Αφού γίνει η εγκατάσταση, καλό θα ήταν να κάνετε ανανέωση και
αναβάθμιση του λειτουργικού συστήματος με τις εντολές
Σημαίνει “advanced packaging tool”

Σημαίνει “super user do” και


sudo apt update
στη συνέχεια ζητείται το sudo apt upgrade
password σας

Προγραμματισμός Ι
Power Shell σε Windows 10 (4/7)
 Εγκατάσταση του gcc compiler που θα χρησιμοποιούμε στο μάθημα

sudo apt install gcc

 Κι είμαστε έτοιμοι να γράψουμε το πρώτο μας πρόγραμμα ακόμα και


με χρήση κάποιου ενσωματωμένου editor (π.χ. nano)

Προγραμματισμός Ι

Power Shell σε Windows 10 (5/7)

 Κaι να το μεταγλωττίσουμε
Το όνομα του «εκτελέσιμου»
(συνήθως ίδιο με το όνομα του Το όνομα του αρχείου
αρχείου πηγαίου κώδικα χωρίς πηγαίου κώδικα
την κατάληξη)

Προγραμματισμός Ι
Δεν υπάρχουν συντακτικά λάθη!!
Power Shell σε Windows 10 (6/7)
 Ενώ αν είχαμε λάθη...

ALT+SHIFT+3
(εμφάνιση
γραμμών)

Προγραμματισμός Ι

Power Shell σε Windows 10 (7/7)

 Για να «τρέξουμε» το εκτελέσιμο:

./όνομα_εκτελέσιμου

 Δηλαδή, για το προηγούμενο παράδειγμα:

Προγραμματισμός Ι
Προγραμματισμός Ι
Εισαγωγή

Σύντομη Αναδρομή
 Αναδρομή
 Η γλώσσα προγραμματισμού C είναι μία γλώσσα υψηλού
επιπέδου
 Δημιουργήθηκε από τον Dennis Richie στις αρχές της
δεκαετίας του 1970 στα εργαστήρια Bell Labs της εταιρείας
AT&T
 Η γλώσσα ονομάστηκε C, γιατί πολλά από τα χαρακτηριστικά
της προήλθαν από μια παλαιότερη γλώσσα, η οποία είχε
αναπτυχθεί από τον Ken Thompson και ονομαζόταν “B”
 Γιατί δημιουργήθηκε η C ?
 Ο κύριος σκοπός της δημιουργίας της γλώσσας C ήταν η χρήση
της για την ανάπτυξη του λειτουργικού συστήματος UNIX

Προγραμματισμός Ι
Το πρότυπο ANSI (Ι)
 Το πρόβλημα:
 Η Γρήγορη και Άμεση Αποδοχή της C είχε σαν
αποτέλεσμα
 διάφοροι οργανισμοί και εταιρείες να δημιουργούν τις δικές
τους εκδόσεις της γλώσσας, οι οποίες όμως εμφάνιζαν
διαφορές μεταξύ τους
 η ύπαρξη πολλών εκδόσεων είχε σαν συνέπεια να
εμφανιστούν προβλήματα ασυμβατότητας, δηλαδή
προγράμματα τα οποία να μεταγλωττίζονται επιτυχώς με μία
έκδοση να μην μεταγλωττίζονται με κάποια άλλη
 Η λύση (1983):
 ANSI (American National Standard Institute)
 ορισμός επιστημονικής επιτροπής για την καθιέρωση ενός
προτύπου της C, το οποίο να καθορίζει πλήρως τους
κανόνες, τα χαρακτηριστικά και τη λειτουργικότητα της
γλώσσας

Προγραμματισμός Ι

Το πρότυπο ANSI (ΙΙ)


 ANSI C
 Το πρότυπο ολοκληρώθηκε το 1989 και ονομάζεται ANSI C
 Από τότε όλοι οι C μεταγλωττιστές υποστηρίζουν το
συγκεκριμένο πρότυπο
 C99
 Στο τέλος της δεκαετίας του ’90 το πρότυπο ANSI C
επανεξετάστηκε
 Οι αλλαγές που έγιναν οδήγησαν στη δημιουργία ενός νεότερου
προτύπου, γνωστού ως C99
 Οι περισσότεροι μεταγλωττιστές υποστηρίζουν το μεγαλύτερο
τμήμα του, αν όχι όλο
 C11, C17, C2x
 Πρόκειται για εμπλουτισμό του προτύπου C99, το 2011, το
2017 και μετά το 2020...
 Αποτέλεσμα:
 Το παλαιότερο πρότυπο (ANSI C) παραμένει το επικρατέστερο
μέχρι και σήμερα Προγραμματισμός Ι
Πλεονεκτήματα της γλώσσας C
 Πολύ γρήγορη
 Απλό συντακτικό και λίγες δεσμευμένες λέξεις
 Φορητότητα κώδικα (εκτελείται σε διαφορετικά
λειτουργικά συστήματα)
 Υποστηρίζει τον δομημένο προγραμματισμό και την
ύπαρξη και κλήση συναρτήσεων
 Υποστηρίζει μέχρι και την ενσωμάτωση Assembly
κώδικα
 Παρέχει έτοιμες συναρτήσεις
 Δημοφιλής γλώσσα (παρά την ηλικία της)
 Αποτελεί το πρώτο βήμα για την εκμάθηση κάποιας
αντικειμενοστρεφούς γλώσσας προγραμματισμού (π.χ.
Java, C++, C# κτλ)

Προγραμματισμός Ι

Μειονεκτήματα της γλώσσας C


 Έλλειψη περιορισμών
 Επομένως, ο προγραμματιστής πρέπει να είναι πολύ
προσεκτικός κατά τη συγγραφή του προγράμματος, γιατί μπορεί
εύκολα να εισάγει λάθη, τα οποία ο μεταγλωττιστής δεν θα
αντιληφθεί
 «Δύσκολη» (συντακτικά) γλώσσα
 Η C, αν και είναι μία μικρή γλώσσα (υπό την έννοια ότι
περιέχει λίγες λέξεις με ειδική σημασία), δεν είναι μία
«εύκολη» γλώσσα προγραμματισμού για να μάθει κάποιος
 Ένα C πρόγραμμα μπορεί να γραφεί με αρκετά πολύπλοκο
τρόπο ακόμα και αν αποτελείται από λίγες γραμμές κώδικα
 Δεν είναι αντικειμενοστρεφής γλώσσα
 Άρα δεν υποστηρίζει λειτουργίες, όπως η ενθυλάκωση, ο
πολυμορφισμός, η κληρονομικότητα κ.ά.

Προγραμματισμός Ι
Κύκλος δημιουργίας ενός προγράμματος C
Το πρόγραμμα γράφεται σε έναν
 Συγγραφή κώδικα (Edit) Editor Disk συντάκτη κειμένου και αποθηκεύεται
στον δίσκο με την κατάληξη .c

Ο μεταγλωττιστής μεταγλωττίζει το
 Μεταγλώττιση (Compile) Compiler Disk πρόγραμμα σε κώδικα γλώσσας
μηχανής, δημιουργεί και αποθηκεύει
το αποτέλεσμα στο δίσκο ως αρχείο
με κατάληξη .ο (ή .obj)

Ο συνδέτης (linker) συνδέει τον


 Σύνδεση (Link) Linker Disk
κώδικα που παράγεται από τον
μεταγλωττιστή .ο (Unix/Linux) ή
.obj (Windows) με τις βιβλιοθήκες,
με αποτέλεσμα τη δημιουργία του
 Φόρτωση (Load)
Primary Memory
εκτελέσιμου αρχείου (a.out ή .exe
Loader αντίστοιχα)
Το πρόγραμμα φορτώνεται
στη μνήμη
Disk ..
..
..

Primary Memory
 Εκτέλεση (Execute) CPU Η CPU παίρνει τις εντολές
του προγράμματος και τις
εκτελεί «σειριακά», πιθανώς
αποθηκεύοντας νέες τιμές
..
.. δεδομένων καθώς εκτελείται
..
το πρόγραμμα
Προγραμματισμός Ι

Το πρώτο πρόγραμμα

Προγραμματισμός Ι
Η οδηγία #include
 #include <stdio.h>

 Με την οδηγία #include <όνομα_αρχείου> ο


μεταγλωττιστής υποχρεώνεται να ενσωματώσει και
να συμπεριλάβει (include) τα περιεχόμενα του
αρχείου στον κώδικα του προγράμματος

 Το αρχείο stdio.h (standard input output)


περιέχει τις βασικές (standard) δηλώσεις των
συναρτήσεων με τις οποίες γίνεται εμφάνιση
δεδομένων στην οθόνη (output) και εισαγωγή
δεδομένων από το πληκτρολόγιο (input)

Προγραμματισμός Ι

Παρατηρήσεις
 Το αρχείο stdio.h συμπεριλαμβάνεται σχεδόν πάντα σε ένα C
πρόγραμμα, αφού περιέχει τις συναρτήσεις για εμφάνιση και
διάβασμα δεδομένων

 Συνήθως, τα αρχεία που συμπεριλαμβάνονται έχουν κατάληξη .h


και ονομάζονται αρχεία επικεφαλίδας (header files)

 Η οδηγία #include ξεκινάει πάντα με το σύμβολο # και ΔΕΝ


τελειώνει με ελληνικό ερωτηματικό (;)

 Ο προγραμματιστής μπορεί να δημιουργήσει ένα δικό του αρχείο


και να το συμπεριλάβει στο πρόγραμμά του με την οδηγία
#include

 Κατά τη μεταγλώττιση του προγράμματος, ο μεταγλωττιστής


ψάχνει να βρει σε ποιο σημείο του δίσκου είναι αποθηκευμένο το
αρχείο για να το ενσωματώσει στο πρόγραμμα
 Όταν το όνομα του αρχείου είναι γραμμένο μέσα σε < >, τότε ο
μεταγλωττιστής ψάχνει σε προεπιλεγμένους φακέλους
 Όταν το όνομα του αρχείου είναι γραμμένο μέσα σε διπλά εισαγωγικά (" "),
τότε ο μεταγλωττιστής ψάχνει πρώτα στον ίδιο φάκελο που βρίσκεται το
πρόγραμμα και μετά σε προεπιλεγμένους φακέλους
Προγραμματισμός Ι
Η συνάρτηση main()
 int main (void)
 Ένα πρόγραμμα γραμμένο στη γλώσσα C μπορεί –
όπως θα δούμε - να περιέχει αρκετές συναρτήσεις
 πρέπει όμως οπωσδήποτε να περιέχει τη συνάρτηση main(),
η οποία καλείται και «κύρια συνάρτηση του προγράμματος»
 Η εκτέλεση του προγράμματος ξεκινάει από την πρώτη
εντολή που υπάρχει μέσα στη συνάρτηση main()
 Η εκτέλεση του προγράμματος τελειώνει με την
τελευταία εντολή που υπάρχει μέσα στη συνάρτηση
main(), εκτός αν κληθεί νωρίτερα μία εντολή
εξόδου, όπως η return
 Οι εντολές της συνάρτησης main() ή αλλιώς το
«σώμα της συνάρτησης» πρέπει να περιέχονται μέσα
σε άγκιστρα {...}
Προγραμματισμός Ι

Η εντολή printf()
 printf("Ramones: Hey Ho, Let’s Go\n");
 Οι εντολές γράφονται συνήθως μία σε κάθε γραμμή και
σχεδόν πάντα τελειώνουν με το ελληνικό ερωτηματικό (;)
 Η εντολή που χρησιμοποιείται στο παραπάνω πρόγραμμα
είναι η printf()
 Η printf() δηλώνεται μέσα στο αρχείο stdio.h και γι’
αυτό το λόγο συμπεριλαμβάνουμε αυτό το αρχείο με την
οδηγία #include
 Η εντολή printf() χρησιμοποιείται για την εμφάνιση
δεδομένων στην οθόνη
 Ο χαρακτήρας '\n' μέσα στην εντολή printf()
δημιουργεί μία νέα γραμμή μετά την εμφάνιση του
μηνύματος στην οθόνη
 Ισοδυναμεί δηλ. με το πάτημα του πλήκτρου Enter
Προγραμματισμός Ι
Η εντολή return
 return 0;
 Στην απλούστερη μορφή της, η συνάρτηση main()
γράφεται όπως στο παράδειγμά μας, δηλαδή σαν
int main(void)
 Η ειδική λέξη int υποδηλώνει ότι η συνάρτηση main()
πρέπει να επιστρέψει έναν ακέραιο αριθμό (integer) στο
λειτουργικό σύστημα
 O ακέραιος αυτός αριθμός επιστρέφεται με την εντολή
return (που αποτελεί δεσμευμένη λέξη της C)
 Κατά σύμβαση, όταν ο αριθμός που επιστρέφεται είναι
το μηδέν (0), τότε σημαίνει ότι το πρόγραμμά μας
τερματίστηκε κανονικά

Προγραμματισμός Ι

Παρατηρήσεις (I)
 Ένα πρόγραμμα γραμμένο σε C πρέπει να περιέχει ΜΟΝΟ ΜΙΑ
συνάρτηση main()

 Μπορεί να συναντήσετε και άλλες δηλώσεις της main()


 π.χ.  ή π.χ.
void main(void) int main()
{ {
... ...
} }
 Προτείνεται ο τρόπος δήλωσης της main() όπως έγινε στο
παράδειγμα, διότι ακολουθεί το πρότυπο ANSI

 Δηλ.
int main(void)
{
...
}
Προγραμματισμός Ι
Παρατηρήσεις (II)
 Η δεσμευμένη λέξη void μέσα στις παρενθέσεις
δηλώνει ρητά ότι η συνάρτηση main() δεν δέχεται
κάποια ορίσματα

 Σε επόμενη διάλεξη, θα συναντήσουμε μία εναλλακτική


δήλωση της main(), όπου δέχεται παραμέτρους

Προγραμματισμός Ι

Εισαγωγή Σχολίων σε πρόγραμμα (Ι)


 Τι είναι τα «Σχόλια»???
 Είναι κείμενο το οποίο εισάγεται από τον προγραμματιστή στον
κώδικα προκειμένου να καταστήσει τον ίδιο τον κώδικα
περισσότερο ευανάγνωστο και σαφή

 Τα σχόλια εισάγονται είτε για επεξήγηση προς τρίτους (που


πιθανόν διαβάσουν τον κώδικά μας) είτε και για μας τους ίδιους
(ιδίως όταν πρόκειται να διαβάσουμε τον κώδικά μας μετά από
αρκετά μεγάλο χρονικό διάστημα)

 Περιέχεται μεταξύ /* και */ (και αγνοείται από τον υπολογιστή)

 Επίσης σχόλιο είναι και το κείμενο στην ίδια γραμμή μετά από //

Προγραμματισμός Ι
Εισαγωγή Σχολίων σε πρόγραμμα (ΙΙ)
 π.χ.

 π.χ.

Προγραμματισμός Ι

Εισαγωγή Σχολίων σε πρόγραμμα (ΙΙΙ)


 π.χ. το σχόλιο

 μπορεί να γραφεί ισοδύναμα ως:

Προγραμματισμός Ι
Παρατηρήσεις
 Η χρήση των πλάγιων  Αντίθετα, η χρήση των /* */ για
καθέτων // σαν ένδειξη εισαγωγή σχολίων υποστηρίζεται από
εισαγωγής σχολίων δεν είναι όλους τους C μεταγλωττιστές, αφού
σύμφωνη με το ANSI C υποστηρίζεται από το ANSI C πρότυπο
πρότυπο
Προτείνεται η εισαγωγή περιγραφικών
 Άρα, αν και υπάρχουν σχολίων σε πολύπλοκα σημεία του
μεταγλωττιστές που την κώδικα, ώστε ο κώδικας να γίνεται πιο
υποστηρίζουν, δεν είναι κατανοητός τόσο στον ίδιο τον
υποχρεωτικό να προγραμματιστή όσο και σε συνεργάτες
υποστηρίζεται από όλους τους του που ενδέχεται να πρέπει να
C μεταγλωττιστές κατανοήσουν τον κώδικά του

Προγραμματισμός Ι

Μεταγλώττιση Προγράμματος
 Όταν έχουμε γράψει τον κώδικα του προγράμματός μας π.χ.
αρχείο my_example.c, θα πρέπει αρχικά να τον
μεταγλωττίσουμε (να κάνουμε compilation) στη γλώσσα που
αντιλαμβάνεται ο υπολογιστής (γλώσσα μηχανής)

 Τη διαδικασία αυτή την αναλαμβάνει ο compiler (μεταγλωττιστής)

 Αν ο κώδικάς μας μεταγλωττιστεί επιτυχώς, τότε δημιουργείται


το αρχείο my_example.o (Unix/Linux) ή my_example.obj
(Windows), και είμαστε έτοιμοι να μεταβούμε στο επόμενο
στάδιο, δηλαδή στη σύνδεση (linking) του κώδικά μας με τις
βιβλιοθήκες της γλώσσας C

Προγραμματισμός Ι
Σύνδεση Προγράμματος (1/2)
 Η επόμενη διαδικασία πραγματοποιείται από το πρόγραμμα
σύνδεσης (linker), το οποίο συνδέει τον κώδικα αντικείμενου που
δημιουργήθηκε στη φάση μεταγλώττισης με πρόσθετο κώδικα που
χρειάζεται για τη δημιουργία του τελικού εκτελέσιμου

 Ο πρόσθετος κώδικας περιλαμβάνει τον κώδικα των συναρτήσεων


βιβλιοθήκης που χρησιμοποιεί το πρόγραμμα (π.χ. printf())

 Αυτός ο κώδικας περιέχεται σε αρχεία βιβλιοθήκης (library


files) που παρέχονται μαζί με τον μεταγλωττιστή
 Σημειώστε ότι τα αρχεία βιβλιοθήκης είναι διαφορετικά από τα
αρχεία επικεφαλίδας
 ΠΡΟΣΟΧΗ! Ο κώδικας των συναρτήσεων βρίσκεται στα αρχεία
βιβλιοθηκών και όχι σε αρχεία επικεφαλίδας

Προγραμματισμός Ι

Σύνδεση Προγράμματος (2/2)


 Για παράδειγμα, αν χρησιμοποιείτε τον gcc μεταγλωττιστή και το
πρόγραμμά σας χρησιμοποιεί μία μαθηματική συνάρτηση (π.χ.
sqrt()) μπορεί να χρειαστεί να φορτώσετε την αντίστοιχη
βιβλιοθήκη γράφοντας:

gcc test.c –lm

 Σε ένα ενοποιημένο περιβάλλον, συνήθως υπάρχει επιλογή με το


όνομα Build, με την οποία η μεταγλώττιση και σύνδεση του
προγράμματος γίνεται σε ένα βήμα

 Αν η σύνδεση είναι επιτυχής, τότε δημιουργείται το εκτελέσιμο


(executable) αρχείο του προγράμματός μας a.out (σε Unix/
Linux με τον μεταγλωττιστή gcc) ή my_example.exe (σε
Windows) και είμαστε σε θέση να το εκτελέσουμε

Προγραμματισμός Ι
Εκτέλεση Προγράμματος
 Η εκτέλεση ενός προγράμματος γίνεται με αρκετούς τρόπους

 Για παράδειγμα, αν χρησιμοποιήσουμε τον μεταγλωττιστή gcc


γράφουμε σε μία γραμμή εντολών:

$ a.out

 Αν εμφανιστεί ένα μήνυμα λάθους παρόμοιο με No such file


or directory ή command not found γράψτε:

./a.out

 Σε ένα ενοποιημένο περιβάλλον, π.χ. Dev-C++, θα υπάρχει


επιλογή παρόμοια με Execute->Compile & Run

Προγραμματισμός Ι

Παρατηρήσεις (Ι)
 Η C είναι μία γλώσσα προγραμματισμού που κάνει διάκριση μεταξύ πεζών και
κεφαλαίων γραμμάτων (case sensitive)
 Δηλαδή, αν γράψουμε Printf() αντί για printf() ο μεταγλωττιστής
θα εμφανίσει μήνυμα λάθους, γιατί ο χαρακτήρας 'P' είναι διαφορετικός
από τον χαρακτήρα 'p'

 Ένα μήνυμα λάθους που εμφανίζει ένας μεταγλωττιστής μπορεί να μην


συμβαίνει στη γραμμή που υποδεικνύει, αλλά στις αμέσως προηγούμενες
γραμμές

 Αν ο μεταγλωττιστής εμφανίζει πολλά μηνύματα λάθους, τότε προσπαθήστε


να βρείτε και να διορθώσετε μόνο το πρώτο λάθος και μεταγλωττίστε πάλι
το πρόγραμμά σας. Είναι πολύ πιθανό, η νέα μεταγλώττιση να εμφανίσει
πολύ λιγότερα ή και καθόλου λάθη

 Ο μεταγλωττιστής μπορεί να μην εμφανίσει μηνύματα λάθους, αλλά να


εμφανίσει μηνύματα προειδοποίησης (warnings). Ένα μήνυμα προειδοποίησης
μπορεί να ενημερώνει τον προγραμματιστή για ενδεχόμενη δυσλειτουργία του
προγράμματός του, οπότε καλό είναι να μην τα αγνοείτε

Προγραμματισμός Ι
Παρατηρήσεις (ΙI)
 O μεταγλωττιστής ανιχνεύει λανθασμένη εφαρμογή των κανόνων
της γλώσσας που τον εμποδίζει να μεταγλωττίσει το πρόγραμμα
σωστά (π.χ. ορθογραφικά λάθη, συντακτικά λάθη, αδήλωτες
μεταβλητές, ...) και όχι λάθη λογικής που ενδέχεται να
περιέχονται στο πρόγραμμά σας

 Δηλαδή, ο μεταγλωττιστής δεν είναι «μέσα στο κεφάλι σας» για


να ξέρει ποιος είναι ο σκοπός του προγράμματός σας

 Άρα, αν το πρόγραμμά σας μεταγλωττίζεται σωστά, δεν σημαίνει


απαραίτητα ότι θα παράγει και σωστά αποτελέσματα

Προγραμματισμός Ι

Παρατηρήσεις (ΙII)
 Π.χ. αν θέλετε να δημιουργήσετε ένα πρόγραμμα που να εμφανίζει τη λέξη
OLE, μόνο αν η τιμή μίας ακέραιας μεταβλητής a είναι μεγαλύτερη από 5,
και εσείς γράψετε:

τότε ο μεταγλωττιστής δεν θα εμφανίσει κάποιο μήνυμα λάθους, γιατί αυτό


το λάθος δεν ανήκει στα λάθη που αναγνωρίζει

 Το λάθος αυτό ονομάζεται λογικό λάθος, και ο μεταγλωττιστής δεν το


αντιλαμβάνεται

 Τα λογικά λάθη ονομάζονται και bugs και είναι λάθη τα οποία εισάγει άθελά
του ο προγραμματιστής και μόνο αυτός μπορεί να τα εντοπίσει και να τα
διορθώσει

 ΜΕΓΑΛΗ ΠΡΟΣΟΧΗ λοιπόν, γιατί τα bugs είσαστε αναγκασμένοι να τα


εντοπίζετε και να τα διορθώνετε μόνοι σας, κάνοντας τη λεγόμενη
αποσφαλμάτωση (debugging), με ή χωρίς τη βοήθεια κάποιου ειδικού
περιβάλλοντος ανίχνευσης λαθών (debugger), ανάλογα με την
πολυπλοκότητα του προγράμματός σας
Προγραμματισμός Ι
Προγραμματισμός Ι

Τύποι Δεδομένων – Δήλωση Μεταβλητών –


Έξοδος Προγράμματος

Μνήμη και Μεταβλητές


 Σχέση Μνήμης Υπολογιστή και Μεταβλητών
 Η μνήμη (RAM) ενός υπολογιστή αποτελείται από πολλά
εκατομμύρια θέσεις αποθήκευσης δεδομένων που έχουν διαδοχική
αρίθμηση

 Το μέγεθος κάθε θέσης μνήμης είναι μία οκτάδα (byte)

 Π.χ. σκεφτείτε ότι ένας παλιός υπολογιστής με μόνο 16ΜΒ μνήμης


έχει μνήμη:
16 * 1.024 = 16.384 kbytes
16.384 * 1.024 = 16.777.216 θέσεις μνήμης (bytes)

 Κάθε θέση μνήμης μπορεί να έχει ένα όνομα και ένα περιεχόμενο

 Μεταβλητή ονομάζεται μία θέση μνήμης που έχει ένα συγκεκριμένο


όνομα

 Η τιμή μίας μεταβλητής είναι το περιεχόμενο αυτής της θέσης


μνήμης (ή των θέσεων μνήμης, όπως θα δούμε) και μπορεί να
αλλάξει κατά τη διάρκεια εκτέλεσης του προγράμματος
Προγραμματισμός Ι
Ονόματα Μεταβλητών
 Απαράβατοι κανόνες κατά τη δήλωση του ονόματος
μίας μεταβλητής
 Μπορεί να αποτελείται από πεζά και κεφαλαία γράμματα του λατινικού
αλφαβήτου και ψηφία

 Αποδεκτός είναι επίσης και ο χαρακτήρας υπογράμμισης '_'


(underscore)

 Ο πρώτος χαρακτήρας πρέπει να είναι γράμμα ή ο χαρακτήρας


υπογράμμισης '_‘

 Η γλώσσα C είναι case sensitive (δηλ. κάνει διάκριση μεταξύ των


πεζών και κεφαλαίων γραμμάτων)
 Συνεπώς, η μεταβλητή με το όνομα nick είναι διαφορετική από τη
μεταβλητή με το όνομα Nick

 Οι δεσμευμένες λέξεις της C απαγορεύεται να χρησιμοποιηθούν ως


ονόματα μεταβλητών

Προγραμματισμός Ι

Δεσμευμένες Λέξεις της C

Προγραμματισμός Ι
Παρατηρήσεις
 Το όνομα που επιλέγετε να δώσετε σε μία μεταβλητή είναι
χρήσιμο να περιγράφει όσο το δυνατόν καλύτερα τον σκοπό της
μεταβλητής μέσα στο πρόγραμμα, ώστε ο κώδικας να είναι πιο
ευανάγνωστος
 Π.χ. το όνομα μίας μεταβλητής που υπολογίζει το άθροισμα
κάποιων αριθμών είναι προτιμότερο να είναι sum αντί για
var

 Αν το όνομα που επιλέξατε για μία μεταβλητή αποτελείται από


δύο ή και περισσότερες λέξεις, τότε προτείνεται να τις
διαχωρίζετε μεταξύ τους με τον χαρακτήρα υπογράμμισης '_',
έτσι ώστε να διευκολύνεται η ερμηνεία τους
 Π.χ. το όνομα μίας μεταβλητής που υπολογίζει τον αριθμό
των βιβλίων σε μία βιβλιοθήκη είναι προτιμότερο να είναι
books_number αντί για booksnumber

 Προτείνεται, τα ονόματα μεταβλητών να αποτελούνται μόνο


από πεζά γράμματα
Προγραμματισμός Ι

Δήλωση Μεταβλητών
 Για να χρησιμοποιήσετε μία μεταβλητή μέσα σε ένα πρόγραμμα
πρέπει πρώτα να τη δηλώσετε

 Η δήλωση μίας μεταβλητής γίνεται με τον ακόλουθο τρόπο:

τύπος_δεδομένων όνομα_μεταβλητής;

 Το όνομα_μεταβλητής είναι το τυχαίο όνομα που επιλέγει ο


προγραμματιστής σύμφωνα με τους κανόνες και τις
παρατηρήσεις που είπαμε προηγουμένως

 Ο τύπος_δεδομένων είναι ένας από τους αριθμητικούς τύπους


δεδομένων που υποστηρίζει η γλώσσα C
 Π.χ. η δεσμευμένη λέξη int χρησιμοποιείται για τη δήλωση
ακέραιων μεταβλητών, δηλαδή μεταβλητών που μπορούν να
έχουν μόνο ακέραιες τιμές ενώ η δεσμευμένη λέξη float
χρησιμοποιείται για τη δήλωση πραγματικών μεταβλητών,
δηλαδή μεταβλητών που μπορούν να έχουν τιμές με
κλασματικό μέρος

Προγραμματισμός Ι
Τύποι Μεταβλητών
Συνηθισμένο μέγεθος Εύρος τιμών Ψηφία
Τύπος
(bytes) (min-max) ακρίβειας

char 1 -128 … 127


short int 2 -32.768 … 32.767
int 4 -2.147.483.648…2.147.483.647
long int 4 -2.147.483.648…2.147.483.647
Μικρότερη θετική τιμή: 1.17*10-38
float 4 6
Μεγαλύτερη θετική τιμή: 3.4*1038
Μικρότερη θετική τιμή: 2.2*10-308
double 8 15
Μεγαλύτερη θετική τιμή: 1.8*10308
long double 8, 10, 12, 16

unsigned char 1 0 … 255

unsigned short int 2 0 … 65535

unsigned int 4 0 … 4.294.967.295

unsigned long int 4 0 … 4.294.967.295

 Π.χ.
int a; /* Δήλωση ακέραιας μεταβλητής με όνομα a. */
float b; /* Δήλωση πραγματικής μεταβλητής με όνομα b. */
Προγραμματισμός Ι

Παρατηρήσεις (1/3)
 Πολλές μεταβλητές του ίδιου τύπου μπορούν να δηλωθούν στην ίδια
γραμμή, αρκεί να διαχωρίζονται μεταξύ τους με κόμμα (,)
 Δηλαδή, αντί να δηλώσετε τις μεταβλητές a, b και c σε τρεις
ξεχωριστές γραμμές:
int a;
int b;
int c;
μπορείτε να τις δηλώσετε σε μία γραμμή ως εξής:
int a, b, c;

 Η δεσμευμένη λέξη int μπορεί να παραληφθεί στους ακέραιους


τύπους
 Π.χ., short αντί για short int

 Οι δεσμευμένες λέξεις μπορούν να βρίσκονται σε οποιαδήποτε σειρά


κατά τη δήλωση μιας μεταβλητής
 Π.χ., unsigned long int a;
είναι το ίδιο με:
int long unsigned a;
Προγραμματισμός Ι
Παρατηρήσεις (2/3)
 ‘Όταν δηλώνεται μία μεταβλητή, ο μεταγλωττιστής δεσμεύει τόσα
bytes όσα χρειάζονται για να είναι σε θέση να αποθηκεύσει την
τιμή της

 Το μέγεθος της μνήμης που δεσμεύει ένας τύπος δεδομένων


μπορεί να διαφέρει από υπολογιστή σε υπολογιστή
 Δηλαδή, ο τύπος int μπορεί να δεσμεύει 2 bytes σε κάποιον
υπολογιστή και όχι 4 bytes
 Για να μάθετε πόσες οκτάδες δεσμεύει ένας τύπος δεδομένων στον
υπολογιστή που εργάζεστε πρέπει να χρησιμοποιήσετε τον τελεστή
sizeof  Θα τον δούμε παρακάτω

 Με τη δήλωση μιας μεταβλητής, ο μεταγλωττιστής γνωρίζει το


όνομά της και τη διεύθυνση μνήμης στην οποία βρίσκεται η
μεταβλητή αυτή, έτσι, όταν η μεταβλητή χρησιμοποιείται στο
πρόγραμμα ο μεταγλωττιστής χρησιμοποιεί το όνομά της και
γνωρίζοντας την αντίστοιχη διεύθυνση μνήμης έχει πρόσβαση στο
περιεχόμενο της μεταβλητής

Προγραμματισμός Ι

Παρατηρήσεις (3/3)
 Να χρησιμοποιείτε τον τύπο float μόνο όταν η ακρίβεια των
δεκαδικών ψηφίων δεν είναι τόσο σημαντική στο πρόγραμμά σας

 Σε περίπτωση που χρειάζεστε υψηλή ακρίβεια των δεκαδικών


ψηφίων, να χρησιμοποιείτε τον τύπο double

 Π.χ., ποια πιστεύετε θα είναι η έξοδος του παρακάτω


προγράμματος???

#include <stdio.h>
int main(void)
{ Μεγάλη ήττα...!!!
float a = 3.1; Περιμέναμε η έξοδος του
προγράμματος να είναι Yes,
if(a == 3.1) αλλά εμφανίζει... No !!!!
printf("Yes\n"); Αν δηλώναμε τη μεταβλητή a
else ως double θα εμφάνιζε
printf("No\n");
σίγουρα Yes
return 0;
}
Προγραμματισμός Ι
Εκχώρηση τιμών σε Μεταβλητές (Ι)
 Η εκχώρηση μίας τιμής σε μία μεταβλητή γίνεται είτε μαζί με
τη δήλωση της μεταβλητής είτε αργότερα

 Π.χ. με την πρώτη εντολή δηλώνεται μία ακέραια μεταβλητή


(int) με όνομα a και μετά της εκχωρείται η τιμή 100

int a;
a = 100;

 Εναλλακτικά, θα μπορούσαμε να γράψουμε την εκχώρηση τιμής


μαζί με τη δήλωση:
int a = 100;

Προγραμματισμός Ι

Εκχώρηση τιμών σε Μεταβλητές (ΙΙ)


 Επίσης, επιτρέπεται η απόδοση αρχικών τιμών σε περισσότερες
από μία μεταβλητές ίδιου τύπου μαζί με τη δήλωσή τους, π.χ.
int a = 100, b = 200, c = 300;

 Για την εκχώρηση μίας πραγματικής τιμής σε μία μεταβλητή τύπου


float χρησιμοποιείται η τελεία (.) για το δεκαδικό μέρος και όχι
το κόμμα (,) π.χ.
float a = 1.24;

 Αν μπροστά από μία ακέραια τιμή υπάρχει το ψηφίο 0, τότε αυτή


η τιμή ερμηνεύεται σαν οκταδικός αριθμός
 Π.χ. με την παρακάτω εντολή η τιμή που εκχωρείται στη μεταβλητή
a δεν είναι 100, αλλά 64

int a = 0100;

 Παρομοίως, αν μπροστά από μία ακέραια τιμή υπάρχει το 0x ή το


0X, τότε αυτή η τιμή ερμηνεύεται σαν δεκαεξαδικός αριθμός
 Π.χ. με την παρακάτω εντολή η τιμή της μεταβλητής a γίνεται 16.

int a = 0x10;
Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Η τιμή μίας μεταβλητής μπορεί (προφανώς) να αλλάζει μέσα στο
πρόγραμμα
 Όταν γίνεται χρήση μίας μεταβλητής στο πρόγραμμα
χρησιμοποιείται πάντα η τελευταία τιμή της και όχι κάποια από
τις προηγούμενες τιμές της

 Ποια τιμή εκτυπώνεται στην οθόνη???

Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 Η τιμή που εκχωρείται σε μία μεταβλητή πρέπει να συμβαδίζει με
τον τύπο της μεταβλητής
 Π.χ. με την εντολή:
int a = 10.9;
η τιμή της μεταβλητής a γίνεται 10, γιατί η μεταβλητή a
δηλώνεται σαν ακέραια μεταβλητή και όχι σαν πραγματική και το
δεκαδικό μέρος αποκόπτεται (Προσοχή!! Δεν στρογγυλοποιείται)

 Η τιμή που εκχωρείται σε μία μεταβλητή πρέπει να είναι μέσα στο


επιτρεπτό εύρος τιμών
 Π.χ. με την εντολή:
char ch = 130;
η τιμή της μεταβλητής ch δεν γίνεται 130, γιατί το εύρος τιμών
μίας μεταβλητής τύπου char είναι από -128 έως 127. Άρα, η
τιμή 130 είναι μία τιμή εκτός των επιτρεπτών ορίων

Προγραμματισμός Ι
Παρατηρήσεις (ΙΙΙ)
 Η τιμή μίας πραγματικής μεταβλητής μπορεί να είναι και ακέραια
 Π.χ. επιτρέπεται να γράψουμε:
float a = 50;
γιατί είναι ισοδύναμο με:
float a = 50.0;
 Η τιμή μίας πραγματικής μεταβλητής μπορεί να γραφεί και με
επιστημονική σημειογραφία (συνήθως χρησιμοποιείται όταν η τιμή
είναι πολύ μικρή ή πολύ μεγάλη)
 Π.χ. αντί για
a = 0.085;
μπoρούμε να γράψουμε
a = 85E-3;
 Το γράμμα Ε ή e αναπαριστά το 10, ενώ ο αριθμός που το
ακολουθεί είναι η θετική ή αρνητική δύναμη του 10.

 Δηλαδή, η έκφραση 85E-3 αντιστοιχεί στον αριθμό 85*10-3


Προγραμματισμός Ι

Σταθερές (Ι)
 Σταθερά ονομάζεται μία μεταβλητή που η τιμή της δεν μπορεί να
αλλάξει μέσα στο πρόγραμμα
 Για να δηλωθεί μία μεταβλητή σαν σταθερά, πρέπει να προηγηθεί
η λέξη const πριν (ή μετά) από τον τύπο της μεταβλητής
 Επίσης, μαζί με τη δήλωση της σταθεράς, πρέπει να της
εκχωρηθεί και μία αρχική τιμή, η οποία δεν θα μπορεί να αλλάξει
μέσα στο πρόγραμμα

 Π.χ. με την επόμενη εντολή η ακέραια μεταβλητή a δηλώνεται


σαν σταθερά και της εκχωρείται (μόνιμα) η τιμή 10
const int a = 10;
 Αν σε κάποιο σημείο του προγράμματος επιχειρήσουμε να της
αλλάξουμε τιμή, π.χ. να γράψουμε:
a = 100;
τότε ο μεταγλωττιστής θα εμφανίσει μήνυμα λάθους για μη
επιτρεπτή ενέργεια
Προγραμματισμός Ι
Σταθερές (ΙΙ)
 Εναλλακτικός τρόπος για τη δήλωση μίας σταθεράς είναι η χρήση
της οδηγίας #define, η οποία χρησιμοποιείται για τη δήλωση
μακροεντολών

 Συνήθως, μία μακροεντολή αντιστοιχίζει ένα συμβολικό όνομα με


κάποια αριθμητική τιμή
 Για τη δήλωση μακροεντολών, η οδηγία #define χρησιμοποιείται
ως εξής:

#define όνομα_μακροεντολής τιμή

 Π.χ. η εντολή:
#define NUM 100
δηλώνει τη μακροεντολή με όνομα NUM και τιμή 100

 Η NUM μπορεί να χρησιμοποιηθεί οπουδήποτε μέσα στο πρόγραμμα

 Ο μεταγλωττιστής όταν συναντάει τη NUM μέσα στο πρόγραμμα


την αντικαθιστά με την τιμή 100
Προγραμματισμός Ι

Παρατηρήσεις
 Οι δηλώσεις των μακροεντολών με την οδηγία #define είναι
προτιμότερο να γίνονται πριν από τη συνάρτηση main()
 Τα ονόματα των μακροεντολών με την οδηγία #define συνηθίζεται να
δηλώνονται με κεφαλαία γράμματα
Στο τέλος της οδηγίας #define δεν μπαίνει ελληνικό ερωτηματικό (;)
 Π.χ.

vs.

Προγραμματισμός Ι
const vs. #define
 Μπορούμε να δημιουργήσουμε ονόματα σταθερών, χρησιμοποιώντας
είτε τη λέξη-κλειδί const είτε με τον ορισμό μακροεντολής
(#define), αλλά υπάρχουν αρκετές σημαντικές διαφορές μεταξύ των
δύο τρόπων δημιουργίας σταθερών

 Με την #define μπορούμε να δημιουργήσουμε ένα όνομα


αριθμητικής σταθεράς, σταθεράς χαρακτήρα ή αλφαριθμητικού

 Με χρήση της λέξης-κλειδί const δημιουργούμε σταθερές


οποιουδήποτε τύπου, π.χ. δεικτών, πινάκων, δομών, ενώσεων κτλ.

 Οι σταθερές με χρήση της λέξης-κλειδί const υπόκεινται τους


ίδιους κανόνες εμβέλειας, όπως και οι μεταβλητές (θα δούμε
περισσότερα στο Κεφ. 11), ενώ οι σταθερές που δηλώνονται ως
μακροεντολές με την #define δεν ακολουθούν τους ίδιους κανόνες

 Οι τιμές των σταθερών που έχουν δηλωθεί με τη λέξη-κλειδί const


φαίνονται στον debugger, σε αντίθεση με αυτές που έχουν δηλωθεί
με την #define Προγραμματισμός Ι 19

Η συνάρτηση printf()
 Η συνάρτηση printf() χρησιμοποιείται για την εμφάνιση δεδομένων
στο αρχείο εξόδου stdout (standard output stream) , το οποίο εξ’
ορισμού συνδέεται με την οθόνη

 Η συνάρτηση printf() δέχεται μία μεταβλητή λίστα παραμέτρων


 Η πρώτη παράμετρος είναι ένα αλφαριθμητικό μορφοποίησης
(format string), δηλαδή μία ακολουθία χαρακτήρων μέσα σε
διπλά εισαγωγικά (" ") η οποία καθορίζει τον τρόπο με τον
οποίο θα εμφανιστούν τα δεδομένα στην οθόνη

 Οι επόμενες παράμετροι είναι προαιρετικές και, αν υπάρχουν, η


printf() εμφανίζει τις τιμές τους στην οθόνη

 Το αλφαριθμητικό μορφοποίησης (format string) μπορεί να περιέχει:


 Απλούς χαρακτήρες (οι οποίοι εμφανίζονται όπως είναι στην
οθόνη)
 Ακολουθίες Διαφυγής
 Προσδιοριστικά Μετατροπής
Προγραμματισμός Ι
Ακολουθία Διαφυγής
 Μία ακολουθία διαφυγής (escape sequence) χρησιμοποιείται είτε
για να μετακινηθεί ο δρομέας (cursor) σε κάποια θέση της
οθόνης είτε για την εμφάνιση κάποιων ειδικών χαρακτήρων
 Μία ακολουθία διαφυγής αποτελείται από μία ανάστροφη
κεκλιμένη (\) (backslash) και έναν ειδικό χαρακτήρα

Προγραμματισμός Ι

Προσδιοριστικό Μετατροπής
 Ένα προσδιοριστικό μετατροπής (conversion specification)
αρχίζει με τον χαρακτήρα % και ακολουθείται από έναν ή
περισσότερους χαρακτήρες μετατροπής που έχουν ειδική σημασία

Προγραμματισμός Ι
Παραδείγματα (Ι)

Προγραμματισμός Ι

Παραδείγματα (ΙΙ)

Προγραμματισμός Ι
Εμφάνιση μεταβλητών
 Τα ονόματα των μεταβλητών εισάγονται στην printf() μετά τα
διπλά εισαγωγικά (" ") με τη χρήση κόμματος (,) και αν οι
μεταβλητές είναι περισσότερες από μία πρέπει και αυτές να
διαχωρίζονται μεταξύ τους με κόμμα (,)

 Ο μεταγλωττιστής αντιστοιχίζει ένα-προς-ένα, από αριστερά


προς τα δεξιά, τα ονόματα των μεταβλητών με τα προσδιοριστικά
μετατροπής

 Αν τα προσδιοριστικά μετατροπής είναι περισσότερα από τις


μεταβλητές, τότε για τα πρόσθετα προσδιοριστικά εμφανίζονται
τυχαίες τιμές (σκουπίδια)

 Αντίστοιχα, αν τα προσδιοριστικά μετατροπής είναι λιγότερα από


τις μεταβλητές, τότε δεν εμφανίζονται οι τιμές των πρόσθετων
μεταβλητών

Προγραμματισμός Ι

Παράδειγμα

Προγραμματισμός Ι
Προαιρετικά Πεδία
 Ένα προσδιοριστικό μετατροπής, στην απλή μορφή
του, αρχίζει με τον χαρακτήρα % και ακολουθείται από
τον κατάλληλο χαρακτήρα μετατροπής

 Όμως, ανάμεσά τους, μπορεί να περιέχονται έως και


τέσσερα επιπλέον προαιρετικά πεδία, όπως φαίνεται
στο σχήμα
πλάτος χαρακτήρας
flags πεδίου πρόθεμα μετατροπής

%
% +
flag 8
width .3 L
precision modifier
g
type

flags
σημαία ακρίβεια

flags προσδιοριστικό μετατροπής


Προγραμματισμός Ι

Ακρίβεια
 Όταν εμφανίζουμε την τιμή μίας πραγματικής μεταβλητής (τύπου float ή
double) μπορούμε να καθορίσουμε πόσα ψηφία ακρίβειας θα
εμφανιστούν στην οθόνη

 Εξ’ ορισμού (by default) εμφανίζονται έξι δεκαδικά ψηφία

 Αν δεν επιθυμούμε να εμφανιστούν έξι ψηφία, τότε μετά τον χαρακτήρα


% πρέπει να προσθέσουμε την τελεία (.) και
 είτε έναν ακέραιο αριθμό που να δηλώνει το επιθυμητό πλήθος των
δεκαδικών ψηφίων
 είτε τον χαρακτήρα * και έναν ακέραιο στη λίστα των μεταβλητών
που να δηλώνει το επιθυμητό πλήθος των δεκαδικών ψηφίων

 Η τελική τιμή του πραγματικού αριθμού που εμφανίζεται με την


printf() στρογγυλοποιείται προς τα πάνω ή προς τα κάτω, ανάλογα με
το αν η τιμή του πρώτου ψηφίου που αποκόπτεται είναι μεγαλύτερη ή όχι
από το 4, αντίστοιχα

 Αν δεν θέλουμε να εμφανιστούν δεκαδικά ψηφία, τότε προσθέτουμε μόνο


την τελεία (.), χωρίς αυτή να ακολουθείται από κάποιον αριθμό
Προγραμματισμός Ι
Παράδειγμα

Προγραμματισμός Ι

Εμφάνιση χαρακτήρων σε Αλφαριθμητικό


 Όταν θέλουμε να εμφανίσουμε ένα αλφαριθμητικό, δηλαδή μία ακολουθία
χαρακτήρων, μπορούμε να καθορίσουμε πόσοι χαρακτήρες του θα
εμφανιστούν, ακολουθώντας την ίδια τεχνική με προηγουμένως

 Αν ο αριθμός που θα δηλωθεί υπερβαίνει το πλήθος των χαρακτήρων,


τότε εμφανίζονται όλοι οι χαρακτήρες του αλφαριθμητικού και δεν
προστίθεται κανένας άλλος

Προγραμματισμός Ι
Πλάτος Πεδίου
 Όταν εμφανίζουμε την τιμή μίας ακέραιας ή πραγματικής μεταβλητής
μπορούμε να καθορίσουμε το συνολικό πλήθος των χαρακτήρων που θα
εμφανιστούν στην οθόνη, μαζί με τα ψηφία ακρίβειας και την
υποδιαστολή

 Για να καθορίσουμε το συνολικό πλήθος εισάγουμε:


 είτε έναν ακέραιο αριθμό αμέσως μετά από τον χαρακτήρα %, ο
οποίος ονομάζεται πλάτος πεδίου
 είτε τον χαρακτήρα * και έναν ακέραιο στη λίστα μεταβλητών, ο
οποίος δηλώνει το πλάτος πεδίου
 Αν η τιμή της μεταβλητής χρειάζεται λιγότερους χαρακτήρες από το
δηλωμένο πλάτος, τότε στην έξοδο προστίθενται κενοί χαρακτήρες από
αριστερά προς τα δεξιά μέχρι να συμπληρωθεί το συνολικό πλήθος των
χαρακτήρων

 Αν η τιμή της μεταβλητής χρειάζεται περισσότερους χαρακτήρες από το


δηλωμένο πλάτος, τότε ο αριθμός αυτός δεν λαμβάνεται υπόψη και η
τιμή της μεταβλητής εμφανίζεται με όσους χαρακτήρες απαιτείται

ΣΗΜΕΙΩΣΗ: Προφανώς, σε περίπτωση πραγματικού αριθμού, μετά το πλάτος


του πεδίου μπορεί να καθοριστεί και η ακρίβεια του πραγματικού αριθμού (με
χρήση της τελείας και ενός αριθμού που έπεται αυτής και δηλώνει το πλήθος των
δεκαδικών ψηφίων, όπως δείξαμε προηγουμένως)
Προγραμματισμός Ι

Παράδειγμα

Προγραμματισμός Ι
Πρόθεμα
 Για την εμφάνιση ενός short ακεραίου μπορεί να χρησιμοποιηθεί
προαιρετικά το γράμμα h, ενώ για την εμφάνιση ενός long
ακεραίου μπορεί να χρησιμοποιηθεί το γράμμα l ή L, όπως
φαίνεται στο παρακάτω παράδειγμα

Προγραμματισμός Ι

Σημαίες
 Οι σημαίες χρησιμοποιούνται για περαιτέρω
μορφοποίηση των εμφανιζόμενων τιμών, όπως φαίνεται
στον παρακάτω πίνακα

Προγραμματισμός Ι
Παράδειγμα

Προγραμματισμός Ι

Παρατηρήσεις (Ι)
 Οταν χρησιμοποιείτε συχνά μία πραγματική μεταβλητή σε διάφορες
εκφράσεις μέσα στο πρόγραμμα (π.χ. συγκρίσεις, πράξεις, ...), τότε να
προτιμάτε τον τύπο double και όχι τον τύπο float, γιατί είναι πιθανό
να μην γίνει η διαχείριση των δεκαδικών ψηφίων με τον τρόπο που θα
αναμένατε

 Π.χ. το επόμενο πρόγραμμα μπορεί να μην εμφανίσει την τιμή


12345.65432, αλλά μία τιμή παραπλήσια σε αυτή.

Προγραμματισμός Ι
Παρατηρήσεις (ΙΙ)
 Αν θέλετε η ακολουθία χαρακτήρων της printf(), για λόγους
εμφάνισης, να εκτείνεται σε περισσότερες από μία γραμμές στον κώδικά
σας, τότε να χρησιμοποιείτε τον χαρακτήρα της ανάστροφης κεκλιμένης
'\' (backslash)

 Π.χ. ο κώδικας της παρακάτω printf() εκτείνεται σε δύο γραμμές

 Όμως, σαν αποτέλεσμα στην οθόνη, όλοι οι χαρακτήρες του μηνύματος


θα εμφανίζονται στην ίδια γραμμή

 Λόγω της ειδικής σημασίας του χαρακτήρα %, για την εμφάνιση του
χαρακτήρα '%' πρέπει να γραφούν δύο χαρακτήρες %

 Π.χ. η επόμενη printf() εμφανίζει το μήνυμα 100% στην οθόνη

Προγραμματισμός Ι

Μετατροπή Τύπου (type cast)


 Υπάρχουν περιπτώσεις όπου ένας τύπος δεδομένων πρέπει να
μετατραπεί προσωρινά σε κάποιον άλλο τύπο δεδομένων

 Π.χ. είναι πιθανό σε ένα σημείο του προγράμματος μία ακέραια


μεταβλητή που έχει δηλωθεί σαν int να πρέπει να μετατραπεί
προσωρινά σε μία πραγματική τύπου float, ή και το αντίστροφο

 Η γενική μορφή μίας τέτοιας μετατροπής είναι:

(τύπος_δεδομένων) (παράσταση)

 Π.χ. αν η μεταβλητή a έχει δηλωθεί:


int a;
τότε η έκφραση:
(float)a;
προσαρμόζει προσωρινά τον τύπο της a από int σε float

 Λέγοντας προσωρινά, εννοούμε ότι στη συνέχεια του προγράμματος ο


τύπος της μεταβλητής a συνεχίζει να είναι int
Προγραμματισμός Ι
Παράδειγμα

 Η έκφραση (float)i
προσαρμόζει προσωρινά τον
τύπο της μεταβλητής i από
int σε float, έτσι ώστε
το αποτέλεσμα της διαίρεσης
να είναι πραγματικός
αριθμός

 Αν γράφαμε k = i/j, τότε


η τιμή του k θα ισούνταν με
το αποτέλεσμα της ακέραιας
διαίρεσης 20/30, άρα υπό
μορφή float με ακρίβεια 2
δεκαδικών ψηφίων (λόγω της
σύγκεκριμένης printf()) η
τιμή του k θα ήταν 0.00

Προγραμματισμός Ι

Προγραμματισμός Ι

Είσοδος Δεδομένων
Η συνάρτηση scanf()
 Η συνάρτηση scanf() χρησιμοποιείται για την είσοδο δεδομένων από
ένα αρχείο εισόδου, το οποίο ονομάζεται stdin (standard input
stream) και εξ’ ορισμού συνδέεται με το πληκτρολόγιο

 Η scanf() δέχεται μία μεταβλητή λίστα παραμέτρων, παρόμοια με


την printf(), δηλαδή:

 Η πρώτη παράμετρος είναι ένα αλφαριθμητικό μορφοποίησης


(format string), το οποίο, συνήθως, περιέχει μόνο απλά
προσδιοριστικά μετατροπής (π.χ. %d για μεταβλητές τύπου int,
%f για μεταβλητές τύπου float κτλ...)

 Οι επόμενες προαιρετικές παράμετροι είναι οι διευθύνσεις


μνήμης των μεταβλητών στις οποίες θα εκχωρηθούν τα
δεδομένα που θα εισάγει ο χρήστης από το πληκτρολόγιο

Κάθε προσδιοριστικό μετατροπής πρέπει να αντιστοιχεί σε μία


διεύθυνση μεταβλητής και η αντιστοίχιση γίνεται ένα προς ένα

Για μεταβλητές τύπου double, χρησιμοποιείται το προσδιοριστικό


μετατροπής %lf και όχι το %f, το οποίο χρησιμοποιείται μόνο για
μεταβλητές τύπου float
Προγραμματισμός Ι

Παράδειγμα 1
 Π.χ.
int i;
scanf("%d", &i);

 Ο χαρακτήρας &, που μπαίνει πριν από το όνομα της μεταβλητής,


ονομάζεται τελεστής διεύθυνσης και χρησιμοποιείται για να αποθηκευτεί
ο αριθμός που θα εισάγει ο χρήστης στη διεύθυνση μνήμης της
μεταβλητής i

 Περισσότερες λεπτομέρειες για τη σημασία του τελεστή διεύθυνσης &


θα δούμε αναλυτικά στους “δείκτες της C”

Προγραμματισμός Ι
Παράδειγμα 2 (Ι)
 Μπορούμε να χρησιμοποιήσουμε τη συνάρτηση scanf() για να διαβάσουμε
περισσότερες από μία τιμές από το πληκτρολόγιο και να τις αποθηκεύσουμε
ως τιμές σε κάποιες μεταβλητές του προγράμματος
 Π.χ.
int i;
float j;
scanf("%d%f", &i, &j);
 H πρώτη παράμετρος της scanf() είναι το αλφαριθμητικό μορφοποίησης
%d%f, ενώ οι επόμενες παράμετροι είναι οι διευθύνσεις μνήμης των
μεταβλητών i και j αντίστοιχα
 Το %d αντιστοιχεί στη διεύθυνση της μεταβλητής i
 Το %f αντιστοιχεί στη διεύθυνση της μεταβλητής j
 Δηλαδή η αντιστοίχιση γίνεται ένα προς ένα και από αριστερά προς τα δεξιά

 Για την είσοδο των δεδομένων χρησιμοποιείται συνήθως το «κενό διάστημα»


(space) μεταξύ των διαφορετικών τιμών που εισάγονται, δεδομένου ότι κατά
το διάβασμα αριθμητικών τιμών, η scanf() αγνοεί όλα τα λευκά διαστήματα
(π.χ. κενά διαστήματα, tab, χαρακτήρα νέας γραμμής) που μπορεί να
υπάρχουν πριν από κάθε αριθμητική τιμή
Προγραμματισμός Ι

Παράδειγμα 2 (ΙΙ)
 Στο προηγούμενο παράδειγμα, αν ο χρήστης εισάγει π.χ. τις τιμές
10 και 4.65, αυτές θα πρέπει να απέχουν μεταξύ τους ένα ή
περισσότερα κενά

 Για να ληφθούν από τη συνάρτηση scanf() πρέπει μετά ο χρήστης


να πατήσει Enter

 Τότε, η τιμή 10 αποθηκεύεται στη μεταβλητή i και η τιμή 4.65 στη


μεταβλητή j, αντίστοιχα

Προγραμματισμός Ι
Παραδείγματα 3 & 4
 Στο επόμενο παράδειγμα, η scanf() διαβάζει έναν χαρακτήρα και τον
αποθηκεύει στη μεταβλητή ch
char ch;
scanf("%c", &ch);
 Στο επόμενο παράδειγμα, η scanf() διαβάζει ένα αλφαριθμητικό και το
αποθηκεύει στον πίνακα χαρακτήρων str
 Παρατηρήστε, ότι πριν από τη μεταβλητή str δεν χρησιμοποιείται ο
τελεστής &, γιατί - όπως θα δούμε στο κεφάλαιο των “Πινάκων της C”- το
όνομα ενός πίνακα ισοδυναμεί με τη διεύθυνση του πρώτου στοιχείου του
char str[100];
scanf("%s", str);
 Aν ο χρήστης εισάγει το αλφαριθμητικό sample και πατήσει Enter, τότε οι
χαρακτήρες του θα αποθηκευτούν στις αντίστοιχες θέσεις του πίνακα str
 Δηλαδή, η τιμή του str[0] θα γίνει 's', του str[1] θα γίνει 'a', του
str[2] θα γίνει 'm', κ.ο.κ.
 Το παράδειγμα αυτό θα το κατανοήσετε καλύτερα αργότερα, αφού θα
μιλήσουμε για πίνακες, χαρακτήρες και αλφαριθμητικά
Προγραμματισμός Ι

Παρατηρήσεις
 Να θυμάστε ότι η scanf() απαιτεί τον τελεστή διεύθυνσης & πριν από
το όνομα κάθε αριθμητικής μεταβλητής (π.χ. int, double, char,
float, …)

Αν τον ξεχάσετε, το πρόγραμμά σας δεν θα εκτελεστεί σωστά

Αντίθετα, όταν ο τύπος της μεταβλητής είναι δείκτης, ο τελεστής


διεύθυνσης δεν χρειάζεται

 Για να διαβάσετε με τη scanf() ένα αλφαριθμητικό που μπορεί να


αποτελείται από πολλές λέξεις (π.χ. "Text with multiple words"),
πρέπει να χρησιμοποιήσετε μία πιο σύνθετη μορφή της,

π.χ.: scanf("%[^\n]",str);

γιατί η scanf() εξ’ ορισμού σταματάει το διάβασμα του αλφαριθμητικού


όταν συναντήσει έναν κενό χαρακτήρα

Προγραμματισμός Ι
Τί επιστρέφει η συνάρτηση scanf() ???
 Η συνάρτηση scanf() επιστρέφει έναν ακέραιο αριθμό που δηλώνει
πόσα από τα δεδομένα εισόδου διαβάστηκαν και εκχωρήθηκαν στις
μεταβλητές του προγράμματος, ενώ οι τιμές που δεν διαβάστηκαν
παραμένουν στο stdin

 Π.χ. στο παράδειγμα:


int i;
scanf("%d", &i);

αν ο χρήστης εισάγει έναν ακέραιο, η συνάρτηση scanf() επιστρέφει


την τιμή 1
 Ενώ στο παράδειγμα:
int i;
float j;
scanf("%d%f", &i, &j);

αν ο χρήστης εισάγει έναν ακέραιο και έναν πραγματικό αριθμό, η


συνάρτηση scanf() επιστρέφει την τιμή 2
Προγραμματισμός Ι

Παρατηρήσεις
 Η συνάρτηση scanf() δεν είναι μία ασφαλής συνάρτηση και πρέπει να
τη χρησιμοποιείτε με μεγάλη προσοχή
 Σε επαγγελματικές εφαρμογές πρέπει να ελέγχετε την τιμή επιστροφής
της

 Σχεδόν σε όλα τα προγράμματα που θα υλοποιήσουμε, όταν


χρησιμοποιούμε τη συνάρτηση scanf(), για λόγους απλότητας, δεν θα
ελέγχουμε την τιμή επιστροφής της και θα θεωρούμε ότι οι τιμές που
εισάγει ο χρήστης θα είναι σε συμφωνία με τα προσδιοριστικά
μετατροπής
Προγραμματισμός Ι
Παρεμβολή απλών χαρακτήρων στη scanf() (I)
 Στην πιο συνηθισμένη χρήση της, το αλφαριθμητικό μορφοποίησης της
scanf() δεν περιέχει απλούς χαρακτήρες παρά μόνο τα
προσδιοριστικά μετατροπής (π.χ. %d, %f,…)

 Ωστόσο, αν παρεμβληθούν κάποιοι απλοί χαρακτήρες, τότε πρέπει οι


αντίστοιχοι χαρακτήρες να εισαχθούν και από το πληκτρολόγιο

 Π.χ. στην επόμενη scanf() παρεμβάλλεται ο χαρακτήρας κόμμα (,)

#include <stdio.h>
int main(void)
{
int a, b;
scanf("%d , %d", &a, &b);
printf("%d %d\n", a, b);
return 0;
}
 Για να λειτουργήσει σωστά αυτό το πρόγραμμα πρέπει οι ακέραιες τιμές
που θα εισάγει ο χρήστης να διαχωρίζονται μεταξύ τους με κόμμα (,)

Προγραμματισμός Ι

Παρεμβολή απλών χαρακτήρων στη scanf() (II)


 Αν αντί για κόμμα (όπως είδαμε στο προηγούμενο παράδειγμα), μεταξύ
των ειδικών χαρακτήρων μέσα στην scanf() χρησιμοποιούνταν ο
χαρακτήρας 'm', (όπως φαίνεται παρακάτω), θα έπρεπε να εισαχθεί ο
χαρακτήρας 'm' μεταξύ των τιμών που θα έδινε ο χρήστης από το
πληκτρολόγιο

#include <stdio.h>
int main(void)
{
int a, b;
scanf("%dm%d", &a, &b);
printf("%d %d\n", a, b);
return 0;
}

 Αν δηλαδή ο χρήστης επιθυμούσε να εισάγει τις τιμές 12 και 43, θα


έπρεπε να πληκτρολογήσει: 12m43

 Γενικά, προτείνουμε να μην παρεμβάλλεται κάποιος χαρακτήρας μεταξύ


των προσδιοριστικών μετατροπής, έτσι ώστε να μην χρειάζεται κάποια
επιπλέον εισαγωγή χαρακτήρων από τον χρήστη (σκεφτείτε επίσης ότι
ο τελικός χρήστης του προγράμματος δεν είναι απαραίτητα και ο
δημιουργός του προγράμματος)
Προγραμματισμός Ι
Καθαρισμός Μνήμης
 Η συνάρτηση scanf() δεν θα λειτουργήσει σωστά, αν ο χρήστης
δεν εισάγει τα δεδομένα σύμφωνα με την ακολουθία των
προσδιοριστικών μετατροπής που ορίζονται σε αυτήν

 Στο επόμενο παράδειγμα η scanf() αναμένει μία ακέραια και μία


πραγματική τιμή

 Τι θα συμβεί αν ο χρήστης εισαγάγει – έστω κατά λάθος – ως


ακέραια τιμή την τιμή 5.65 ?
Προγραμματισμός Ι

Παράδειγμα (Ι)
 Η συνάρτηση scanf() δεν διαβάζει τον χαρακτήρα νέας γραμμής που
πληκτρολογεί ο χρήστης στο τέλος της εισαγωγής δεδομένων

 Αυτός ο χαρακτήρας θα διαβαστεί στην επόμενη κλήση της scanf()

 Αν όμως, στην επόμενη κλήση της, η scanf() χρησιμοποιείται για το


διάβασμα χαρακτήρων, τότε θα διαβαστεί μόνο αυτός ο χαρακτήρας
(της νέας γραμμής) και οι υπόλοιποι θα αγνοηθούν

 Π.χ. το επόμενο πρόγραμμα δεν θα εκτελεστεί σωστά

Προγραμματισμός Ι
Παράδειγμα (ΙΙ)
 Αν αντιστρέψουμε στο προηγούμενο παράδειγμα τη σειρά του
διαβάσματος, τότε το πρόγραμμα θα εκτελεστεί σωστά, αφού –
σύμφωνα με προηγούμενη παρατήρηση – ο χαρακτήρας νέας
γραμμής που υπάρχει πριν από την αριθμητική τιμή αγνοείται

Προγραμματισμός Ι

Τρόποι Καθαρισμού Μνήμης


 Ένας τρόπος για να αδειάσουμε τη μνήμη του πληκτρολογίου από
τα δεδομένα που έχουν παραμείνει είναι με τη χρήση της
συνάρτησης getchar() χρησιμοποιώντας τον παρακάτω
επαναληπτικό βρόχο

while(getchar() != '\n');

ή – ακόμα καλύτερα – τον παρακάτω:

while((ch = getchar()) != '\n' && ch != EOF);

 Ωστόσο, πολλοί προγραμματιστές χρησιμοποιούν τη συνάρτηση


fflush()

fflush(stdin);

 Προσέξτε όμως, σύμφωνα με το πρότυπο της C, η συμπεριφορά


της fflush() όταν χρησιμοποιείται με όρισμα το stdin είναι
ακαθόριστη πράγμα που σημαίνει, ΜΗΝ ΤΗ ΧΡΗΣΙΜΟΠΟΙΕΙΤΕ
Προγραμματισμός Ι
Παραδείγματα (1)
 Γράψτε ένα πρόγραμμα που να διαβάζει την ακτίνα ενός κύκλου και να
εμφανίζει το εμβαδό του αντίστοιχου κύκλου και την περίμετρό του

Προγραμματισμός Ι 16

Παραδείγματα (2)
 Γράψτε ένα πρόγραμμα, το οποίο να διαβάζει δύο ακέραιες τιμές, να τις
αποθηκεύει σε δύο ακέραιες μεταβλητές και να αντιμεταθέτει τις τιμές
τους

Προγραμματισμός Ι 17
Προγραμματισμός Ι

Τελεστές

Ο τελεστής εκχώρησης =
 Ο τελεστής = χρησιμοποιείται για την απόδοση τιμής (ή αλλιώς ανάθεση
τιμής) σε μία μεταβλητή
 Π.χ. με την εντολή:
a = 10;
η τιμή της μεταβλητής a γίνεται 10 (μπορούμε επίσης να πούμε ότι στη
μεταβλητή a εκχωρήθηκε η τιμή 10)

ενώ με την εντολή:


a = k;
η τιμή της μεταβλητής a γίνεται ίση με την τιμή της μεταβλητής k
(μπορούμε επίσης να πούμε ότι στη μεταβλητή a εκχωρήθηκε η τιμή της
μεταβλητής k)

 Αν ο τελεστής = χρησιμοποιείται πολλές φορές σε μία εντολή


εκχώρησης, τότε η τελική τιμή εκχώρησης αποθηκεύεται σε όλες τις
μεταβλητές (ο τελεστής εφαρμόζεται από δεξιά προς τα αριστερά)

 Π.χ. με την εντολή:


int a,b,c;
a = b = c = 10;
οι τιμές των μεταβλητών a, b και c γίνονται ίσες με 10
Προγραμματισμός Ι
Παρατηρήσεις (I)
ΠΡΟΣΟΧΗ!!!
 Τονίζουμε ότι ο τελεστής εκχώρησης = εφαρμόζεται από δεξιά
προς τα αριστερά

 Συνεπώς, τί θα εμφανίσει το παρακάτω πρόγραμμα ???

int a, b, c;
a = b = 10 = c;
printf("%d %d %d\n",a,b,c);

 Το πρόγραμμα δεν θα “τρέξει”, και ο compiler θα εντοπίσει


συντακτικό λάθος (δεδομένου ότι γίνεται προσπάθεια εκχώρησης
της τιμής της μεταβλητής c στην “σταθερή τιμή” 10... (πράγμα
αδύνατον)

 Μην συγχέετε λοιπόν την ισότητα από τα “μαθηματικά”, με τον


τελεστή ανάθεσης...
Προγραμματισμός Ι

Παρατηρήσεις (IΙ)
 Ο δεξιός τελεστέος (right operand) πρέπει να είναι τύπου rvalue,
δηλ. μία έκφραση που έχει τιμή, όπως μία σταθερά ή μία μεταβλητή

 Ο αριστερός τελεστέος (left operand) πρέπει να είναι τύπου


lvalue
 O τύπος lvalue αναφέρεται σε κάτι αποθηκευμένο στη μνήμη που
μπορεί να μεταβάλλεται, όπως μία μεταβλητή
 Ο αριστερός τελεστέος απαγορεύεται να είναι σταθερά ή το αποτέλεσμα
ενός υπολογισμού, π.χ.:

10 = a; /* Δεν επιτρέπεται. */
a+b = 20; /* Δεν επιτρέπεται. */

 Στις παραπάνω περιπτώσεις ο μεταγλωττιστής θα εμφανίσει μήνυμα


λάθους της μορφής: "error '=' : left operand must be
lvalue"
Προγραμματισμός Ι
Παρατηρήσεις (IIΙ)

 Αν ο τύπος της μεταβλητής δεν είναι ίδιος με τον τύπο της τιμής,
τότε, εφόσον η μετατροπή είναι δυνατή, η τιμή πρώτα
μετατρέπεται στον τύπο της μεταβλητής και μετά εκχωρείται σε
αυτήν

Π.χ.

int a;
float b;
b = a = 10.96;

 Ποιες τιμές αποθηκεύονται στις μεταβλητές a και b???

Προγραμματισμός Ι

Παρατηρήσεις (IV)
 Σε περίπτωση υπερχείλισης (overflow), σε υπολογισμούς με τιμές
προσημασμένων μεταβλητών όπως π.χ. κατά την πρόσθεση δύο
μεγάλων ακεραίων, το αποτέλεσμα δεν θα έχει νόημα και δεν είναι
δυνατόν να προσδιοριστεί

 Για την αποφυγή υπερχείλισης μπορούμε να ελέγχουμε την


πιθανότητα να γίνει υπερχείλιση, πριν αυτή γίνει, π.χ. θεωρήστε
ότι οι a, b και c είναι όλες int μεταβλητές και η b είναι θετική

 Θα μπορούσαμε πριν αναθέσουμε την τιμή a+b στη c, να γράψουμε:

if(a > INT_MAX - b) /* Η INT_MAX ορίζεται στο <limits.h>. */


{
printf("Error: overflow case\n");
return;
}
c = a+b;

 Όταν πραγματοποιείτε αριθμητικές πράξεις, να είστε σίγουροι ότι


δεν θα προκύψει υπερχείλιση για να αποφύγετε δυσάρεστες
καταστάσεις Προγραμματισμός Ι
Αριθμητικοί τελεστές (1/2)
 Οι μαθηματικοί τελεστές +, -, *, / χρησιμοποιούνται για την
εκτέλεση των γνωστών μαθηματικών πράξεων

 Αν και οι δύο τελεστέοι είναι ακέραιοι, τότε ο τελεστής /


ισοδυναμεί με το πηλίκο της ακέραιας διαίρεσης (αποκόπτεται το
δεκαδικό μέρος)
 Π.χ., int a = 3, b = 2, c;
c = a/b;
η τιμή της έκφρασης a/b που ανατίθεται στη μεταβλητή c είναι 1
και όχι 1.5
 Αν, έστω και ένας απ’ τους τελεστέους είναι μεταβλητή ή
σταθερά δεκαδικού τύπου (float ή double), εκτελείται κανονικά
η διαίρεση και το δεκαδικό μέρος δεν αποκόπτεται, π.χ.:
int a = 8;
float b = 5;
το αποτέλεσμα a/b είναι 1.6, αφού η μεταβλητή b είναι float
και το αποτέλεσμα a/10.0 είναι 0.8, αφού και η σταθερά
(10.0) έχει δεκαδικό μέρος επίσης
Προγραμματισμός Ι

Αριθμητικοί τελεστές (2/2)


 Ο τελεστής % χρησιμοποιείται για τον υπολογισμό του υπολοίπου
της ακέραιας διαίρεσης δύο ακεραίων

 Π.χ. στο επόμενο παράδειγμα :

int a, b, c, d;
a = 11;
b = 3;
c = a%b;
d = a%c%b;

η τιμή της μεταβλητής c είναι 2 και n τιμή της μεταβλητής d


είναι 1

 ΠΡΟΣΟΧΗ!!! Ο τελεστής % μπορεί να εφαρμοστεί μόνο μεταξύ


ακεραίων αριθμών, αλλιώς ο μεταγλωττιστής εμφανίζει μήνυμα
λάθους
Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Αν ο δεξιός τελεστέος κατά τη χρήση του τελεστή / ή % έχει τιμή
0, το αποτέλεσμα είναι ακαθόριστο (undefined), π.χ. το
πρόγραμμα μπορεί ακόμα και να τερματίσει
 Αν οι τελεστέοι είναι αρνητικοί, το αποτέλεσμα εξαρτάται απ’ την
υλοποίηση του μεταγλωττιστή
 Π.χ., το αποτέλεσμα του: -8/6 μπορεί να είναι είτε -1 είτε -2,
κι εξαρτάται απ’ την υλοποίηση αν στρογγυλοποιεί προς τα πάνω ή
προς τα κάτω το αποτέλεσμα
 Ομοίως, το αποτέλεσμα του: -8%6 μπορεί να είναι είτε 2 είτε -2,
κι εξαρτάται επίσης απ’ την υλοποίηση
 Γι’ αυτόν τον λόγο, αν θέλετε να ελέγξετε αν το n είναι περιττός,
μη γράφετε κάτι σαν αυτό:
if((n % 2) == 1)
 Αν το n είναι περιττός, αλλά αρνητικός, το αποτέλεσμα σε
κάποιες υλοποιήσεις μεταγλωττιστών μπορεί να είναι -1, οπότε
ένας τέτοιος έλεγχος αποτυγχάνει, δηλαδή είναι πιο ασφαλές να
γράψετε:
if((n % 2) != 0)
Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 Οι πέντε αυτοί τελεστές λέγονται και «δυαδικοί» (binary), επειδή
απαιτούν δύο τελεστέους

 Η C παρέχει και τους μοναδιαίους (unary) τελεστές + και – που


απαιτούν μόνο έναν τελεστέο
 Ο μοναδιαίος τελεστής + δεν επιφέρει κάποιο αποτέλεσμα στον
τελεστέο
 Το αποτέλεσμα της χρήσης του μοναδιαίου τελεστή – είναι ο αντίθετος
του τελεστέου
 Όταν σε κάποια έκφραση εμπλέκονται και μοναδιαίοι και δυαδικοί
τελεστές + και –, καθορίζει ο μεταγλωττιστής το πώς
εφαρμόζονται, π.χ.:
int a = +20; /* Ο μοναδιαίος τελεστής + δεν έχει κάποια επίδραση. */
a = -10; /* Το – είναι εδώ ο μοναδιαίος τελεστής. */
int b = -(a-5); /* Το «μέσα –» αντιστοιχεί στον δυαδικό τελεστή ενώ
το «έξω –» στον μοναδιαίο, αντίστοιχα. */
-a; /* Δεν επιφέρει κάποιο αποτέλεσμα στη μεταβλητή a (δεν γίνεται
κάποια ανάθεση), άρα η τιμή της a παραμένει ως έχει. */

 Δηλ, η μεταβλητή b αποκτά την τιμή 15 και η a την τιμή -10


Προγραμματισμός Ι
Ο τελεστής αύξησης ++
 Ο τελεστής αύξησης ++ μπαίνει πριν ή μετά από το
όνομα μίας μεταβλητής

 Σε κάθε περίπτωση η τιμή της μεταβλητής αυξάνεται


κατά ένα

Προγραμματισμός Ι

Ο τελεστής αύξησης ++ στην εκχώρηση


 Όταν χρησιμοποιούμε τον τελεστή αύξησης ++ σε
κάποια εντολή εκχώρησης, τότε:
 Αν ο τελεστής χρησιμοποιείται μετά το όνομα της μεταβλητής,
τότε πρώτα χρησιμοποιείται η τρέχουσα τιμή της μεταβλητής
και μετά αυτή αυξάνεται κατά ένα
 Αν ο τελεστής χρησιμοποιείται πριν το όνομα της μεταβλητής,
τότε πρώτα αυξάνεται η τιμή της μεταβλητής κατά ένα και
μετά αυτή χρησιμοποιείται

Έξοδος: a = 5 b = 4 Έξοδος: a = 5 b = 5
Προγραμματισμός Ι
Ο τελεστής μείωσης --
 Ο τελεστής μείωσης -- μπαίνει πριν ή μετά από το
όνομα μίας μεταβλητής

 Σε κάθε περίπτωση η τιμή της μεταβλητής μειώνεται


κατά ένα

 Για τον τελεστή μείωσης ισχύουν ακριβώς οι ίδιοι


κανόνες που παρουσιάστηκαν για τον τελεστή αύξησης
Προγραμματισμός Ι

Παραδείγματα
 Ποια είναι η έξοδος του καθενός προγράμματος?

Με το ++a πρώτα αυξάνει την τρέχουσα τιμή


της a, και στη συνέχεια η νέα αυτή τιμή
χρησιμοποιείται. Με το b-- χρησιμοποιείται
πρώτα η τρέχουσα τιμή της b και μετά
μειώνεται. Τέλος, με το --c η τιμή της c
Το πρόγραμμα εμφανίζει αρχικά 3.45, και μετά πρώτα μειώνεται και η νέα τιμή χρησιμοποιείται
η τιμή της μεταβήτής a αυξάνεται. στην έκφραση. Συνεπώς, το αποτέλεσμα της
έκφρασης είναι: 2-2+2 = 2 ενώ οι τιμές των
Στη συνέχεια, η τιμή της a πρώτα αυξάνεται,
a, b, και c γίνονται 2, 1, and 2, αντίστοιχα.
συνεπώς, το πρόγραμμα εμφανίζει 5.45

Προγραμματισμός Ι
Παρατηρήσεις
 Μην εφαρμόζετε τους τελεστές ++ και –- ταυτόχρονα στην ίδια
μεταβλητή και μην δημιουργείτε εκφράσεις των οποίων η τελική
τιμή εξαρτάται από τη σειρά που μπορεί να εφαρμοστούν οι επί
μέρους τελεστές

 Π.χ., το αποτέλεσμα της έκφρασης:

a * a++;

εξαρτάται απ’ την υλοποίηση του μεταγλωττιστή, δηλαδή ο ίδιος


ο μεταγλωττιστής θα καθορίσει αν η τιμή της μεταβλητής a
πρέπει πρώτα να αυξηθεί και μετά να γίνει ο πολλαπλασιασμός
ή το αντίστροφο

Προγραμματισμός Ι

Τελεστές Σύγκρισης (Ι)


 Οι τελεστές σύγκρισης >, >=, <, <=, !=,==, χρησιμοποιούνται
για τη σύγκριση των τιμών που έχουν δύο εκφράσεις

 Συνήθως χρησιμοποιούνται σε εντολές ελέγχου (π.χ. στην εντολή


if) και σε επαναληπτικούς βρόχους (π.χ. στις εντολές for,
while ή do-while)

 Π.χ.

Προγραμματισμός Ι
Τελεστές Σύγκρισης (ΙΙ)
 Μία έκφραση χαρακτηρίζεται αληθής (true), όταν η τιμή της είναι
διαφορετική από το μηδέν, ενώ - αν είναι μηδέν- χαρακτηρίζεται
ψευδής (false)

 To αποτέλεσμα της έκφρασης στην οποία χρησιμοποιείται κάποιος


τελεστής σύγκρισης είναι 1 (όταν η έκφραση είναι αληθής – true)
ενώ το αποτέλεσμα είναι 0 (όταν η έκφραση είναι ψευδής – false)

 Π.χ. το αποτέλεσμα της έκφρασης (a > 10) είναι ένα (1) μόνο
αν η τιμή της μεταβλητής a είναι μεγαλύτερη από το 10, αλλιώς
είναι μηδέν (0)

 Ποια είναι η έξοδος


του διπλανού
προγράμματος:

Έξοδος: 1 0

Προγραμματισμός Ι

Παρατηρήσεις
 Μην συγχέετε τον τελεστή ελέγχου ισότητας (==) με τον τελεστή
εκχώρησης (=)

 Ο τελεστής == χρησιμοποιείται για να ελέγξουμε αν δύο


εκφράσεις έχουν την ίδια τιμή, ενώ ο τελεστής = χρησιμοποιείται
για να αναθέσουμε μία τιμή σε μία μεταβλητή

 Π.χ., η τιμή της έκφρασης:


a == 10

είναι 1, μόνο η τιμή της μεταβλητής a είναι 10, αλλιώς είναι 0

ενώ η τιμή της έκφρασης:

a = 10
είναι πάντοτε 1 και η έκφραση εκχωρεί την τιμή 10 στη
μεταβλητή a (η εκχώρηση μη μηδενικής τιμής είναι πάντα αληθής
έκφραση, ενώ η εκχώρηση του μηδενός είναι πάντα ψευδής
έκφραση, αντίστοιχα)
Προγραμματισμός Ι
Ο τελεστής !
 Ο τελεστής ! είναι μοναδιαίος, δηλαδή εφαρμόζεται σε
έναν μόνο τελεστέο

 Αν μία έκφραση exp είναι αληθής (δηλαδή έχει μη


μηδενική τιμή), τότε το αποτέλεσμα της πράξης !exp
είναι μηδέν (0)

 Αν μία έκφραση exp είναι ψευδής (δηλαδή έχει


μηδενική τιμή), τότε το αποτέλεσμα της πράξης !exp
είναι ένα (1)

Προγραμματισμός Ι

Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος:

Έξοδος: Num = 0

 Ποια είναι η έξοδος του παρακάτω προγράμματος:

Έξοδος: Num = 1

Προγραμματισμός Ι
Παρατηρήσεις
 Συνήθως, ο τελεστής ! χρησιμοποιείται σε συνθήκες
ελέγχου στην εντολή if

 Π.χ.
η εντολή:
if(!a)
είναι ισοδύναμη με
if(a == 0)

και η εντολή:
if(a)
είναι ισοδύναμη με
if(a != 0)

Προγραμματισμός Ι

Συνδυαστικοί τελεστές
 Οι συνδυαστικοί τελεστές χρησιμοποιούνται για να γραφούν μαθηματικές
εκφράσεις με πιο σύντομο τρόπο, βάσει του παρακάτω τύπου:

exp1 op= exp2;

όπου συνήθως ο τελεστής op είναι κάποιος από τους αριθμητικούς


τελεστές +, -, *, %, / ή κάποιος από τους τελεστές bit που θα δούμε
παρακάτω (&, ^, |, <<, >>). Η παραπάνω έκφραση είναι ισοδύναμη με:

exp1 = exp1 op (exp2);

 Π.χ. η έκφραση:
a += b;
είναι ισοδύναμη με:
a = a + b;

ενώ η έκφραση:
a *= 3;
είναι ισοδύναμη με:
a = a * 3;

Προγραμματισμός Ι
Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος:

Έξοδος: Num = 2

Προγραμματισμός Ι

Λογικοί τελεστές
 Ο τελεστής &&
 Η τιμή μίας έκφρασης που περιέχει τον τελεστή && είναι ένα (1),
δηλαδή αληθής, μόνο αν όλοι οι όροι της έκφρασης είναι αληθείς
 η τιμή της έκφρασης που περιέχει τον τελεστή && είναι μηδέν (0),
δηλαδή ψευδής, αν έστω και ένας όρος έχει ψευδή τιμή
 Ο τελεστής && εφαρμόζει δηλαδή τη λογική πράξη ΚΑΙ (λογική
πράξη AND) μεταξύ των όρων στους οποίους εφαρμόζεται
 Ο τελεστής ||
 Μία έκφραση που περιέχει τον τελεστή || είναι ένα (1), δηλαδή
αληθής, αν έστω και ένας όρος της έκφρασης είναι αληθής
 Μία έκφραση που περιέχει τον τελεστή || είναι μηδέν (0), δηλαδή
ψευδής, αν κανένας όρος της έκφρασης δεν είναι αληθής
 Ο τελεστής || εφαρμόζει δηλαδή τη λογική πράξη Ή (λογική
πράξη OR) μεταξύ των όρων στους οποίους εφαρμόζεται

Προγραμματισμός Ι
Παραδείγματα
 Η έκφραση (10 == 10) && (5 > 3) είναι αληθής, γιατί και οι
δύο όροι της έκφρασης είναι αληθείς
 Αν γράφαμε:
a = (10 == 10) && (5 > 3); τότε η τιμή της μεταβλητής a
θα γινόταν ίση με 1

 Η έκφραση (10 == 10) && (5 > 3) && (13 < 8) είναι


ψευδής, γιατί υπάρχει ένας όρος που έχει ψευδή τιμή
 Αν γράφαμε:
a = (10 == 10) && (5 > 3) && (13 < 8); τότε η τιμή της
μεταβλητής a θα γινόταν ίση με 0

Προγραμματισμός Ι

Παραδείγματα
 Η έκφραση (10 == 10) || (3 > 5) είναι αληθής, γιατί ένας
όρος της έκφρασης είναι αληθής
 Αν γράφαμε:
a = (10 == 10) || (3 > 5); τότε η τιμή της μεταβλητής a
θα γινόταν ίση με 1

 Η έκφραση (10 != 10) || (3 > 5) είναι ψευδής, γιατί δεν


υπάρχει κάποιος όρος που να είναι αληθής
 Αν γράφαμε:
a = (10 != 10) || (3 > 5); τότε η τιμή της μεταβλητής a
θα γινόταν ίση με 0

Προγραμματισμός Ι
Παρατηρήσεις
 Αν ο όρος που ελέγχεται σε μία έκφραση με τον τελεστή && έχει
ψευδή τιμή, τότε ο μεταγλωττιστής δεν ελέγχει τους υπόλοιπους
όρους και θέτει κατευθείαν την τιμή της συνολικής έκφρασης ίση
με 0

 Αντίστοιχα, αν ο όρος που ελέγχεται σε μία έκφραση με τον


τελεστή || είναι αληθής, τότε ο μεταγλωττιστής δεν ελέγχει τους
υπόλοιπους όρους και θέτει κατευθείαν την τιμή της συνολικής
έκφρασης ίση με 1

Προγραμματισμός Ι

Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος?

Έξοδος: 1
0

Προγραμματισμός Ι
Ο τελεστής ,
 Ο τελεστής κόμμα (,) διαχωρίζει επί μέρους εκφράσεις οι
οποίες αποτελούν μία έκφραση:

exp1, exp2, exp3,...

 Επειδή ο τελεστής κόμμα (,) παρουσιάζει αριστερή


συσχέτιση (είναι left associative) οι επί μέρους εκφράσεις
εκτελούνται διαδοχικά από αριστερά προς τα δεξιά

 Δηλαδή, πρώτη εκτελείται η exp1, μετά η exp2, μετά η


exp3, έως και την τελευταία έκφραση

 Ο τύπος και η τιμή ολόκληρης της έκφρασης είναι οι


αντίστοιχοι της τελευταίας εκτελεσθείσας έκφρασης

Προγραμματισμός Ι

Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος?

Έξοδος: 50

Προγραμματισμός Ι
Παρατηρήσεις
 Δεδομένου ότι η χρήση του τελεστή κόμμα (,) μπορεί να οδηγήσει
σε δυσνόητο και περίπλοκο κώδικα, καλό θα ήταν να αποφεύγεται

 O τελεστής κόμμα (,) χρησιμοποιείται κυρίως στην εντολή for ,


π.χ.:

int a, b;
for(a = 1, b = 2; b < 10; a++, b++)

 Στο παραπάνω κομμάτι κώδικα στην πρώτη έκφραση εντός της


παρένθεσης, πρώτα η τιμή της a γίνεται 1 και μετά η τιμή της b
γίνεται 2, ενώ, στην τρίτη έκφραση εντός της παρένθεσης πρώτα
εκτελείται η εντολή a++ και μετά η b++

 Σημειώστε ότι τα κόμματα που χρησιμοποιούνται για να χωρίσουν


τις παραμέτρους μίας συνάρτησης ή τις μεταβλητές κατά τη
δήλωση μεταβλητών του ίδιου τύπου δεν είναι «τελεστές κόμμα»,
αλλά απλοί διαχωριστές (separators)
Προγραμματισμός Ι

Ο τελεστής sizeof
 Ο τελεστής sizeof υπολογίζει τις οκτάδες που δεσμεύει στη μνήμη του
υπολογιστή ο τύπος δεδομένων ή η μεταβλητή που δηλώνεται στις
παρενθέσεις του
 Π.χ.

Προγραμματισμός Ι
Ο τύπος enum (Ι)
 Ο τύπος απαρίθμησης enum (enumeration type) χρησιμοποιείται
για να οριστεί ένα σύνολο ακεραίων με συγκεκριμένα ονόματα και
σταθερές τιμές

 Στην απλή περίπτωση δηλώνεται ως εξής:

enum όνομα {λίστα απαρίθμησης};

 Το αναγνωριστικό όνομα είναι προαιρετικό και δηλώνει το όνομα


της απαρίθμησης, π.χ. η εντολή:

enum seasons {AUTUMN, WINTER, SPRING, SUMMER};

δηλώνει τον τύπο απαρίθμησης seasons και τις ακέραιες


σταθερές AUTUMN, WINTER, SPRING και SUMMER

Προγραμματισμός Ι

Ο τύπος enum (ΙΙ)


 Εξ’ορισμού, κατά τη δήλωση ενός τύπου απαρίθμησης, η τιμή της
πρώτης σταθεράς αρχικοποιείται με 0

 Αν σε κάποια σταθερά δεν αποδίδεται τιμή, η τιμή της γίνεται


ίση με την τιμή της προηγούμενης σταθεράς αυξημένη κατά ένα

 Επομένως, στο προηγούμενο παράδειγμα που δεν αποδίδονται


τιμές στις σταθερές, οι τιμές των σταθερών AUTUMN, WINTER,
SPRING και SUMMER γίνονται 0, 1, 2 και 3, αντίστοιχα

 Σε περίπτωση που στο προηγούμενο παράδειγμα θα θέλαμε να


αποδώσουμε συγκεκριμένες τιμές στις σταθερές, θα μπορούσαμε
να δηλώσουμε τον τύπο απαρίθμησης π.χ. ως εξής:

enum seasons {AUTUMN=10, WINTER, SPRING=30, SUMMER};

 Στο παράδειγμα αυτό οι τιμές των σταθερών AUTUMN, WINTER,


SPRING και SUMMER γίνονται 10, 11, 30 και 31, αντίστοιχα
Προγραμματισμός Ι
Δήλωση μεταβλητής τύπου enum
 Για να δηλώσουμε μία μεταβλητή σύμφωνα με έναν ήδη δηλωμένο
τύπο απαρίθμησης γράφουμε:

enum όνομα_τύπου λίστα_μεταβλητών;

 Π.χ. με την εντολή:

enum seasons s1, s2;

δηλώνουμε τις s1 και s2 σαν μεταβλητές απαρίθμησης του τύπου


seasons του προηγούμενου παραδείγματος

 Εναλλακτικά, μπορούμε να δηλώσουμε τις μεταβλητές μαζί με τη


δήλωση του τύπου απαρίθμησης, π.χ.:

enum seasons {AUTUMN, WINTER, SPRING, SUMMER} s1, s2;

Προγραμματισμός Ι

Παρατηρήσεις
 Θυμηθείτε ότι η οδηγία #define αποτελεί έναν εναλλακτικό τρόπο
δήλωσης συμβολικών ονομάτων που αντιστοιχούν σε συγκεκριμένες
τιμές

 Η κύρια διαφορά τους είναι ότι ο τύπος enum ομαδοποιεί τις


σταθερές, ώστε να φαίνεται ότι χαρακτηρίζουν ένα σύνολο τιμών

 Αυτό που πρέπει να θυμάστε με τις μεταβλητές τύπου


απαρίθμησης (enum) είναι ότι η γλώσσα προγραμματισμού C τις
χειρίζεται σαν ακέραιες μεταβλητές ενώ τα ονόματα της λίστας
απαρίθμησης σαν ακέραιες σταθερές

enum seasons {AUTUMN, WINTER, SPRING, SUMMER} s1, s2;

Προγραμματισμός Ι
Οι τελεστές bit
 Οι τελεστές bit χρησιμοποιούνται για το χειρισμό των bits μίας
ακέραιας μεταβλητής ή σταθεράς

 Η τιμή ενός bit – ως γνωστόν – μπορεί να είναι 0 ή 1

 Ο υπολογισμός της τιμής μίας έκφρασης που περιέχει τελεστές bit


γίνεται με την εφαρμογή τους στα αντίστοιχα bits των τελεστέων

 Οι τελεστές bit είναι οι εξής:


 Ο τελεστής AND &
 Ο τελεστής OR |
 Ο τελεστής XOR ^
 Ο τελεστής NOT ~

 Όταν εκτελείτε πράξεις με τελεστές bit είναι ασφαλέστερο να


δηλώνετε ως unsigned τις αντίστοιχες μεταβλητές αλλιώς, να
λαμβάνετε υπόψη σας το bit προσήμου, όταν κάνετε τους
υπολογισμούς σας
Προγραμματισμός Ι

Ο τελεστής &
 Ο τελεστής & εφαρμόζει τη λογική πράξη AND (λογική πράξη
ΚΑΙ) στα bits των δύο τελεστέων και θέτει το bit εξόδου στο 1
μόνο αν τα αντίστοιχα bits και στους δύο τελεστέους είναι 1,
αλλιώς, το bit εξόδου τίθεται στο 0
 Π.χ. το αποτέλεσμα της πράξης 19 & 2 είναι 2

 Ο τελεστής & χρησιμοποιείται συχνά για να μηδενίσει, δηλ. να


θέσει ίσα με το μηδέν (0), μια σειρά από bits
 Π.χ., η εντολή: a = a & 3; κάνει όλα τα bits της a εκτός από τα
δύο «λιγότερα σημαντικά» bits

 Μην συγχέετε τους τελεστές && και & (π.χ., αν a=1 και b=2, το
αποτέλεσμα της έκφρασης a && b είναι 1, ενώ το αποτέλεσμα
της έκφρασης a & b είναι 0)
Προγραμματισμός Ι
Ο τελεστής |
 Ο τελεστής | εφαρμόζει τη λογική πράξη OR (λογική πράξη Ή)
στα bits των δύο τελεστέων και θέτει το bit εξόδου στο 0 μόνο
αν τα αντίστοιχα bits και στους δύο τελεστέους είναι 0, αλλιώς,
το bit εξόδου τίθεται στο 1
 Π.χ. το αποτέλεσμα της πράξης 19 | 6 είναι 23

 Ο τελεστής | χρησιμοποιείται συχνά για να θέσει ίσα με το ένα


(1) μια σειρά από bits
 Π.χ., η εντολή: a = a | 3; κάνει τα δύο «λιγότερα σημαντικά»
bits της μεταβλητής a ίσα με ένα

 Μην συγχέετε τους τελεστές || και | (π.χ., αν a=1 και b=2,


το αποτέλεσμα της έκφρασης a || b είναι 1, ενώ το
αποτέλεσμα της έκφρασης a | b είναι 3)
Προγραμματισμός Ι

Ο τελεστής ^
 Ο τελεστής ^ εφαρμόζει τη λογική πράξη XOR (eXclusive OR ,
αποκλειστικό Ή) στα bits των δύο τελεστέων και θέτει το bit
εξόδου στο 1 μόνο αν τα αντίστοιχα bits και στους δύο
τελεστέους είναι διαφορετικά μεταξύ των, αλλιώς, το bit τίθεται
στο 0

 Π.χ. το αποτέλεσμα της πράξης 19 ^ 6 είναι 21

Προγραμματισμός Ι
Ο τελεστής ~
 Ο τελεστής συμπληρώματος ~ είναι μοναδιαίος, δηλαδή
εφαρμόζεται σε έναν τελεστέο και εφαρμόζει τη λογική πράξη
NOT (λογική πράξη ΔΕΝ)

 Συγκεκριμένα, αντιστρέφει κάθε bit στον τελεστέο του,


αλλάζοντας όλα τα 0 σε 1, και αντιστρόφως

 Π.χ. σε ένα 32-bit σύστημα το αποτέλεσμα της πράξης ~19


είναι (232 – 1)- 19

Προγραμματισμός Ι

Οι τελεστές ολίσθησης
 Οι τελεστές ολίσθησης (>> και <<) μετατοπίζουν τα bits μίας
ακέραιας μεταβλητής ή σταθεράς κατά ένα συγκεκριμένο αριθμό
θέσεων, όπως δείχνουν τα «νοητά βέλη»

 Ο τελεστής >> μετατοπίζει τα bits της μεταβλητής προς τα


δεξιά, όπως δηλαδή δείχνουν τα «νοητά βέλη»

 Ο τελεστής << μετατοπίζει τα bits της μεταβλητής προς τα


αριστερά, όπως δηλαδή δείχνουν τα «νοητά βέλη»

 Επειδή η εφαρμογή των τελεστών ολίσθησης σε αρνητικούς


αριθμούς εξαρτάται από τον μεταγλωττιστή, είναι ασφαλέστερο να
τους χρησιμοποιείτε σε θετικούς ακεραίους ή unsigned
μεταβλητές

Προγραμματισμός Ι
Ο τελεστής >>
 Η έκφραση i >> n μετατοπίζει τα bits της μεταβλητής i κατά
n θέσεις δεξιά και τοποθετεί μηδενικά στα n υψηλότερης τάξης
bits της μεταβλητής

 Π.χ. ποια θα είναι η τιμή της μεταβλητής a κατά την εκτέλεση


του παρακάτω κώδικα;

unsigned int a, b = 35;


a = b >> 2;

a = 8 , διότι: 00100011 >> 2 = 00001000

 Συγκεκριμένα, χάθηκαν τα τελευταία bits 1 και 1 του αρχικού


αριθμού (35) και τοποθετήθηκαν τα bits 0 και 0 στην έβδομη
και όγδοη θέση, αντίστοιχα

 Και ποια είναι η τιμή της μεταβλητής b ???

Προγραμματισμός Ι

Ο τελεστής <<
 Η έκφραση i << n μετατοπίζει τα bits της μεταβλητής i κατά
n θέσεις αριστερά και τοποθετεί μηδενικά στα n χαμηλότερης
τάξης bits της μεταβλητής

 Π.χ. ποια θα είναι η τιμή της μεταβλητής a κατά την εκτέλεση


του παρακάτω κώδικα;

unsigned int a, b = 35;


a = b << 2;

a = 140 , διότι: 00100011 << 2 = 0010001100

 Συγκεκριμένα, τα bits του αρχικού αριθμού (35) ολίσθησαν δύο


θέσεις αριστερά και τοποθετήθηκαν τα bits 0 και 0 στην
πρώτη και στη δεύτερη θέση, αντίστοιχα

 Και ποια είναι η τιμή της μεταβλητής b ???

Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Όταν χρησιμοποιείται ο τελεστής << και το αποτέλεσμα
αποθηκεύεται σε μία μεταβλητή, ο τύπος δεδομένων της μεταβλητής
πρέπει να είναι τέτοιος ώστε να μπορεί να αποθηκευτεί η τελική τιμή

 Π.χ. Ποια είναι η έξοδος του προγράμματος ???

Περιμένατε να τυπωθεί: Value = 256


αλλά τυπώθηκε Value = 0
Γιατί???
Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 Επειδή η θέση ενός bit αντιστοιχεί σε μία δύναμη του 2:

 η ολίσθηση ενός θετικού ακεραίου κατά n θέσεις δεξιά (>>)


ισοδυναμεί με τη διαίρεση της τιμής του με 2n

 Π.χ. θυμηθείτε 35 >> 2 = 8

 η ολίσθηση ενός θετικού ακεραίου n θέσεις αριστερά (<<)


ισοδυναμεί με τον πολλαπλασιασμό της τιμής του με 2n

 Π.χ. θυμηθείτε 35 << 2 = 140

 Να θυμάστε ότι είναι ασφαλέστερο να χρησιμοποιείτε τους τελεστές


bit σε unsigned μεταβλητές

Προγραμματισμός Ι
Προτεραιότητα Τελεστών
 Κάθε τελεστής χαρακτηρίζεται από μία προτεραιότητα

 Σε μία έκφραση που περιέχονται περισσότεροι του ενός τελεστές, οι πράξεις


εκτελούνται σύμφωνα με τη σειρά προτεραιότητας του κάθε τελεστή

 Π.χ. το αποτέλεσμα της πράξης:


7 + 5 * 3 – 1 είναι 21,
γιατί ο τελεστής * έχει μεγαλύτερη προτεραιότητα από τους τελεστές + και -,
οπότε πρώτα εκτελείται η πράξη 5*3 = 15 και όχι οι πράξεις 7+5 ή 3-1

 Αν μία έκφραση περιέχει διαδοχικούς τελεστές με την ίδια προτεραιότητα,


τότε οι πράξεις εκτελούνται σύμφωνα με τη συσχέτισή τους (associativity),
δηλαδή από αριστερά προς τα δεξιά ή αντίστροφα

 Π.χ. το αποτέλεσμα της πράξης:


7 * 4 / 2 * 5 είναι 70,
γιατί, αφού οι τελεστές * και / έχουν την ίδια προτεραιότητα και συσχέτιση
από αριστερά προς τα δεξιά, τότε:
πρώτα εκτελείται ο πολλαπλασιασμός 7*4 = 28, μετά η διαίρεση 28/2 = 14
και μετά ο πολλαπλασιασμός 14*5 = 70
Προγραμματισμός Ι

Πίνακας Προτεραιοτήτων
Θέση Τελεστές Συσχέτιση
1 ()(παρενθέσεις-κλήση συνάρτησης) [] -> . αριστερά προς δεξιά
++(επιθεματική αύξηση) --(επιθεματική μείωση)
2 ++(προθεματική αύξηση) --(προθεματική μείωση) ! ~ δεξιά προς αριστερά
*(έμμεση αναφορά) &(διεύθυνση) +(μοναδιαίο +) –
(μοναδιαίο -) (προσαρμογή τύπου) sizeof
3 *(πολλαπλασιασμός) / % αριστερά προς δεξιά

4 +(πρόσθεση) -(αφαίρεση) αριστερά προς δεξιά

5 << >> αριστερά προς δεξιά

6 < <= > >= αριστερά προς δεξιά

7 == != αριστερά προς δεξιά

8 & αριστερά προς δεξιά

9 ^ αριστερά προς δεξιά

10 | αριστερά προς δεξιά

11 && αριστερά προς δεξιά

12 || αριστερά προς δεξιά

13 ?: δεξιά προς αριστερά

14 = += -= *= /= %= &= ^= |= <<= >>= δεξιά προς αριστερά

15 , αριστερά προς δεξιά

Προγραμματισμός Ι
Παρατηρήσεις
 Όπως φαίνεται στον πίνακα προτεραιοτήτων, κάθε τελεστής
χαρακτηρίζεται από μία προτεραιότητα

 Αν μία έκφραση περιέχει διαδοχικούς τελεστές με την ίδια


προτεραιότητα, τότε οι πράξεις εκτελούνται σύμφωνα με την
συσχέτισή τους

 Προτείνεται η χρήση παρενθέσεων ( ), ακόμα και όταν δεν


χρειάζονται, έτσι ώστε ο κώδικας να είναι πιο ευανάγνωστος και
να γίνεται σαφέστερη η σειρά εκτέλεσης των πράξεων

 Είναι πιο σαφές να γράψουμε:


π.χ.1) a = 7+(5*3)-1; αντί a = 7+5*3-1;

π.χ.2) if((a >> 2) == 10) αντί if(a >> 2 == 10)

 Οι τελεστές :? , [], ->, . , &, * θα παρουσιαστούν σε επόμενες


διαλέξεις (έλεγχος προγράμματος/πίνακες/δείκτες/δομές)
Προγραμματισμός Ι

Προγραμματισμός Ι

Έλεγχος Προγράμματος
Η εντολή if (Ι)
 Η εντολή if είναι μία από τις βασικότερες δομές ελέγχου ροής
στη C, αλλά και στις περισσότερες γλώσσες προγραμματισμού

 Με την εντολή if γίνεται δυνατή η επιλεκτική εκτέλεση ενός


τμήματος κώδικα, ανάλογα με την τιμή μίας συνθήκης

 Γενική σύνταξη της εντολής if (στην πιο απλή της μορφή):

if(συνθήκη)
{
... /* ομάδα εντολών */
}

Προγραμματισμός Ι

Η εντολή if (ΙΙ)
 Αν η συνθήκη είναι αληθής (true), τότε εκτελούνται οι εντολές
που περικλείονται στα άγκιστρα {...}

 Αν η συνθήκη δεν είναι αληθής, δηλαδή αν η συνθήκη είναι


ψευδής (false), τότε το μπλοκ των εντολών που περικλείεται στα
άγκιστρα παρακάμπτεται και συνεπώς δεν εκτελείται

Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Αν το μπλοκ εντολών περιέχει μόνο μία εντολή, τότε τα άγκιστρα
μπορούν να παραλειφθούν

 Π.χ.

 Αν, βέβαια, το μπλοκ εντολών περιέχει περισσότερες από μία


εντολές, τότε τα άγκιστρα είναι απαραίτητα

Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 ΠΡΟΣΟΧΗ!!!
 Μην βάζετε το ελληνικό ερωτηματικό ; στο τέλος της if
εντολής, γιατί ουσιαστικά το ερωτηματικό τερματίζει στο σημείο
εκείνο την εντολή if

 Π.χ. τί εμφανίζει το παρακάτω παράδειγμα ???

 και τί αυτό ???

 Στην οθόνη εμφανίζεται το μήνυμα x is positive ανεξάρτητα


από την τιμή της μεταβλητής x
Προγραμματισμός Ι
Παρατηρήσεις (ΙΙΙ)
 ΠΡΟΣΟΧΗ!!!
 Μην συγχέετε τον τελεστή ελέγχου ισότητας == (διπλό ίσον) με
τον τελεστή εκχώρησης = (μονό ίσον)

 Το παρακάτω πρόγραμμα εμφανίζει στην οθόνη Yes, παρόλο που


η αρχική τιμή της μεταβλητής x είναι -10 (αφού η συνθήκη
x=-20 είναι πάντα αληθής, δεδομένου ότι πρόκειται για ανάθεση
μη μηδενικής τιμής)

 Για να είχαμε «σωστό χειρισμό» στη συνθήκη if, η συνθήκη θα


έπρεπε να γραφεί ως if(x == -20), δηλαδή με διπλό ίσον και
όχι με μονό

Προγραμματισμός Ι

Παρατηρήσεις (ΙV)
 ΠΡΟΣΟΧΗ!!!
 H εκχώρηση μίας μη μηδενικής τιμής σε μία μεταβλητή ισοδυναμεί
με αληθή συνθήκη, ενώ η εκχώρηση μηδενικής τιμής ισοδυναμεί
με ψευδή συνθήκη

 Π.χ. τι εμφανίζει το παρακάτω κομμάτι κώδικα;

 Δεν εμφανίζεται τίποτα, δεδομένου ότι η συνθήκη είναι ψευδής


(έχουμε εκχώρηση της μηδενικής τιμής)

 Για να είχαμε «σωστό χειρισμό» στη συνθήκη if, η συνθήκη θα


έπρεπε να γραφεί ως if(x == 0), δηλαδή με διπλό ίσον και όχι
με μονό

Προγραμματισμός Ι
Παρατηρήσεις (V)
 Η έκφραση:

if(x) είναι ισοδύναμη με if(x != 0)

 H έκφραση:

if(!x) είναι ισοδύναμη με if(x == 0)

 Η εντολή if μπορεί προαιρετικά να συμπληρώνεται με την εντολή


else, όπως θα δούμε στη συνέχεια

Προγραμματισμός Ι

Παρατηρήσεις (VΙ)
 Ορισμένοι προγραμματιστές προτιμούν να γράφουν πρώτα τη
σταθερά, όταν θέλουν να συγκρίνουν την τιμή μιας μεταβλητής με
μία σταθερά, π.χ. :
if(-20 == x) /* Αντί για if(x == -20) */
 Ο λόγος είναι ότι σε περίπτωση που ξεχάσουν το ένα «ίσον» (=),
ο μεταγλωττιστής θα εμφανίσει μήνυμα λάθους, δεδομένου ότι δεν
επιτρέπεται να ανατεθεί η τιμή μιας μεταβλητής σε μία σταθερά

 Ένα επίσης συνηθισμένο λάθος είναι η «ανάποδη γραφή» του


τελεστή != σε κάποια σύγκριση, π.χ.:
Με την «ανάποδη γραφή»
εφαρμόζεται ο τελεστής ! στη
στεθερά 20, άρα το i γίνεται
0, άρα η συνθήκη ψευδής.
Συνεπώς, το πρόγραμμα
εμφανίζει Two και 0

Προγραμματισμός Ι
Η εντολή if...else (I)
 Όταν θέλουμε να προσδιορίσουμε μία ομάδα εντολών που θα
εκτελεστεί όταν μία συνθήκη είναι αληθής (true) και μία άλλη ομάδα
εντολών που θα εκτελεστεί όταν η συνθήκη αυτή είναι ψευδής
(false), τότε χρησιμοποιούμε την εντολή ελέγχου if...else

 Γενική σύνταξη της εντολής if...else:

if(συνθήκη)
{
... /* ομάδα εντολών A */
}
else
{
... /* ομάδα εντολών B */
}
Προγραμματισμός Ι

Η εντολή if...else (II)


 Όταν η συνθήκη είναι αληθής (true), τότε εκτελείται η ομάδα
εντολών Α (δηλ. οι εντολές που περιέχονται ανάμεσα στα άγκιστρα
του if), ενώ όταν η συνθήκη είναι ψευδής (false), τότε εκτελείται
η ομάδα εντολών B (δηλ. οι εντολές που περιέχονται ανάμεσα στα
άγκιστρα του else)

 Π.χ.

 Το παραπάνω κομμάτι κώδικα εμφανίζει Two, αφού το x είναι ίσο


με -3, που είναι μικρότερο ή ίσο του 0

Προγραμματισμός Ι
Παρατηρήσεις
 Θυμηθείτε ότι στην περίπτωση της εντολής if, αν η ομάδα εντολών
περιέχει μόνο μία εντολή, τότε τα άγκιστρα μπορούν να
παραλειφθούν.

 To ίδιο ισχύει και στην περίπτωση της εντολής if...else

 Δηλαδή, το προηγούμενο παράδειγμα θα μπορούσε να γραφεί και ως


εξής:

 Αν, βέβαια, κάποια από τις ομάδες εντολών περιέχει περισσότερες


από μία εντολές, τότε τα άγκιστρα είναι απαραίτητα στο
συγκεκριμένο μπλοκ

Προγραμματισμός Ι

Ένθετες if εντολές (I)


 Στη γενικότερη περίπτωση, τα
μπλοκ εντολών των if και
else εντολών επιτρέπεται να
περιέχουν και άλλες if και
else εντολές, οι οποίες με τη
σειρά τους μπορεί να περιέχουν
και άλλες, κ.ο.κ.

 Όταν υπάρχει μία if εντολή


μέσα σε μία άλλη, τότε αυτή η
if εντολή ονομάζεται ένθετη ή
φωλιασμένη (nested)

Παράδειγμα με δύο ένθετες if


Αφού το a είναι μεγαλύτερο

εντολές του 5, το πρόγραμμα


εμφανίζει: 1 3
Προγραμματισμός Ι
Ένθετες if εντολές (IΙ)
 Στην περίπτωση που ένα πρόγραμμα περιέχει ένθετες if
εντολές, ο κανόνας είναι ότι κάθε else εντολή συνδέεται με την
αμέσως προηγούμενη if εντολή που υπάρχει στην ίδια ομάδα
εντολών (δηλ. ανάμεσα στα ίδια άγκιστρα), αρκεί αυτή να μη
σχετίζεται με άλλη else εντολή

?
?

 Όταν γίνεται χρήση ένθετων εντολών if προτείνεται η χρήση


των αγκίστρων, για να είναι πιο ξεκάθαρη η σχέση μεταξύ των
εντολών else και if (ιδιαίτερα στην περίπτωση που στο
πρόγραμμά σας χρησιμοποιείτε μεγάλο αριθμό από if και else
εντολές) Προγραμματισμός Ι

Ένθετες if εντολές (IΙΙ)


 Στο διπλανό πρόγραμμα, η
εντολή else printf("3\n");
αντιστοιχεί στην πλησιέστερη if
εντολή, που είναι η
if(c == 40)

 Όμως, η τελική εντολή


else printf("4\n"); δεν
αντιστοιχίζεται με την
πλησιέστερη if εντολή, που
είναι η if(b == 20), γιατί δεν
ανήκουν στο ίδιο μπλοκ

 Η εντολή αυτή συνδέεται με την


εντολή if(a > 5)

 Άρα, η ποια είναι η έξοδος του


προγράμματος ??? Έξοδος: 1 3
Προγραμματισμός Ι
Προτεινόμενη σύνταξη ένθετων if εντολών
 Μία πολύ συνηθισμένη χρήση των ένθετων εντολών if
στηρίζεται στην ακόλουθη σύνταξη:
 Βάσει αυτής της
σύνταξης, όταν βρεθεί
μία συνθήκη που να
είναι αληθής, τότε
εκτελείται η ομάδα
εντολών που σχετίζεται
με αυτή και οι
υπόλοιπες else if
συνθήκες αγνοούνται

 Δηλαδή, η εκτέλεση του


κώδικα συνεχίζει με την
πρώτη εντολή που
υπάρχει μετά την
τελευταία else εντολή
Προγραμματισμός Ι

Παράδειγμα

Αν ο χρήστης εισάγει 1, το πρόγραμμα εμφανίζει One


Αν ο χρήστης εισάγει 2, το πρόγραμμα εμφανίζει Two
Αν εισαχθεί οποιοσδήποτε ακέραιος εκτός των 1 ή 2, το πρόγραμμα εμφανίζει Other
Σε κάθε περίπτωση, το πρόγραμμα συνεχίζει με την εκτέλεση της τελευταίας
printf() και εμφανίζει End
Προγραμματισμός Ι
Παρατηρήσεις
 Σημειώστε ότι η τελική
else εντολή δεν είναι
υποχρεωτικό να υπάρχει

 Αν δεν υπάρχει, και καμία


συνθήκη δεν είναι αληθής,
τότε – πολύ απλά – το
πρόγραμμα δεν κάνει τίποτα

 Ποια θα ήταν η έξοδος του


προηγούμενου
παραδείγματος αν δεν
υπήρχε η τελική else
εντολή (βλ. δίπλα) ενώ ο
χρήστης εισήγαγε την τιμή
3 ??? Έξοδος: End
Προγραμματισμός Ι

Παράδειγμα
 Γράψτε ένα πρόγραμμα που να διαβάζει δύο δεκαδικούς αριθμούς
(π.χ., a και b) και εμφανίζει τη λύση (αν υπάρχει) της εξίσωσης:
a*x + b = 0

Προγραμματισμός Ι
Ο τελεστής ?: (Ι)
 Ο τελεστής ?: επιτρέπει την εκτέλεση μίας από δύο ενέργειες,
σύμφωνα με την τιμή μίας έκφρασης και η σύνταξή του είναι:
exp1 ? exp2 : exp3;
 Σε μία εντολή με τον τελεστή ?: αν η έκφραση exp1 είναι αληθής,
τότε θα εκτελεστεί η έκφραση που ακολουθεί το ερωτηματικό ?
(δηλαδή η exp2), αλλιώς θα εκτελεστεί η έκφραση που ακολουθεί
την άνω-κάτω τελεία : (δηλαδή η exp3)

 Π.χ.

 Ο τελεστής ?: χρησιμοποιείται συνήθως για να υποκαταστήσει την


εντολή if, όταν αυτή έχει απλή μορφή
Προγραμματισμός Ι

Ο τελεστής ?: (ΙΙ)
 Η τιμή μίας έκφρασης με τον τελεστή ?: είναι ίση με την τιμή της
έκφρασης που εκτελείται τελευταία
 Ποια είναι η τιμή της μεταβλητής max στην παρακάτω έκφραση ;
max = (a > b)? a : b;
 Η παραπάνω έκφραση είναι ισοδύναμη με:
if(a > b)
max = a;
else
max = b;

 Γενικότερα, η έκφραση: exp1 ? exp2 : exp3;

Είναι ισοδύναμη με: if(exp1)


exp2;
else
exp3;
Προγραμματισμός Ι
Ο τελεστής ?: (ΙΙΙ)
 Η έκφραση μετά την την άνω-κάτω τελεία : (δηλαδή η exp3)
μπορεί να αντικατασταθεί από άλλη έκφραση που χρησιμοποιεί τον
τελεστή ?:

 Π.χ.

k = exp1 ? exp2 : add1 ? add2 : add3;

Η παραπάνω έκφραση είναι ισοδύναμη με:

if(exp1)
k = exp2;
else if(add1)
k = add2;
else
k = add3;
Προγραμματισμός Ι

Παράδειγμα
 Τι κάνει το παρακάτω πρόγραμμα???

Το πρόγραμμα διαβάζει έναν ακέραιο και αν είναι


μεγαλύτερος του 10 εμφανίζει One, αλλιώς
εμφανίζει Two

Προγραμματισμός Ι
Η εντολή switch (I)
 Η εντολή ελέγχου
switch
χρησιμοποιείται
εναλλακτικά έναντι της
if-else-if δομής,
όταν επιθυμούμε να
ελέγξουμε μία έκφραση
για όλες τις δυνατές
τιμές που αυτή η
έκφραση μπορεί να
πάρει και να
χειριστούμε την κάθε
περίπτωση με
διαφορετικό τρόπο

 Γενική σύνταξη της


εντολής switch:

Προγραμματισμός Ι

Η εντολή switch (II)


 Η έκφραση που ελέγχεται πρέπει να είναι ακέραιη μεταβλητή ή
έκφραση
 Oι τιμές των σταθερά_1, σταθερά_2, ... , σταθερά_n πρέπει
και αυτές να είναι ακέραιες σταθερές με διαφορετικές τιμές μεταξύ
των
 Τα «βήματα» κατά την εκτέλεση της εντολής switch:
1. Η τιμή της έκφρασης συγκρίνεται διαδοχικά με κάθε μία από τις
σταθερά_1, σταθερά_2, ..., σταθερά_n
 Αν βρεθεί μία ίδια τιμή, τότε εκτελούνται οι εντολές που ακολουθούν το
αντίστοιχο case και στη συνέχεια γίνεται τερματισμός της εντολής
switch μέσω της εντολής break (λεπτομέρειες για την εντολή break σε
επόμενο μάθημα...)
 Αν δεν βρεθεί ίδια τιμή, τότε εκτελούνται οι εντολές που ακολουθούν το
default και στη συνέχεια γίνεται τερματισμός της εντολής switch μέσω
της εντολής break
2. Και στις δύο περιπτώσεις, η εκτέλεση του κώδικα συνεχίζει με
την πρώτη εντολή που υπάρχει μετά το άγκιστρο κλεισίματος
της switch εντολής
Προγραμματισμός Ι
Παράδειγμα

Προγραμματισμός Ι

Παρατηρήσεις (I)
 Η ύπαρξη της default περίπτωσης στην εντολή switch δεν
είναι υποχρεωτική (όπως δεν ήταν υποχρεωτική και η ύπαρξη της
εντολής else στην εντολή if)

 Η default περίπτωση μπορεί να βρίσκεται οπουδήποτε μέσα σε


μία εντολή switch (π.χ. να είναι πρώτη ή να βρίσκεται ανάμεσα
στα case), αν όμως υπάρχει, προτείνεται να βρίσκεται στο
τέλος, δηλ. μετά από κάθε case

 Σε περίπτωση που δεν υπάρχει η default περίπτωση και η


τιμή της έκφρασης δεν είναι ίση με κάποια από τις τιμές των
σταθερά_1, σταθερά_2, ..., σταθερά_n, τότε γίνεται
τερματισμός της εντολής switch, χωρίς να γίνει κάποια άλλη
ενέργεια
 Δηλαδή, η ροή του προγράμματος συνεχίζει με την εκτέλεση της
πρώτης εντολής μετά το switch
Προγραμματισμός Ι
Παρατηρήσεις (II)
 Η ύπαρξη της break δεν είναι υποχρεωτική, συνήθως, όμως, η
παράλειψή της οδηγεί σε bug στον κώδικα

 Αν, όμως, τα μπλοκ εντολών που αντιστοιχούν σε δύο ή


περισσότερες case περιπτώσεις είναι κοινά, τότε μπορεί να γίνει
συνένωση των αντίστοιχων case

 Π.χ. αν τα μπλοκ εντολών για τις περιπτώσεις των


σταθερά_1, σταθερά_2 και σταθερά_3 είναι κοινά, τότε τα
αντίστοιχα case συνενώνονται ως εξής (έχουν, όπως βλέπουμε,
κοινή break)

Προγραμματισμός Ι

Παρατηρήσεις (III)
 Η τελευταία break σε μία εντολή switch μπορεί να
παραλειφθεί, αφού ούτως ή άλλως η switch τερματίζεται με
το άγκιστρο κλεισίματός της }

 Κάθε switch εντολή μπορεί να γραφτεί ισοδύναμα με χρήση


πολλαπλών εντολών if-else-if
 Σε περιπτώσεις, όμως, πολλαπλών if-else-if εντολών, η
χρήση της switch οδηγεί σε πιο ευανάγνωστο κώδικα

 MEIONEKTHMATA της switch έναντι της if:


1. Η εντολή switch διαφέρει από την εντολή if στο ότι η
switch κάνει έλεγχο μόνο για ισότητα (δηλαδή, για τιμές
της έκφρασης που να είναι ίσες με σταθερές case), ενώ η
συνθήκη σε μία if εντολή μπορεί να είναι οποιουδήποτε
τύπου
2. Oι τιμές της έκφρασης της switch και των
συγκρινόμενων σταθερών πρέπει υποχρεωτικά να είναι
ακέραιες Προγραμματισμός Ι
Παράδειγμα (Ι)
Ποια είναι η έξοδος
του προγράμματος,
αν ο χρήστης
πληκτρολογήσει:
Α) 2
Β) 1
Γ) 0

Έξοδος:
Α) Two
End
Β) One
Two
End
Γ) Something else
End
Προγραμματισμός Ι

Παράδειγμα (ΙΙ)

Προγραμματισμός Ι
Προγραμματισμός Ι

Βρόχοι Επανάληψης

Ο βρόχος for
 Η εντολή for χρησιμοποιείται για τη δημιουργία επαναληπτικών
βρόχων στη C
 Επαναληπτικός βρόχος καλείται το τμήμα του κώδικα μέσα σε ένα
πρόγραμμα, το οποίο εκτελείται από την αρχή και
επαναλαμβάνεται όσο μία συνθήκη παραμένει αληθής (true)

 Γενική σύνταξη της εντολής for:

for(αρχική_έκφραση; συνθήκη; τελική_έκφραση)


{
/* ομάδα εντολών (ή αλλιώς “σώμα” του βρόχου) που
εκτελείται όσο η συνθήκη παραμένει αληθής. */
}

Προγραμματισμός Ι
Τα βήματα εκτέλεσης της for
1. Εκτελείται η αρχική_έκφραση
 Η αρχική_έκφραση εκτελείται μόνο μία φορά, όταν αρχίζει η εκτέλεση
της for εντολής και μπορεί να είναι οποιαδήποτε έγκυρη έκφραση της C
 Συνήθως, είναι μία εντολή εκχώρησης που αρχικοποιεί κάποια
μεταβλητή, η οποία θα χρησιμοποιηθεί από τις άλλες δύο εκφράσεις
2. Γίνεται έλεγχος της τιμής της συνθήκης
 Η συνθήκη είναι συνήθως μία σχεσιακή έκφραση
 Αν είναι ψευδής, τότε ο for βρόχος τερματίζεται και η εκτέλεση του
προγράμματος συνεχίζει με την πρώτη εντολή που υπάρχει μετά το
άγκιστρο κλεισίματος της for εντολής
 Αν είναι αληθής, τότε εκτελείται η ομάδα των εντολών που
ονομάζεται και «σώμα του βρόχου»
3. Εκτελείται η τελική_έκφραση
 Συνήθως, η τελική_έκφραση αλλάζει την τιμή κάποιας μεταβλητής
που χρησιμοποιείται στη συνθήκη
4. Επαναλαμβάνονται συνεχώς τα βήματα (2) και (3), μέχρι η
τιμή της συνθήκης να γίνει ψευδής
Προγραμματισμός Ι

Παράδειγμα

Έξοδος: 0 1 2 3 4

Προγραμματισμός Ι
Παρατηρήσεις (I)
 Όταν γνωρίζουμε εκ των προτέρων τον αριθμό των επαναλήψεων
που επιθυμούμε να εκτελεστούν, τότε χρησιμοποιούμε συνήθως
την εντολή for και όχι κάποια άλλη επαναληπτική μέθοδο

 Όπως και στην περίπτωση της if-else δομής, αν το μπλοκ


εντολών περιέχει μόνο μία εντολή, τότε τα άγκιστρα μπορούν να
παραλειφθούν

Π.χ. το προηγούμενο παράδειγμα θα μπορούσε να γραφεί:

Προγραμματισμός Ι

Παρατηρήσεις (IΙ)
 Μην βάζετε το ελληνικό ερωτηματικό ; στο τέλος της for
εντολής, γιατί το ερωτηματικό θεωρείται ξεχωριστή πρόταση, η
οποία σημαίνει ότι δεν υπάρχει ομάδα εντολών για εκτέλεση (null
statement)
 Π.χ. η εντολή:
for(a = 0; a < 1000; a++);
αυξάνει την τιμή του a χίλιες φορές και δεν κάνει τίποτα άλλο

 Συνήθως, for βρόχοι με «κενή ομάδα εντολών»


χρησιμοποιούνται σαν βρόχοι εισαγωγής χρονικής καθυστέρησης,
δηλαδή «για να περάσει η ώρα» μέχρι να γίνει κάποια ενέργεια...

Σε περίπτωση, πάντως, που όντως θέλετε να δημιουργήσετε έναν


«κενό επαναληπτικό βρόχο» , τοποθετήστε το ερωτηματικό στην
επόμενη σειρά, έτσι ώστε να είναι ξεκάθαρο
Π.χ. for(a = 0; a < 1000; a++)
;
Προγραμματισμός Ι
Παρατηρήσεις (IΙΙ)
 Τα τμήματα της for εντολής, αρχική_έκφραση, συνθήκη
και τελική_έκφραση μπορεί να αποτελούνται από μία μόνο
εντολή, αλλά και από περισσότερες
 Στην περίπτωση που αποτελούνται από περισσότερες από μία
εντολές, τότε αυτές χωρίζονται μεταξύ τους με τον τελεστή
κόμμα (,).

 Π.χ.:

for(i = 0, j = 10; i+j<15; i++, j--)

αρχική_έκφραση συνθήκη τελική_έκφραση

Προγραμματισμός Ι

Παρατηρήσεις (IΙΙ)
 Στη θέση των αρχική_έκφραση, συνθήκη και
τελική_έκφραση μπορεί να μπει οποιαδήποτε έγκυρη έκφραση
της C

Π.χ.

for(printf("Yes\n"); συνθήκη; τελική_έκφραση)

Με την παραπάνω εντολή, τυπώνεται στην οθόνη Yes και το


πρόγραμμα συνεχίζει με τον έλεγχο της συνθήκης της for...

Προγραμματισμός Ι
Παρατηρήσεις (IV)
 Σε μία for εντολή μπορεί να λείπουν κάποια από τα 3 τμήματά
της ή ακόμη και όλα

 Π.χ. στην εντολή:

for(; a < 5; a++)

λείπει η αρχική_έκφραση

 Ωστόσο, το ελληνικό ερωτηματικό ; εξακολουθεί να υπάρχει και


να λειτουργεί σαν διαχωριστικό μεταξύ των τμημάτων

 Στην εντολή:

for(;;)

λείπουν και τα 3 τμήματα


Προγραμματισμός Ι

Παρατηρήσεις (V)
 Όταν σε μία for εντολή λείπει η συνθήκη ή η συνθήκη είναι
πάντα αληθής, τότε αυτός ο for βρόχος ονομάζεται ατέρμονος
βρόχος, γιατί δεν τερματίζεται ποτέ

 Π.χ. ο βρόχος:

for(a = 0; 0 < 1; a++)

είναι ατέρμονος, γιατί η συνθήκη 0 < 1 είναι πάντα αληθής

 Επίσης, ο βρόχος:

for(;;)

είναι και αυτός ατέρμονος, αφού λείπει η συνθήκη

Προγραμματισμός Ι
Παρατηρήσεις (VI)
 Aν η συνθήκη είναι εξ’αρχής ψευδής, τότε δεν θα εκτελεστεί
ποτέ το μπλοκ εντολών της for

 Π.χ. ο παρακάτω for βρόχος και το μπλοκ εντολών του δεν θα


εκτελεστεί ποτέ, αφού η συνθήκη a > 10 είναι εξ’αρχής ψευδής
(αφού η τιμή του a είναι 0)

for(a = 0; a > 10; a++)


{
printf("%d\n",a);
printf("Yes\n");
}

Προγραμματισμός Ι

Παρατηρήσεις (VIΙ)
 Αν αρχική_έκφραση και η τελική_έκφραση λείπουν, ο for
βρόχος είναι ισοδύναμος με τον αντίστοιχο while βρόχο, όπως
θα δούμε παρακάτω

 Π.χ. ο παρακάτω for βρόχος:

for(; a < 5;)

Είναι ισοδύναμος με τον while βρόχο:

while(a < 5)

Προγραμματισμός Ι
Παραδείγματα (Ι)
 Γράψτε ένα πρόγραμμα που να εμφανίζει τους ακέραιους αριθμούς
από το 1 έως το 10

 Γράψτε ένα πρόγραμμα που να εμφανίζει τους ακέραιους αριθμούς


από το 1 έως το 10, αλλά με ανάποδη σειρά...

Προγραμματισμός Ι

Παραδείγματα (ΙΙ)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει 10 ακέραιους
αριθμούς και να εμφανίζει κάθε φορά το τριπλάσιο του αριθμού,
μόνο αν αυτός είναι μικρότερος του 10 ή μεγαλύτερος του 20

Προγραμματισμός Ι
Παραδείγματα (ΙΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 12 7 End = 2
Test
Test
Test... (ατέρμων βρόχος)

Προγραμματισμός Ι

Παραδείγματα (ΙV)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος:0 1 2
One

Προγραμματισμός Ι
Παραδείγματα (V)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: One
Val1 = 4 Val2 = 2

Προγραμματισμός Ι

H εντολή break
 Η εντολή break χρησιμοποιείται για τον άμεσο τερματισμό ενός
επαναληπτικού βρόχου (π.χ. for, while ή do-while) ή για τον
τερματισμό μίας εντολής switch

 Στους επαναληπτικούς βρόχους, μετά την εκτέλεση της εντολής


break το πρόγραμμα συνεχίζει με την εκτέλεση της πρώτης
εντολής μετά τον βρόχο

 Ωστόσο, όπως θα δούμε στη συνέχεια, η εκτέλεση της εντολής


break μέσα σε έναν ένθετο επαναληπτικό βρόχο προκαλεί τον
τερματισμό μόνο του βρόχου στον οποίο η ίδια περιέχεται

 Επίσης, όπως είδαμε στην εντολή switch, η εκτέλεση της


εντολής break μέσα σε μία switch προκαλεί επίσης τον άμεσο
τερματισμό της λειτουργίας της

Προγραμματισμός Ι
Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος:1 2 3 4
i = 5

Προγραμματισμός Ι

H εντολή continue
 Η εντολή continue χρησιμοποιείται μέσα σε έναν επαναληπτικό
βρόχο (π.χ. for, while ή do-while)

 Η εκτέλεση της εντολής continue μέσα σε έναν επαναληπτικό


βρόχο προκαλεί την άμεση διακοπή της εκτέλεσης της ομάδας
των εντολών της τρέχουσας επανάληψης και την έναρξη της
επόμενης επανάληψης

 Άρα, οι εντολές ανάμεσα στην εντολή continue και στο τέλος


του βρόχου δεν εκτελούνται για την τρέχουσα επανάληψη

Προγραμματισμός Ι
Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος:1 2 3 4 6 7 8 9 10
i = 11

Προγραμματισμός Ι

Ένθετοι for βρόχοι


 Ένας επαναληπτικός βρόχος (π.χ. for, while ή do-while)
μπορεί να είναι ένθετος στο εσωτερικό κάποιου άλλου

 Π.χ. στην παρακάτω γενική περίπτωση, βλέπουμε δύο ένθετα


for, στα οποία για να συμβεί μία επανάληψη του εξωτερικού
βρόχου πρέπει πρώτα να τερματίσει η εκτέλεση του εσωτερικού
βρόχου
Εξωτερικός for βρόχος

for(αρχική_έκφραση_1; συνθήκη_1; τελική_έκφραση_1)


{
for(αρχική_έκφραση_2; συνθήκη_2; τελική_έκφραση_2)
{
Εσωτερικός
for βρόχος /* ομάδα εντολών που θα εκτελείται συνεχώς
όσο η συνθήκη_2 παραμένει αληθής. */
}
/* ομάδα εντολών που θα εκτελείται συνεχώς όσο η
συνθήκη_1 παραμένει αληθής. */
}

Προγραμματισμός Ι
Παραδείγματα (Ι)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: One Two One Two

Προγραμματισμός Ι

Παραδείγματα (ΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: Two Two One Two One


Val1 = 2 Val2 = 0

Προγραμματισμός Ι
Παραδείγματα (ΙΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: One Two Two One

Προγραμματισμός Ι

Παραδείγματα (ΙV)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: *
* *
* * *
* * * *
* * * * *
Προγραμματισμός Ι
Παραδείγματα (V)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει τους βαθμούς μίας
ομάδας 5 φοιτητών σε 3 διαφορετικά μαθήματα και να εμφανίζει
στην οθόνη τον μέσο όρο του κάθε φοιτητή στα 3 μαθήματα και
τον συνολικό μέσο όρο της ομάδας σε όλα τα μαθήματα

Προγραμματισμός Ι (συνεχίζεται...)

Παραδείγματα (V)

Προγραμματισμός Ι
Ο βρόχος while
 Η εντολή while, όπως και η εντολή for,
χρησιμοποιείται για τη δημιουργία επαναληπτικών
βρόχων στη C

 Γενική σύνταξη της εντολής while:

while(συνθήκη)
{
/* ομάδα εντολών που θα εκτελείται όσο η
συνθήκη παραμένει αληθής. */
}

Προγραμματισμός Ι

Εκτέλεση της εντολής while

1. Γίνεται έλεγχος της τιμής της συνθήκης (η οποία είναι


συνήθως μία σχεσιακή έκφραση)
 Αν η συνθήκη είναι ψευδής (false) τότε ο while βρόχος
τερματίζεται και η εκτέλεση του προγράμματος συνεχίζει με την
πρώτη εντολή που υπάρχει μετά το άγκιστρο κλεισίματος της
while εντολής
 Αν η συνθήκη είναι αληθής (true) τότε εκτελείται η ομάδα
εντολών που υπάρχει ανάμεσα στα άγκιστρα {} και η τιμή της
συνθήκης ελέγχεται πάλι
 Αν η τιμή της συνθήκης γίνει ψευδής (false), τότε ο while
βρόχος τερματίζεται
 Αν όχι, επανεκτελείται η ομάδα των εντολών του βρόχου while

Η παραπάνω διαδικασία επαναλαμβάνεται μέχρι η τιμή της


συνθήκης να γίνει ψευδής
Προγραμματισμός Ι
Παρατηρήσεις (I)
 Η εντολή while χρησιμοποιείται συνήθως όταν δεν γνωρίζουμε
τον ακριβή αριθμό των επαναλήψεων που θέλουμε να εκτελεστεί η
ομάδα των εντολών μας
 Όταν αντιθέτως γνωρίζουμε εκ των προτέρων τον αριθμό των
επαναλήψεων που επιθυμούμε να εκτελεστούν, τότε συνήθως
χρησιμοποιούμε την εντολή for

 Όπως και σε προηγούμενες περιπτώσεις (π.χ. εντολές if-else,


for, κτλ), αν η ομάδα εντολών περιέχει μόνο μία εντολή, τότε τα
άγκιστρα μπορούν να παραλειφθούν

 Μην βάζετε το ελληνικό ερωτηματικό ; στο τέλος της while


εντολής, γιατί το ερωτηματικό θεωρείται ξεχωριστή πρόταση, η
οποία σημαίνει ότι δεν υπάρχει ομάδα εντολών για εκτέλεση

Προγραμματισμός Ι

Παρατηρήσεις (IΙ)
 Η εντολή while(x) είναι ισοδύναμη με την while(x != 0)

 Προτείνεται ο δεύτερος τρόπος για να είναι πιο ευανάγνωστο το


πρόγραμμα

 Αντίστοιχα, για τον ίδιο ακριβώς λόγο προτείνεται το


while(x == 0) αντί του while(!x)

 Όταν σε μία while εντολή η συνθήκη είναι πάντα αληθής, τότε


αυτός ο while βρόχος ονομάζεται ατέρμονος βρόχος, γιατί δεν
τερματίζεται ποτέ

 Π.χ. ο βρόχος while(1) είναι ατέρμονος, γιατί η συνθήκη είναι


πάντα αληθής, αφού το 1 είναι διαφορετικό από το 0

Προγραμματισμός Ι
Παρατηρήσεις (IΙΙ)
 Aν η συνθήκη είναι εξ’αρχής ψευδής, τότε δεν θα εκτελεστεί
ποτέ το μπλοκ εντολών της while
 Π.χ.

int a = 10, b = 20;


while(b < a)
{
printf("%d\n", a);
printf("Yes\n");
}

Προγραμματισμός Ι

Παρατηρήσεις (IV)
 Οι εντολές for και while είναι πολύ στενά συνδεδεμένες

 Ουσιαστικά, οι παρακάτω εντολές είναι ισοδύναμες:

exp1;
for(exp1; exp2; exp3) while(exp2)
{ {
/* εντολές */ /* εντολές */
} exp3;
}

Προγραμματισμός Ι
Παραδείγματα (Ι)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει συνεχώς έναν
ακέραιο αριθμό και να τον εμφανίζει μέχρι ο χρήστης να εισάγει
το μηδέν (0). Το μηδέν (0) που θα εισάγει ο χρήστης να μην
εμφανίζεται.

Προγραμματισμός Ι

Παραδείγματα (ΙΙ)
 Πόσες φορές εκτελείται ο while βρόχος στο παρακάτω
πρόγραμμα?

Απάντηση: 2 φορές

Προγραμματισμός Ι
Παραδείγματα (ΙΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Απάντηση: Ατέρμονος βρόχος...


(και όχι 8, που πιθανώς απαντήσατε)

Προγραμματισμός Ι

Παραδείγματα (ΙV)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει συνεχώς ακέραιους αριθμούς
μέχρι ο χρήστης να εισάγει το 0. Στο τέλος, το πρόγραμμα να εμφανίζει
το πλήθος των θετικών και αρνητικών αριθμών που εισήγαγε ο χρήστης.
Το μηδέν (0) να μην προσμετράται ούτε στους θετικούς ούτε στους
αρνητικούς αριθμούς

Προγραμματισμός Ι
Ο βρόχος do-while
 Η εντολή do-while, όπως και οι εντολές for και while,
χρησιμοποιείται για τη δημιουργία επαναληπτικών βρόχων
στη C

 Σε αντίθεση με τις εντολές for και while η συνθήκη


στην εντολή do-while ελέγχεται αφού εκτελεστούν μία
φορά οι εντολές του βρόχου

 Γενική σύνταξη της εντολής do-while:

do
{
/* ομάδα εντολών που εκτελείται αρχικά μία φορά
και στη συνέχεια κατ’ επανάληψη όσο η συνθήκη
παραμένει αληθής. */
}while(συνθήκη);
Προγραμματισμός Ι

while vs. do-while

Προγραμματισμός Ι
Τα βήματα εκτέλεσης της do-while

1. Εκτελείται η ομάδα εντολών που υπάρχει ανάμεσα στα


άγκιστρα {}

2. Γίνεται έλεγχος της τιμής της συνθήκης (η οποία είναι


συνήθως μία σχεσιακή έκφραση)
 Αν η συνθήκη είναι ψευδής (false) τότε ο do-while βρόχος
τερματίζεται και η εκτέλεση του προγράμματος συνεχίζει με την
πρώτη εντολή που υπάρχει μετά το άγκιστρο κλεισίματος της
do-while εντολής
 Αν η συνθήκη είναι αληθής (true) τότε επανεκτελείται η
ομάδα εντολών που υπάρχει ανάμεσα στα άγκιστρα {}
 Το βήμα αυτό επαναλαμβάνεται μέχρι η τιμή της συνθήκης να
γίνει ψευδής

Προγραμματισμός Ι

Παρατηρήσεις (Ι)
 Ο βρόχος do-while χρησιμοποιείται πολύ λιγότερο από τους
for και while βρόχους
 Όποιο πρόβλημα λύνεται με χρήση του βρόχου
do-while θα μπορούσε να επιλυθεί και με χρήση βρόχων while
ή for

 Κάθε do-while βρόχος εκτελείται τουλάχιστον μία φορά, ακόμα


κι αν η συνθήκη του βρόχου είναι ψευδής

 Ο βρόχος do-while πρέπει να τελειώνει με το ελληνικό


ερωτηματικό (;)

 Συνήθως, οι βρόχοι do-while χρησιμοποιούνται όταν θέλουμε να


ελέγξουμε τις τιμές εισόδου σε ένα πρόγραμμα

Προγραμματισμός Ι
Παρατηρήσεις (ΙΙ)
 Σε περίπτωση που ο βρόχος do-while περιέχει μόνο μία εντολή,
τα άγκιστρα μπορούν, και στην do-while εντολή, να παραλειφθούν

 Παρόλα αυτά, προτείνεται να χρησιμοποιείτε πάντοτε τα άγκιστρα


σε εντολές do-while, διότι η παράλειψή τους πιθανότατα να
παραπλανήσει τους αναγνώστες του προγράμματός σας (στο
«κάτω» κομμάτι της εντολής), κάνοντάς τους να νομίζουν ότι
χρησιμοποιείτε while εντολή

Π.χ.:

do
printf("%d\n", i);
while(++i <= 10);

Προγραμματισμός Ι

Παράδειγμα (Ι)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει έναν θετικό ακέραιο
αριθμό και με χρήση της εντολής do-while να εμφανίζει τη
λέξη This τόσες φορές στην οθόνη όσο και ο αριθμός που
εισήγαγε ο χρήστης.

 Τροποποιήστε το παραπάνω πρόγραμμα, ώστε να ζητά επίμονα την


εισαγωγή θετικού αριθμού σε περίπτωση που ο χρήστης εισάγει μη
θετικό ακέραιο (να γίνεται δηλαδή έλεγχος της τιμής εισαγωγής).
Προγραμματισμός Ι
Παράδειγμα (ΙΙ)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει έναν θετικό ακέραιο
N > 3 και στη συνέχεια να υπολογίζει το αποτέλεσμα της
παρακάτω μαθηματικής σχέσης. Το πρόγραμμα θα πρέπει να
αναγκάζει τον χρήστη να εισάγει ακέραιο μεγαλύτερο του 3.

1 1 1 1 1
      ...
1 2 3 4 N

Προγραμματισμός Ι

Η εντολή goto
 Η εντολή goto χρησιμοποιείται με σκοπό να μεταφέρει την
εκτέλεση του προγράμματος σε κάποια άλλη εντολή μέσα στην ίδια
συνάρτηση, με την προϋπόθεση ότι η εντολή έχει μία ετικέτα

 Γενική σύνταξη της εντολής goto:

goto lebel;

 Όταν εκτελείται η εντολή goto η εκτέλεση του προγράμματος


μεταβαίνει άμεσα στην εντολή που ακολουθεί τη θέση που έχει
δηλωθεί με το όνομα lebel

 Η θέση με το όνομα lebel πρέπει να είναι μοναδική μέσα στη


συνάρτηση και προσδιορίζεται γράφοντας το όνομά της και άνω
κάτω τελεία :

Προγραμματισμός Ι
Παράδειγμα
 Αν ο χρήστης εισάγει την τιμή -1 η εκτέλεση του προγράμματος
μεταβαίνει στη θέση START και ο for βρόχος εκτελείται πάλι
από την αρχή

Προγραμματισμός Ι

Παρατηρήσεις (Ι)
 Γενικά, δεν προτείνεται η χρήση της εντολής goto, γιατί η
μετάβαση της εκτέλεσης του προγράμματος από ένα σημείο σε
κάποιο άλλο και μετά σε κάποιο άλλο, κ.ο.κ, οδηγεί σε δυσνόητο
κώδικα που δεν είναι καλά οργανωμένος και άρα δύσκολα
ελέγχεται

 Πολλοί μάλιστα είναι εντελώς αντίθετοι στη χρήση της goto,


υποστηρίζοντας ότι δεν έχει καμία θέση μέσα σε ένα καλά
δομημένο πρόγραμμα

 Ωστόσο, υπάρχουν αρκετά προγράμματα χιλιάδων γραμμών, χωρίς


καμία εντολή goto, τα οποία δεν έχουν καμία σχέση με δομημένο
προγραμματισμό

Προγραμματισμός Ι
Παρατηρήσεις (ΙΙ)
 Ίσως, ορισμένες φορές, η χρήση της goto να οδηγεί και σε
απλούστερο κώδικα (π.χ. στην άμεση έξοδο από έναν «αρκετά
ένθετο» for βρόχο, όπως τον παρακάτω)

 Εξάλλου, και οι εντολές break, continue και return (που


χρησιμοποιούνται από τους «κατακριτές» της goto) δεν είναι
τίποτα άλλο από παραλλαγές της goto

Προγραμματισμός Ι

Προγραμματισμός Ι

Πίνακες
Πίνακες στη C
 Ένας πίνακας στη C είναι μία δομή δεδομένων που αποτελείται από
στοιχεία του ίδιου τύπου (π.χ. πίνακας ακεραίων αριθμών, πίνακας
πραγματικών αριθμών, πίνακας χαρακτήρων, ...)

 Όλοι οι πίνακες δεσμεύουν συνεχόμενες θέσεις στη μνήμη (στην


περιοχή μνήμης που ονομάζεται στοίβα ή stack) του υπολογιστή και
διακρίνονται σε πίνακες μίας διάστασης και πίνακες πολλών
διαστάσεων

 Συνηθέστερα είδη είναι οι μονοδιάστατοι και οι διδιάστατοι πίνακες

Προγραμματισμός Ι

Ορισμός Μονοδιάστατου Πίνακα


 Για να ορίσουμε έναν μονοδιάστατο πίνακα πρέπει να δηλώσουμε το
όνομα του πίνακα, τον τύπο δεδομένων των στοιχείων του
πίνακα και το πλήθος των στοιχείων του πίνακα

 Η γενική περίπτωση ορισμού ενός μονοδιάστατου πίνακα είναι:

τύπος_δεδομένων όνομα_πίνακα [πλήθος_στοιχείων_πίνακα];

Παρατηρήσεις
 Το όνομα_πίνακα πρέπει να είναι μοναδικό (να μην υπάρχει άλλη

μεταβλητή στο πρόγραμμα με το ίδιο όνομα)


 Το πλήθος_στοιχείων_πίνακα (λέγεται και μήκος του πίνακα)
πρέπει να περιβάλλεται από αγκύλες [ ] αμέσως μετά το
όνομα_πίνακα
 Ο τύπος_δεδομένων μπορεί να είναι οποιοσδήποτε τύπος δεδομένων

(π.χ. int, float, char, κτλ…)


Προγραμματισμός Ι
Παραδείγματα Ορισμών
int a[100];
/* πίνακας με όνομα a, ο οποίος περιέχει 100 ακέραιους
τύπου int */

double arr[5];
/* πίνακας με όνομα arr, ο οποίος περιέχει 5 πραγματικούς
αριθμούς τύπου double */

char x[2000];
/* πίνακας με όνομα x, ο οποίος περιέχει 2000 ακεραίους
τύπου char */

 Επαναλαμβάνεται ότι κάθε πίνακας δεσμεύει συνεχόμενες θέσεις


μνήμης στον υπολογιστή

 Ερώτηση: Πόση μνήμη καταλαμβάνει ο κάθε ένας από τους


παραπάνω πίνακες???
Προγραμματισμός Ι

Στοιχεία μονοδιάστατου Πίνακα


 Για να αναφερθούμε σε κάποιο στοιχείο του πίνακα γράφουμε το
όνομα του πίνακα συνοδευόμενο από τον δείκτη θέσης του
στοιχείου μέσα σε αγκύλες []

 Ο δείκτης θέσης είναι ένας ακέραιος αριθμός ή μία ακέραια


μεταβλητή ή έκφραση, η οποία προσδιορίζει τη θέση του
συγκεκριμένου στοιχείου στον πίνακα

 Το πρώτο στοιχείο ενός πίνακα με μέγεθος n στοιχεία


αποθηκεύεται στη θέση [0] του πίνακα, το δεύτερο στοιχείο στη
θέση [1], το τρίτο στη θέση [2], ... κ.ο.κ., με αποτέλεσμα το
τελευταίο στοιχείο να αποθηκεύεται στη θέση [n-1]

Προγραμματισμός Ι
Γραφική Αναπαράσταση (I)
Όλα τα στοιχεία του πίνακα
Π.χ. char c[10]; έχουν το ίδιο όνομα (c)

1ο στοιχείο του πίνακα c -45


- 4 c[0]

2ο στοιχείο του πίνακα c 76 c[1]


. 10 c[2]
. 72 c[3]
. 1543
21 c[4]
. -89
-39 c[5]
. -50 c[6]
. c[7]
12
62
.
-3
0 c[8]

10ο στοιχείο του πίνακα c 11 c[9]

Δείκτης θέσης του κάθε


στοιχείου του πίνακα

Προγραμματισμός Ι

Γραφική Αναπαράσταση (II)


Π.χ. int x[3];

1ο στοιχείο του πίνακα x

2ο στοιχείο του πίνακα x

3ο στοιχείο του πίνακα x

Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Σημειώστε ότι το πλήθος των στοιχείων του πίνακα (δηλαδή το
μήκος του πίνακα) δεν μπορεί να αλλάξει κατά την εκτέλεση του
προγράμματος. Παραμένει σταθερό.

 Για τη δήλωση του μεγέθους ενός πίνακα χρησιμοποιείται πολύ


συχνά η μακροεντολή #define , π.χ.:

#define SIZE 1000


int arr[SIZE]; /* Ο μεταγλωττιστής αντικαθιστά τη
λέξη SIZE με την τιμή 1000 και δημιουργεί έναν
πίνακα με στοιχεία 1000 ακεραίους. */
 Σε αντίθεση με τη χρήση μακροεντολής, απαγορεύεται να
χρησιμοποιηθεί σταθερά δηλωμένη με τη λέξη const για να
προσδιορίσει το πλήθος των στοιχείων του πίνακα, π.χ., η
ακόλουθη δήλωση δεν επιτρέπεται:
const int size = 100;
float arr[size]; /* Απαγορεύεται!!! */
Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 Όταν δηλώνεται ένας πίνακας, ο μεταγλωττιστής δεσμεύει ένα
κομμάτι μνήμης για να αποθηκεύσει τα στοιχεία του πίνακα
 Οι τιμές των στοιχείων του πίνακα αποθηκεύονται η μία μετά την άλλη
σε διαδοχικές θέσεις μνήμης
 Τυπικά, αυτό το κομμάτι μνήμης δεσμεύεται από συγκεκριμένο μέρος της
μνήμης που ονομάζεται στοίβα (stack), και αποδεσμεύεται όταν
τερματιστεί η λειτουργία της συνάρτησης μέσα στην οποία έχει δηλωθεί
ο πίνακας

 Π.χ., με την παρακάτω δήλωση ο μεταγλωττιστής δεσμεύει 40


bytes για να αποθηκεύσει τις τιμές των 10 ακεραίων στοιχείων του

int arr[10];

 Για την εύρεση του μεγέθους της δεσμευμένης μνήμης από έναν
πίνακα, μπορούμε να χρησιμοποιήσουμε τον τελεστή sizeof (π.χ.,
sizeof(arr))

Προγραμματισμός Ι
Παρατηρήσεις (ΙΙΙ)
 Το μέγιστο μέγεθος μνήμης που μπορεί να δεσμευτεί με τη δήλωση
ενός πίνακα εξαρτάται από το μέγεθος της στοίβας

 Π.χ. το επόμενο πρόγραμμα μπορεί να μην εκτελείται σε κάποιον


υπολογιστή, εκτός κι αν η διαθέσιμη μνήμη του στη στοίβα είναι
αρκετά μεγάλη, έτσι ώστε να μπορεί να αποθηκεύσει 500000
δεκαδικές τιμές τύπου double
#include <stdio.h>
int main(void)
{
double arr[500000];
return 0;
}

 Όταν στο σύστημά σας η μνήμη είναι περιορισμένη, μη δηλώνετε


πίνακες με μέγεθος μεγαλύτερο απ’ ότι χρειάζεστε (να αποφεύγετε,
δηλαδή, την κατασπατάληση μνήμης)

Προγραμματισμός Ι

Παρατηρήσεις (ΙV)
 Στην περίπτωση που το μέγεθος του πίνακα πρέπει να είναι
αρκετά μεγάλο, τότε προτείνεται η δήλωση του πίνακα με τη
μορφή δείκτη και η δέσμευση μνήμης με χρήση της συνάρτησης
malloc()

 Π.χ. η προηγούμενη δήλωση γράφεται ισοδύναμα:

double *arr;
arr = (double*)malloc(500000 * sizeof(double));

 Με την παραπάνω δήλωση, το τμήμα της μνήμης δεν δεσμεύεται


από τη στοίβα, αλλά από μία άλλη μεγαλύτερη περιοχή μνήμης
που ονομάζεται σωρός (heap)

 Τη σχέση μεταξύ πινάκων και δεικτών, καθώς και τη συνάρτηση


malloc() θα τις δούμε σε επόμενες διαλέξεις
Προγραμματισμός Ι
Παρατηρήσεις (V)
 Να αποφεύγετε τη χρήση τελεστή αύξησης ή μείωσης στον δείκτη
θέσης ενός πίνακα, π.χ. μη γράψετε ποτέ κάτι σαν αυτό:
b[i] = a[i++];
 Εξαρτάται απ’ τον μεταγλωττιστή πότε θα γίνει η αύξηση του i

 Επίσης, το λέμε και το ξαναλέμε, μην ξεχνάτε ότι η αρίθμηση


των στοιχείων ενός πίνακα n στοιχείων ξεκινά απ’ το 0 (όχι απ’
το 1) και φτάνει ως και το n-1

 Για την αντιγραφή ενός πίνακα (π.χ. int a[10]) σε έναν άλλον
(π.χ. int b[10]), μην διανοηθείτε να γράψετε κάτι σαν το
παρακάτω:
b = a;
παρόλο που φαίνεται αρκετά «κομψό»

 Απαγορεύεται (!!!!) και θα καταλάβετε γιατί, όταν εξηγήσουμε τη


στενή σχέση μεταξύ πινάκων και δεικτών στο επόμενο κεφάλαιο
Προγραμματισμός Ι

Παρατηρήσεις (VΙ)
 Η C δεν ελέγχει αν κάνετε υπέρβαση των ορίων των θέσεων του
πίνακα, αλλά αφήνει τον προγραμματιστή υπεύθυνο γι’ αυτό... Σε
περίπτωση, όμως, που γίνει υπέρβαση των ορίων του πίνακα, η
συμπεριφορά του προγράμματος είναι απρόβλεπτη.

 Π.χ. δείτε το διπλανό πρόγραμμα

 Ο arr περιέχει τρία στοιχεία και οι


επιτρεπτές τιμές των δεικτών θέσης
είναι από 0 έως 2
 Στην τελευταία επανάληψη (i=3) η
έκφραση arr[3] = 100; αναθέτει
μία τιμή σε ένα στοιχείο που δεν ανήκει στον πίνακα και συγκεκριμένα
την τιμή 100 που υπερεγγράφει (overwrites) το περιεχόμενο των 4
byte της μνήμης ακριβώς μετά το στοιχείο arr[2]
 Αν η συγκεκριμένη μνήμη έχει δεσμευτεί για τη μεταβλητή j, το j
αλλάζει τιμή και το πρόγραμμα θα εμφανίσει 100 αντί για 20...!!!
Προγραμματισμός Ι
Παράδειγμα
 Γράψτε ένα πρόγραμμα το οποίο να δηλώνει έναν πίνακα 5
ακεραίων και να δίνει τις τιμές 10,20,30,40 και 50 στα στοιχεία
του και στη συνέχεια να εμφανίζει στην οθόνη τα στοιχεία του
πίνακα που έχουν τιμή μεγαλύτερη από 20.

Προγραμματισμός Ι

Δήλωση Πίνακα και απόδοση αρχικών τιμών (Ι)


 Η C υποστηρίζει αρκετούς τρόπους απόδοσης αρχικών τιμών στα
στοιχεία ενός πίνακα, ταυτόχρονα με τη δήλωση του πίνακα

1. Τη δήλωση του πίνακα την ακολουθεί ο τελεστής = και οι


τιμές των στοιχείων διαχωρίζονται με κόμμα (,) μέσα σε
άγκιστρα {}

int arr[4] = {10, 20, 30, 40};

Σε αυτό το παράδειγμα:
η τιμή του arr[0] γίνεται 10
η τιμή του arr[1] γίνεται 20
η τιμή του arr[2] γίνεται 30
και η τιμή του τελευταίου στοιχείου arr[3] γίνεται 40

Προγραμματισμός Ι
Δήλωση Πίνακα και απόδοση αρχικών τιμών (ΙΙ)
2. Τη δήλωση του πίνακα την ακολουθεί ο τελεστής = και μέσα
στα άγκιστρα {} δεν υπάρχουν τιμές για όλα τα στοιχεία του
πίνακα

int arr[4] = {10, 20};

Σε αυτό το παράδειγμα:
η τιμή του arr[0] γίνεται 10
η τιμή του arr[1] γίνεται 20
η τιμή του arr[2] και του arr[3] γίνεται 0

(δηλ. για τα στοιχεία του πίνακα που δεν υπάρχει αντιστοίχηση


“1-1”, ο μεταγλωττιστής αποδίδει μηδενικές τιμές)

Προγραμματισμός Ι

Δήλωση Πίνακα και απόδοση αρχικών τιμών (ΙΙΙ)


3. Σε περίπτωση που δεν δηλωθεί το μέγεθος του πίνακα, ο
μεταγλωττιστής δημιουργεί έναν πίνακα που το μέγεθός του
είναι ίσο με το πλήθος των τιμών στη λίστα (έμμεσος ορισμός
μεγέθους του πίνακα)

int arr[] = {10, 20, 30, 40};

 Εδώ ο μεταγλωττιστής δημιουργεί έναν πίνακα ακεραίων με τόσες


θέσεις όσες και οι αρχικές τιμές
 Επομένως, δημιουργεί έναν πίνακα 4 ακεραίων και αναθέτει στα
στοιχεία του τις τιμές 10, 20, 30 και 40 αντίστοιχα, άρα:

η τιμή του arr[0] γίνεται 10


η τιμή του arr[1] γίνεται 20
η τιμή του arr[2] γίνεται 30
η τιμή του arr[3] γίνεται 40
Προγραμματισμός Ι
Δήλωση Πίνακα και απόδοση αρχικών τιμών (ΙV)
4. Αν η δήλωση ενός πίνακα αρχίζει με τη λέξη const, τότε οι
τιμές των στοιχείων του πίνακα είναι σταθερές και το
πρόγραμμα δεν μπορεί να τις αλλάξει

const int b[] = {10, 20, 30, 40};

 Εδώ ο μεταγλωττιστής δεν επιτρέπει την αλλαγή τιμών στα


στοιχεία του πίνακα, δηλ.
η τιμή του b[0] γίνεται μόνιμα 10
η τιμή του b[1] γίνεται μόνιμα 20
η τιμή του b[2] γίνεται μόνιμα 30
η τιμή του b[3] γίνεται μόνιμα 40

 Ο μεταγλωττιστής θα εμφανίσει μήνυμα λάθους σε οποιαδήποτε


προσπάθεια αλλαγής της τιμής κάποιου στοιχείου, όπως π.χ. με
την εντολή: b[0] = 80;

Προγραμματισμός Ι

Παραδείγματα (Ι)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 40 50 60

Προγραμματισμός Ι
Παραδείγματα (ΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 10
20
30
40
50
Προγραμματισμός Ι

Παραδείγματα (IIΙ)
 Ποιες θα είναι οι τιμές των στοιχείων του πίνακα a, κατά την
εκτέλεση του παρακάτω προγράμματος ???

Τα a[0] , a[1], και a[2] γίνονται 5, 3, και 1, αντίστοιχα

Προγραμματισμός Ι
Παραδείγματα (IV)
 Γράψτε ένα πρόγραμμα το οποίο να δηλώνει έναν πίνακα 5
ακεραίων και να δίνει τις τιμές 10,20,30,40,50 στα στοιχεία
του. Στη συνέχεια, το πρόγραμμα να αντιγράφει τα περιεχόμενά
του σε έναν δεύτερο πίνακα και να εμφανίζει τα στοιχεία του
δεύτερου πίνακα στην οθόνη.

Προγραμματισμός Ι

Παραδείγματα (V)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 100
100
100
100 (κακώς,υπερεγγραφή)
??? (ίσως 20, ίσως 100)
Προγραμματισμός Ι
Παραδείγματα (VΙ)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει 10 ακεραίους, να
τους αποθηκεύει σε έναν πίνακα ακεραίων και στη συνέχεια να
εμφανίζει τα στοιχεία του πίνακα με αντίστροφη σειρά.

Προγραμματισμός Ι

Παραδείγματα (VIΙ)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει 10 ακεραίους
αριθμούς, να τους αποθηκεύει σε έναν πίνακα ακεραίων και να
τους εμφανίζει στην οθόνη. Το πρόγραμμα πριν αποθηκεύσει
κάποιον αριθμό στον πίνακα πρέπει να ελέγχει αν αυτός ο
αριθμός ήδη υπάρχει και μόνο αν δεν υπάρχει να τον αποθηκεύει
στον πίνακα. Δηλαδή, όλα τα στοιχεία του πίνακα πρέπει να
είναι διαφορετικά μεταξύ των.

Προγραμματισμός Ι
(συνεχίζεται )
Παραδείγματα (VIΙ)

Προγραμματισμός Ι

Παραδείγματα (VIII)
 Τι κάνει το παρακάτω πρόγραμμα ???

Προγραμματισμός Ι
Παραδείγματα (VIII)
Έξοδος:
Rating Frequency
1 2
2 2
3 2
4 2
5 5
6 11
7 5
8 7
9 1
10 3
Προγραμματισμός Ι

Διδιάστατοι Πίνακες στη C


 Οι διδιάστατοι πίνακες μοιάζουν με τους γνωστούς μαθηματικούς
πίνακες δύο διαστάσεων της άλγεβρας και αποτελούνται και αυτοί
από γραμμές και στήλες

 Για να ορίσουμε έναν διδιάστατο πίνακα πρέπει να δηλώσουμε το


όνομα του πίνακα, τον τύπο δεδομένων των στοιχείων του
πίνακα, καθώς και το πλήθος των γραμμών και των στηλών του

 Η γενική περίπτωση ορισμού ενός διδιάστατου πίνακα είναι:

τύπος_δεδομένων όνομα_πίνακα [πλήθος_γραμμών] [πλήθος_στηλών]

 Το πλήθος των στοιχείων ενός διδιάστατου πίνακα είναι ίσο με το


γινόμενο του πλήθους των γραμμών του επί το πλήθος των στηλών
του

Προγραμματισμός Ι
Δήλωση διδιάστατου Πίνακα
 Π.χ. η εντολή int array[10][5]; δηλώνει έναν διδιάστατο
πίνακα με όνομα array, ο οποίος περιέχει 50 στοιχεία και καθένα
από αυτά τα στοιχεία είναι ένας ακέραιος αριθμός (int)

 Παρομοίως, η εντολή char a[3][4]; δηλώνει έναν διδιάστατο


πίνακα με όνομα a, ο οποίος περιέχει 12 στοιχεία και καθένα από
αυτά τα στοιχεία είναι μία μεταβλητή τύπου char

 Για να αναφερθούμε σε κάποιο στοιχείο ενός διδιάστατου πίνακα


γράφουμε το όνομα του πίνακα συνοδευόμενο από τον αριθμό
γραμμής (ή αλλιώς τον δείκτη γραμμής) και τον αριθμό στήλης (ή
αλλιώς τον δείκτη στήλης) του συγκεκριμένου στοιχείου του πίνακα

 Οι αριθμοί γραμμών και στηλών πρέπει να δηλώνονται μέσα σε


αγκύλες [][]

Προγραμματισμός Ι

Γραφική Αναπαράσταση
Π.χ. αν έχουμε δηλώσει τον πίνακα: int a[3][4]

Παρατηρήστε ότι – όπως και στους μονοδιάστατους πίνακες έτσι


και στους διδιάστατους - η αρίθμηση για τις γραμμές και τις
στήλες ξεκινάει από το 0

Προγραμματισμός Ι
Διδιάστατοι Πίνακες και Μνήμη
 Κατά τη δήλωση του πίνακα, ο μεταγλωττιστής – όπως και στην
περίπτωση των μονοδιάστατων πινάκων – δεσμεύει ένα τμήμα
μνήμης από τη στοίβα (stack) για να αποθηκεύσει τα στοιχεία του

 Π.χ. με τη δήλωση int array[10][5]; ο μεταγλωττιστής


δεσμεύει 200 bytes για να αποθηκεύσει τα 50 στοιχεία του
πίνακα (αφού κάθε στοιχείο του πίνακα – ως ακέραια μεταβλητή –
απαιτεί 4 bytes).

 Τα στοιχεία του πίνακα αποθηκεύονται σε διαδοχικές θέσεις στη


μνήμη ξεκινώντας από τα στοιχεία της 1ης γραμμής,
συνεχίζοντας με τα στοιχεία της 2ης γραμμής, κ.ο.κ.

Προγραμματισμός Ι

Διδιάστατοι Πίνακες και Μνήμη (1/2)


 Συγκεκριμένα, η θέση ενός τυχαίου στοιχείου a[i][j] του
πίνακα a[ROWS][COLS] όπως αποθηκεύεται ο πίνακας στη μνήμη
υπολογίζεται σύμφωνα με τον τύπο:

θέση_στη_μνήμη = (i * COLS) + j + 1

 Π.χ. η θέση του στοιχείου a[2][1] ενός πίνακα με 3 γραμμές


και 4 στήλες (π.χ. double a[3][4]) είναι η:

θέση_στη_μνήμη = (i * COLS) + j + 1 = 2*4 + 1 + 1 = 10

 Δηλαδή, για τον πίνακα double a[3][4] , το στοιχείο a[2][1]


είναι το δέκατο κατά σειρά που αποθηκεύεται στη μνήμη

 Παρατηρήστε, ότι για να υπολογιστεί η θέση του στοιχείου στη


μνήμη δεν χρειάζεται να είναι γνωστή η πρώτη διάσταση του
πίνακα (π.χ. ROWS), αλλά μόνο το πλήθος των στηλών του (π.χ.
COLS)
Προγραμματισμός Ι
Διδιάστατοι Πίνακες και Μνήμη (2/2)
 Θεωρήστε και πάλι έναν διδιάστατο πίνακα (έστω ακεραίων τύπου
int) έστω int x[ROWS][COLS]

 Δεδομένου ότι τα στοιχεία του πίνακα αποθηκεύονται με τη σειρά


στη μνήμη και δεδομένου επίσης ότι το όνομα του πίνακα (x)
ισούται με τη διεύθυνση του πρώτου στοιχείου του πίνακα, ο
μεταγλωττιστής για να βρει τη διεύθυνση μνήμης του στοιχείου
x[i][j] 1) υπολογίζει το μέγεθος σε bytes της γραμμής, δηλ:
row_size = COLS × sizeof(int) και στη συνέχεια
πολλαπλασιάζει με i 2) πολλαπλασιάζει το j με το μέγεθος σε
bytes του ενός στοιχείου και 3) προσθέτει τα δύο γινόμενα στη
διεύθυνση του πρώτου στοιχείου του πίνακα, άρα:

memory_address = x + (i × row_size) + (j × sizeof(int))

 Παρατηρήστε ότι για να βρεθεί η διεύθυνση στη μνήμη ενός


στοιχείου διδιάστατου πίνακα, απαιτείται μόνο ο αριθμός των
στηλών του πίνακα
Προγραμματισμός Ι

Δήλωση Πίνακα και απόδοση αρχικών τιμών (Ι)


 Όπως και στην περίπτωση των μονοδιάστατων πινάκων, η C
υποστηρίζει παρόμοιους τρόπους απόδοσης αρχικών τιμών στα
στοιχεία ενός διδιάστατου πίνακα, ταυτόχρονα με τη δήλωση του
πίνακα

 Σε όλες τις παρακάτω περιπτώσεις οι αρχικές τιμές αποδίδονται


στα στοιχεία του πίνακα ανά γραμμή, ξεκινώντας από τα στοιχεία
της πρώτης γραμμής, συνεχίζοντας στα στοιχεία της δεύτερης
γραμμής, κ.ο.κ.

Προγραμματισμός Ι
Δήλωση Πίνακα και απόδοση αρχικών τιμών (ΙΙ)
1. Τη δήλωση του πίνακα την ακολουθεί ο τελεστής = και οι
τιμές των στοιχείων κάθε γραμμής περικλείονται ανάμεσα σε
εσωτερικά άγκιστρα {} διαχωριζόμενες μεταξύ τους με κόμμα
(,)

Σε αυτό το παράδειγμα:
η τιμή του arr[0][0] γίνεται 10
η τιμή του arr[0][1] γίνεται 20
η τιμή του arr[0][2] γίνεται 30 κ.ο.κ.

Εναλλακτικά, μπορούμε να παραλείψουμε τα εσωτερικά άγκιστρα


και να γράψουμε:

Προγραμματισμός Ι

Δήλωση Πίνακα και απόδοση αρχικών τιμών (ΙΙΙ)


2. Τη δήλωση του πίνακα την ακολουθεί ο τελεστής = και μέσα
στα εσωτερικά άγκιστρα {} δεν υπάρχουν τιμές για όλα τα
στοιχεία της κάθε γραμμής του πίνακα
Σε αυτή την περίπτωση ο μεταγλωττιστής αποδίδει την τιμή 0
στα υπόλοιπα στοιχεία της κάθε γραμμής του πίνακα

Π.χ. στο παραπάνω παράδειγμα:


η τιμή του arr[0][0] γίνεται 10
η τιμή του arr[0][1] γίνεται 20
η τιμή του arr[1][0] γίνεται 40
η τιμή του arr[1][1] γίνεται 50
η τιμή του arr[2][0] γίνεται 70
ενώ οι τιμές των arr[0][2] , arr[1][2] , arr[2][1]
και arr[2][2] γίνονται 0
Προγραμματισμός Ι
Δήλωση Πίνακα και απόδοση αρχικών τιμών (ΙV)
3. Αν έχουμε παραλείψει τιμές για όλα τα στοιχεία κάποιας
γραμμής, τότε ο μεταγλωττιστής αποδίδει την τιμή 0 σε όλα
τα στοιχεία της γραμμής

Π.χ. στο παραπάνω παράδειγμα:


οι τιμές όλων των στοιχείων της 2ης και της 3ης γραμμής
γίνονται 0
4. Αν τέλος έχουμε παραλείψει τα εσωτερικά άγκιστρα {} και
δεν αποδίδουμε τιμές για όλα τα στοιχεία του πίνακα, τότε ο
μεταγλωττιστής αποδίδει την τιμή 0 στα υπόλοιπα στοιχεία
του πίνακα

Π.χ. στο παραπάνω παράδειγμα:


οι τιμές των arr[0][0] και arr[0][1] γίνονται 10 και 20
αντίστοιχα, ενώ όλων των υπολοίπων στοιχείων γίνονται 0
Προγραμματισμός Ι

Δήλωση Πίνακα και απόδοση αρχικών τιμών (V)


5. Το πλήθος των γραμμών δεν είναι υποχρεωτικό να δηλωθεί.
Πρέπει όμως υποχρεωτικά να δηλωθεί ο αριθμός των στηλών

Π.χ. στο παραπάνω παράδειγμα:


ο μεταγλωττιστής θα δημιουργήσει αυτόματα έναν διδιάστατο
πίνακα ακεραίων με δύο γραμμές και τρεις στήλες, διότι οι
αρχικές τιμές που αποδίδονται στα στοιχεία του πίνακα (έξι
τιμές συνολικά) απαιτούν πίνακα με τουλάχιστον δύο γραμμές
για τις τρεις ήδη δηλωμένες στήλες
Συγκεκριμένα, η τιμή:
του arr[0][0] γίνεται 10
του arr[0][1] γίνεται 20
του arr[0][2] γίνεται 30
του arr[1][0] γίνεται 40 κ.ο.κ.
Προγραμματισμός Ι
Δήλωση Πίνακα και απόδοση αρχικών τιμών (VΙ)
6. Οι τρόποι αρχικοποίησης ενός διδιάστατου πίνακα που
παρουσιάστηκαν χρησιμοποιούνται όταν θέλουμε να δώσουμε
συγκεκριμένες τιμές στα στοιχεία ενός πίνακα.
Για πίνακες μεγαλύτερου μεγέθους και όταν οι αρχικές τιμές
του δεν απαιτείται να είναι συγκεκριμένες (π.χ. είναι είτε
ίδιες για όλα τα στοιχεία του πίνακα είτε μπορούν να
παραχθούν εύκολα διότι σχετίζονται μεταξύ τους) συνήθως
χρησιμοποιούνται διπλοί επαναληπτικοί βρόχοι

Προγραμματισμός Ι

Παραδείγματα (Ι)
 Γράψτε ένα πρόγραμμα το οποίο να αρχικοποιεί έναν διδιάστατο
πίνακα 5×5 ως τον μοναδιαίο τετραγωνικό 5×5 πίνακα και να
εμφανίζει τα στοιχεία του πίνακα στην οθόνη υπό τη μορφή
πίνακα 5×5 της άλγεβρας

Προγραμματισμός Ι
Παραδείγματα (ΙΙ)
 Γράψτε ένα πρόγραμμα το οποίο
να διαβάζει ακέραιους αριθμούς,
να τους αποθηκεύει σε έναν
2×4 πίνακα και να εμφανίζει τη
μικρότερη και τη μεγαλύτερη
τιμή κάθε γραμμής του πίνακα

Προγραμματισμός Ι

Προγραμματισμός Ι

Δείκτες
Μνήμη Υπολογιστή
 Η μνήμη RAM (Random Access Memory)
ενός υπολογιστή αποτελείται από πολλές
χιλιάδες θέσεις αποθήκευσης δεδομένων
που έχουν διαδοχική αρίθμηση

 Κάθε θέση ή κελί μνήμης προσδιορίζεται


από μία μοναδική διεύθυνση
 Η διεύθυνση της κάθε θέσης μνήμης είναι
ένας αύξοντας αριθμός με τιμή που
κυμαίνεται από το 0 έως μία μέγιστη τιμή
(η οποία εξαρτάται από το μέγεθος της
διαθέσιμης μνήμης στον συγκεκριμένο
υπολογιστή). Για παράδειγμα, αν ο
υπολογιστής διαθέτει N bytes τότε οι
διευθύνσεις μνήμης κυμαίνονται από 0
έως N-1, όπως φαίνεται στο σχήμα
 Το περιεχόμενο της κάθε θέσης μνήμης
είναι ένας ακέραιος αριθμός με μέγεθος 1
byte Προγραμματισμός Ι

Μνήμη Υπολογιστή και Μεταβλητές


 Όταν δηλώνεται μία μεταβλητή, ο μεταγλωττιστής δεσμεύει τις
απαραίτητες συνεχόμενες θέσεις (bytes) στη μνήμη, για να
αποθηκεύσει την τιμή της

 Όπως ήδη ξέρουμε, κάθε τύπος μεταβλητής απαιτεί συγκεκριμένο


χώρο στη μνήμη

 Π.χ. ο τύπος char απαιτεί 1 byte μνήμης, οι τύποι float και


int απαιτούν 4 bytes, ο τύπος double απαιτεί 8 bytes, κ.ο.κ.

 Όταν μία μεταβλητή καταλαμβάνει πολλές θέσεις μνήμης (δηλ.


περισσότερα από 1 byte), τότε ως διεύθυνση της μεταβλητής
θεωρείται η διεύθυνση της πρώτης θέσης μνήμης (δηλ. του 1ου
byte από τα bytes που καταλαμβάνει η μεταβλητή)

Προγραμματισμός Ι
Παράδειγμα
 Έστω η δήλωση: int a = 10;

 Τότε: Ο μεταγλωττιστής ψάχνει και βρίσκει 4


συνεχόμενες θέσεις μνήμης στη RAM, οι οποίες δεν
πρέπει να έχουν δεσμευτεί για άλλη μεταβλητή, και
τις δεσμεύει για να αποθηκεύσει την τιμή της
μεταβλητής a
 Στο διπλανό σχήμα θεωρούμε ότι η διεύθυνση της Memory
Διεύθυνση Περιεχόμενο
μεταβλητής a αρχίζει στη θέση 5000 Μνήμης Μνήμης
 Έτσι, η τιμή της a (η τιμή 10) θα αποθηκευτεί στις 0
θέσεις μνήμης από 5000 έως και 5003 ξεκινώντας 1
από την οκτάδα με την χαμηλότερη διεύθυνση 2
.
(little-endian architecture) .

Memory

Memory
 Ο μεταγλωττιστής συσχετίζει το όνομα της .
μεταβλητής a, με τη διεύθυνση της μεταβλητής 5000 10
0
 Όταν το πρόγραμμα χρησιμοποιεί το όνομα της 5001 0
0
μεταβλητής, ο μεταγλωττιστής προσπελαύνει 5002 0
0
αυτομάτως τη διεύθυνση της μεταβλητής 5003
.
0
10
.
 Π.χ. με την εντολή a = 80; ο μεταγλωττιστής .
γνωρίζει ότι η διεύθυνση της a είναι η 5000 και N-1
θέτει το περιεχόμενό της ίσο με 80 Memory

Προγραμματισμός Ι

Δήλωση Δείκτη
 Ο δείκτης είναι μία μεταβλητή, στην οποία αποθηκεύεται η
διεύθυνση μνήμης μίας άλλης μεταβλητής

 Η γενική περίπτωση δήλωσης ενός δείκτη είναι:

τύπος_δεδομένων *όνομα_δείκτη;

Παρατηρήσεις
 O τύπος_δεδομένων μπορεί να είναι οποιοσδήποτε από τους
τύπους μεταβλητών της C και δηλώνει τον τύπο της μεταβλητής
στην οποία – συνηθίζουμε να λέμε – «δείχνει ο δείκτης»
 Το όνομα_δείκτη πρέπει να ακολουθεί τους κανόνες
ονοματολογίας της C και να μην υπάρχει άλλη δήλωση με το ίδιο
όνομα μέσα στο πρόγραμμα
 Ο τελεστής * χρησιμοποιείται για να δηλώσει ότι η μεταβλητή είναι
δείκτης

Προγραμματισμός Ι
Παραδείγματα Δήλωσης Δείκτη
int *ptr;
 Η μεταβλητή ptr είναι ένας δείκτης προς κάποια ακέραια
μεταβλητή
 Αυτό σημαίνει, ότι στον δείκτη ptr θα αποθηκευτεί η διεύθυνση
κάποιας ακέραιας μεταβλητής τύπου int

double *pt;
 Η μεταβλητή pt είναι ένας δείκτης προς κάποια πραγματική
μεταβλητή (και μάλιστα, τύπου double)
 Αυτό σημαίνει, ότι στον δείκτη pt θα αποθηκευτεί η διεύθυνση
κάποιας πραγματικής μεταβλητής (και πιο συγκεκριμένα, μιας
μεταβλητής τύπου double)

Προγραμματισμός Ι

Παρατηρήσεις (1/3)
 Με τη δήλωση:

int *ptr;

ο ptr δηλώνεται ως δείκτης σε μεταβλητές τύπου int, έτσι, ως


τιμή του ptr μπορεί να αποθηκευτεί η διεύθυνση μνήμης
οποιασδήποτε μεταβλητής τύπου int

 Γενικά, αν ο ptr είναι «δείκτης σε τύπο» T, τότε η έκφραση


*ptr είναι τύπου T

 Π.χ. στην προηγούμενη δήλωση (int *ptr;), η έκφραση *ptr


είναι τύπου int

 Οι μεταβλητές δείκτες, μπορούν να δηλωθούν ταυτόχρονα με


άλλες μεταβλητές του ίδιου τύπου, π.χ.:
int *ptr, i, j, k;
Προγραμματισμός Ι
Παρατηρήσεις (2/3)
 Σημειώνεται ότι επιτρέπεται να χρησιμοποιήσουμε τον τελεστή *, όχι
δίπλα στο όνομα της μεταβλητής, αλλά δίπλα στον τύπο δεδομένων

 Π.χ.: int* ptr;

 Αν και κάποιοι προγραμματιστές προτιμούν την παραπάνω γραφή,


προτείνουμε να μην τη χρησιμοποιείτε, διότι είναι δυνατόν να
προκαλέσει σύγχυση όταν δηλώνονται περισσότερες της μίας
μεταβλητές

 Π.χ., με τη δήλωση: int* p1, p2;


το p1 δηλώνεται ως «δείκτης σε ακέραιο» ενώ το p2 ως ακέραιος

Σωστά??? Ή μήπως και οι δύο μεταβλητές είναι «δείκτες σε


ακέραιο»???

 Σωστά, αλλά προτιμήστε τη δήλωση: int *p1, p2;


που είναι περισσότερο ξεκάθαρη...
Προγραμματισμός Ι

Παρατηρήσεις (3/3)
 Όταν δηλώνεται μία μεταβλητή-δείκτης, ο μεταγλωττιστής, όπως κάνει
και για οποιαδήποτε μεταβλητή, δεσμεύει τις απαραίτητες θέσεις μνήμης
για να αποθηκεύσει την τιμή του

 Το επόμενο πρόγραμμα εμφανίζει πόσα bytes μνήμης δεσμεύτηκαν για τον


δείκτη ptr με χρήση του τελεστή sizeof

 Ta bytes που δεσμεύονται για μία μεταβλητή-δείκτη είναι 4, ανεξάρτητα


από τον τύπο δεδομένων στον οποίο δείχνει ο δείκτης

 Δηλαδή, στο προηγούμενο παράδειγμα είτε έχουμε τη δήλωση


char *ptr; είτε: float *ptr; είτε: double *ptr;
το αποτέλεσμα είναι 4 Προγραμματισμός Ι
Απόδοση τιμής σε Δείκτη
 Ένας δείκτης, πριν χρησιμοποιηθεί, πρέπει να έχει σαν τιμή τη διεύθυνση
κάποιας μεταβλητής ή, ισοδύναμα, να «δείχνει» σε κάποια υπαρκτή
μεταβλητή
 Για να βρούμε τη διεύθυνση κάποιας μεταβλητής χρησιμοποιούμε τον
τελεστή διεύθυνσης & πριν από το όνομα της μεταβλητής
 Υπενθυμίζεται ότι η διεύθυνση είναι η θέση της μεταβλητής στη μνήμη του
υπολογιστή και δεν έχει καμία σχέση με την τιμή της μεταβλητής

Προγραμματισμός Ι

Παρατηρήσεις
 Για την εμφάνιση στην οθόνη της διεύθυνσης μνήμης μίας μεταβλητής
συνήθως χρησιμοποιείται το προσδιοριστικό %p, το οποίο εμφανίζει τη
διεύθυνση σε δεκαεξαδική μορφή (μπορούμε να χρησιμοποιήσουμε και το
προσδιοριστικό %d, για την εμφάνιση της διεύθυνσης σε δεκαδική μορφή)

 Όταν εκχωρείται η διεύθυνση μίας μεταβλητής σε έναν δείκτη, ο δείκτης


πρέπει να έχει δηλωθεί σαν δείκτης στον ίδιο τύπο με τη μεταβλητή
Προσοχή λοιπόν σε λάθη όπως αυτό του παρακάτω παραδείγματος
int *ptr;
float a;
ptr = &a;

 Η τιμή που εκχωρείται σε έναν δείκτη πρέπει να είναι η διεύθυνση κάποιας


υπαρκτής μεταβλητής και όχι μία σταθερή αριθμητική τιμή
Στο παρακάτω παράδειγμα ο μεταγλωττιστής θα εμφανίσει μήνυμα λάθους

int *ptr;
ptr = 1000;
Προγραμματισμός Ι
Η ειδική τιμή NULL (Ι)
 Υπενθυμίζεται ότι, όταν δηλώνεται μία μεταβλητή, τότε ο
μεταγλωττιστής αναθέτει στη μεταβλητή μία τυχαία τιμή (τιμή
«σκουπίδι»)

 Επομένως, όταν δηλώνεται μία μεταβλητή-δείκτης, τότε η αρχική


τιμή του δείκτη αυτού είναι μία τυχαία διεύθυνση μνήμης

 Στο παρακάτω παράδειγμα, εμφανίζεται στην οθόνη η τυχαία τιμή


που εκχώρησε ο μεταγλωττιστής στον δείκτη ptr

Προγραμματισμός Ι

Η ειδική τιμή NULL (ΙΙ)


 Όταν θέλουμε να δηλώσουμε ρητά ότι ένας δείκτης δεν δείχνει
πουθενά (null pointer), τότε του αναθέτουμε την τιμή NULL

 Η τιμή NULL είναι μία ειδική τιμή ίση με το μηδέν (0)

 Επί της ουσίας πρόκειται για μια μακροεντολή με όνομα NULL και
τιμή ίση με μηδέν (0) ή – ακόμα καλύτερα – με τιμή μηδέν (0)
προσαρμοσμένη στον τύπο void*, δηλαδή τιμή (void*)0

 Η μακροεντολή NULL δηλώνεται σε διάφορα header files όπως


π.χ. και στο stdio.h
 Επομένως, το επόμενο παράδειγμα εμφανίζει την τιμή 0

Προγραμματισμός Ι
Η ειδική τιμή NULL (ΙΙΙ)
 Παρόλο που είναι δυνατόν να χρησιμοποιηθεί απευθείας κι η τιμή 0
αντί της NULL, σε περίπτωση που πρόκειται για μεταβλητή-δείκτη
είναι προτιμότερο να χρησιμοποιείτε τη μακροεντολή NULL για να
αποφεύγεται η παρακάτω σύγχυση:

 Π.χ., η ανάθεση ptr = 0; μπορεί να μας ξεγελάσει


 Το ptr είναι δείκτης (λόγω ονόματος) ή μήπως απλή αριθμητική
μεταβλητή (λόγω της ανάθεσης του μηδενός)?

 Αντιθέτως, η ανάθεση ptr = NULL; κάνει αμέσως ξεκάθαρο ότι ο


ptr είναι δείκτης

 Η τιμή ενός δείκτη μπορεί να συγκριθεί έναντι της τιμής NULL,


όπως παρακάτω:

if(ptr != NULL) /* Ισοδύναμο με if(ptr) */


if(ptr == NULL) /* Ισοδύναμο με if(!ptr) */
Προγραμματισμός Ι

Χρήση Δείκτη
 Για να αποκτήσουμε πρόσβαση στο περιεχόμενο κάποιας διεύθυνσης
μνήμης με χρήση δείκτη, χρησιμοποιούμε τον τελεστή * (dereference
operator ή αλλιώς indirection operator) πριν από το όνομα του δείκτη
 Π.χ.

 Η έκφραση *ptr είναι ισοδύναμη με το περιεχόμενο της διεύθυνσης στην


οποία δείχνει ο δείκτης ptr
 Αφού ο ptr δείχνει στη διεύθυνση της μεταβλητής a, το *ptr είναι ένας
διαφορετικός τρόπος έκφρασης του a, οπότε, το πρόγραμμα εμφανίζει 10
Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Πριν χρησιμοποιηθεί κάποια μεταβλητή-δείκτης θα πρέπει να έχει
αρχικοποιηθεί, δηλαδή να της έχει εκχωρηθεί μία υπαρκτή διεύθυνση,
δηλαδή ο δείκτης να δείχνει στη διεύθυνση κάποιας μεταβλητής
 Το επόμενο πρόγραμμα θα εμφανίσει μήνυμα λάθους κατά την εκτέλεσή
του, γιατί στην εντολή i = *ptr; χρησιμοποιείται ο δείκτης ptr, ο
οποίος δεν δείχνει στη διεύθυνση κάποιας μεταβλητής (δεν έχει
αρχικοποιηθεί)

 Συνήθως, σε Unix/Linux περιβάλλον το παραπάνω λάθος υποδεικνύεται


με το μήνυμα ”Segmentation fault”

Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 Το επόμενο πρόγραμμα λειτουργεί σωστά, γιατί τώρα ο δείκτης ptr
δείχνει στη διεύθυνση κάποιας υπαρκτής μεταβλητής (της μεταβλητής j)
πριν χρησιμοποιηθεί στην εντολή i = *ptr;
 Επομένως, αφού ο δείκτης ptr δείχνει στη διεύθυνση της μεταβλητής j,
το *ptr θα είναι ίσο με την τιμή του j, δηλαδή 20
 Άρα, με την εντολή i = *ptr; η τιμή του i θα γίνει ίση με 20

Έξοδος: Val = 20

Προγραμματισμός Ι
Παρατηρήσεις (ΙΙΙ)
 Οι τελεστές * (περιεχόμενο διεύθυνσης μνήμης που δείχνει ο δείκτης) και
& (διεύθυνση μνήμης μιας μεταβλητής) είναι μεταξύ τους συμπληρωματικοί ή
αντίστροφοι (αλλιώς λέμε ότι αλληλοαναιρούνται ή αλληλοεξουδετερώνονται ή
ακυρώνει ο ένας τον άλλον)
Έξοδος (π.χ.):
The address of a is 0028F958
The value of ptr is 0028F958

The value of a is 21
The value of *ptr is 21

Showing that * and & cancel each other


&*ptr = 0028F958
*&ptr = 0028F958

Προγραμματισμός Ι

Παρατηρήσεις (ΙV)
 Δεδομένου ότι οι χαρακτήρες /* σηματοδοτούν την αρχή ενός σχολίου,
σε μία έκφραση σαν την παρακάτω:

a = b/*ptr;

ο,τιδήποτε ακολουθεί το /* έως και να συναντηθεί το */ στον κώδικά


μας, θα θεωρηθεί σχόλιο και η μεταγλώττιση θα αποτύχει

 Σε αυτήν την περίπτωση να αφήνετε ένα κενό ή να κάνετε χρήση της


παρένθεσης

 Π.χ.: a = b/ *ptr;

ή a = b/(*ptr);

Προγραμματισμός Ι
Δεν ξεχνώ λοιπόν...

Δήλωση μεταβλητών

Δήλωση «μεταβλητών
δεικτών»

Ανάθεση τιμών στους


Δείκτες (τις διευθύνσεις
υπαρκτών μεταβλητών)

Απόδοση τιμής σε κάποια


μεταβλητή, μέσω δείκτη (το
περιεχόμενο της διεύθυνσης
στην οποία δείχνει ο δείκτης)

Προγραμματισμός Ι

Παραδείγματα (Ι)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 30

Προγραμματισμός Ι
Παραδείγματα (ΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 30

Προγραμματισμός Ι

Παραδείγματα (ΙΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 11

Προγραμματισμός Ι
Παραδείγματα (ΙV)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 0 1 2

Προγραμματισμός Ι

Παραδείγματα (V)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 200

Προγραμματισμός Ι
Παραδείγματα (VΙ)
 Γράψτε ένα πρόγραμμα το οποίο κάνοντας χρήση ενός δείκτη για
να διαβάσει μία δεκαδική τιμή και να εμφανίζει την απόλυτη τιμή
της τιμής αυτής.

Προγραμματισμός Ι

Ο δείκτης void* (1/2)


 Ένας δείκτης σε τύπο void είναι ένας «γενικός» δείκτης, με την
έννοια ότι μπορεί να δείξει σε μία μεταβλητή οποιουδήποτε τύπου

 Ένας δείκτης μπορεί να μετατραπεί σε τύπο void και πάλι πίσω


στον αρχικό τύπο χωρίς απώλεια πληροφορίας

 Σημειώστε ότι, αν δεν είναι δείκτης σε void και οι τύποι είναι


διαφορετικοί, πρέπει να γίνει προσαρμογή

 Π.χ.

Προγραμματισμός Ι
Ο δείκτης void* (2/2)
 Για να προσπελάσουμε τη μεταβλητή με χρήση ενός void* δείκτη
πρέπει να προσαρμόσουμε τον τύπο του στον τύπο της μεταβλητής,
ώστε ο μεταγλωττιστής να γνωρίζει το αντίστοιχο μέγεθος, όπως
φαίνεται στο παρακάτω πρόγραμμα:

 Για να αποκτήσουμε πρόσβαση στο


περιεχόμενο της ακέραιας
μεταβλητής i, προσαρμόζουμε τον
τύπο του δείκτη σε int*
 Με αυτόν τον τρόπο, μπορούμε να
αλλάξουμε την τιμή της μεταβλητής
στην οποία δείχνει ο «γενικός»
δείκτης
 Επομένως, το πρόγραμμα θα
εμφανίσει: 30

Προγραμματισμός Ι

Χρήση της λέξης const στη δήλωση ενός δείκτη


 Χρησιμοποιούμε τη δεσμευμένη λέξη const κατά τη δήλωση του
δείκτη, όταν επιθυμούμε μία μεταβλητή-δείκτης :
 είτε να μην μπορεί να αλλάξει την τιμή της μεταβλητής στην οποία
δείχνει (χρήση της λέξης const πριν τον τύπο δεδομένων)
 είτε να μην μπορεί να δείξει σε κάποια άλλη μεταβλητή (χρήση της
λέξης const πριν το όνομα του δείκτη)

 Δείτε λοιπόν, τι επιτρέπεται και τι όχι, στα παρακάτω


παραδείγματα
Ο δείκτης ptr δεv μπορεί να
αλλάξει την τιμή της μεταβλητής
στην οποία δείχνει (της i)
Ωστόσο, επιτρέπεται να “δείξει” σε
κάποια άλλη μεταβλητή ίδιου τύπου
(εδώ της j)
Ο δείκτης ptr δεv μπορεί να
“δείξει” σε άλλη μεταβλητή (όπως
π.χ. εδώ στην j), παρά μόνο στην
i (ωστόσο, επιτρέπεται να αλλάξει
την τιμή του i)
Προγραμματισμός Ι
Χρήση της λέξης const στη δήλωση ενός δείκτη
 Χρησιμοποιώντας 2 φορές τη δεσμευμένη λέξη const κατά τη
δήλωση του δείκτη, μπορούμε προφανώς να τον αναγκάσουμε και να
μην μπορεί να αλλάξει την τιμή της μεταβλητής στην οποία δείχνει
και (ταυτόχρονα) να μην μπορεί να δείξει σε κάποια άλλη μεταβλητή

 Π.χ.

Προγραμματισμός Ι

Αριθμητική Δεικτών
 Η αριθμητική δεικτών αφορά στην εκτέλεση αριθμητικών πράξεων με
δείκτες
 Σύμφωνα με το πρότυπο, η αριθμητική δεικτών παράγει αξιόπιστα
αποτελέσματα όταν εφαρμόζεται σε στοιχεία του ίδιου πίνακα, αλλιώς
το αποτέλεσμα είναι απροσδιόριστο

 Εξαίρεση στον παραπάνω κανόνα αποτελεί η διεύθυνση του πρώτου


στοιχείου μετά το τέλος του πίνακα (το πρότυπο εγγυάται ότι μπορεί
να χρησιμοποιηθεί η θέση αυτή στην αριθμητική δεικτών)
 Oι επιτρεπτές πράξεις είναι:
 Η πρόσθεση ακεραίου σε δείκτη
 Η αφαίρεση ακεραίου από δείκτη και
 Η αφαίρεση δύο δεικτών
 Οι παραπάνω πράξεις έχουν ορισμένες ιδιαιτερότητες και για τον
λόγο αυτό, απαιτείται ιδιαίτερη προσοχή
 Ως λειτουργίες, επίσης επιτρέπονται η σύγκριση δεικτών καθώς και
η ανάθεση σε έναν δείκτη της τιμής 0 ή η σύγκρισή του με την τιμή 0
Προγραμματισμός Ι
Δείκτες και Ακέραιοι (Αύξηση Δεικτών)
 Θεωρώντας ότι ο δείκτης ptr δείχνει σε ένα στοιχείο ενός
πίνακα, η πρόσθεση ενός θετικού ακέραιου n στον ptr, π.χ.:

ptr = ptr + n;

αυξάνει την τιμή του δείκτη κατά n * μέγεθος του τύπου στον
οποίο δείχνει ο ptr και τον κάνει να δείχνει στη διεύθυνση του
n-οστού στοιχείου μετά από αυτό που έδειχνε

 Αν το αποτέλεσμα της πράξης είναι εκτός των ορίων του πίνακα,


το αποτέλεσμα είναι απροσδιόριστο (εξαιρείται η διεύθυνση της
πρώτης θέσης αμέσως μετά το τέλος του πίνακα, η οποία
θεωρείται έγκυρη), π.χ, αν ο ptr έχει δηλωθεί σαν δείκτης σε:
 char, τότε η τιμή του δείκτη αυξάνεται κατά n*1 = n, αφού το
μέγεθος του τύπου char είναι 1 byte
 int ή float, τότε η τιμή του δείκτη αυξάνεται κατά n*4, αφού το
μέγεθος των τύπων int και float είναι 4 bytes
 double, τότε η τιμή του δείκτη αυξάνεται κατά n*8, αφού το μέγεθος
του τύπου double είναι 8 bytes
Προγραμματισμός Ι

Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος???

Ο ptr αυξάνεται κατά 8, (όχι κατά 2), δεδομένου ότι ο ptr έχει
δηλωθεί ως δείκτης σε int
Έτσι, το πρόγραμμα εμφανίζει δύο διευθύνσεις (ως ακέραιο του
δεκαδικού συστήματος) με τη δεύτερη να είναι κατά 8 θέσεις
μεγαλύτερη απ’ την πρώτη (π.χ. αν η πρώτη ήταν η διεύθυνση
89234834 η δεύτερη θα είναι η 89234842) κι επειδή ο ptr δείχνει
στο arr[2], το πρόγραμμα εμφανίζει 30, επίσης.
Προγραμματισμός Ι
Δείκτες και Ακέραιοι (Μείωση Δεικτών)
 Όμοια με την πρόσθεση, η αφαίρεση ενός θετικού ακέραιου από έναν
δείκτη σε μία ανάθεση όπως η παρακάτω :

ptr = ptr - n;

μειώνει την τιμή του δείκτη κατά n * μέγεθος του τύπου στον
οποίο δείχνει ο δείκτης και κάνει τον δείκτη να δείχνει στη
διεύθυνση του n-οστου στοιχείου πριν από αυτό που έδειχνε

 Π.χ.
Η δεύτερη διεύθυνση θα είναι κατά 8
θέσεις μικρότερη απ’την πρώτη και,
όπως και κατά την πρόσθεση, το
αποτέλεσμα θα είναι έγκυρο αν ο
δείκτης δείχνει σε ένα στοιχείο εντός
του πίνακα
Προγραμματισμός Ι

Δείκτες και τελεστές ++ ή -- (1/2)


 Η χρήση των τελεστών αύξησης ή μείωσης (++ και --) επιφέρει
έγκυρο αποτέλεσμα, ακόμα κι αν ο δείκτης δεν δείχνει σε κάποιο
στοιχείο ενός πίνακα
 Σε αυτή την περίπτωση ο δείκτης αυξάνεται ή μειώνεται κατά το
μέγεθος του τύπου στον οποίο δείχνει
Π.χ.:

Έξοδος: Το πρόγραμμα εμφανίζει δύο διευθύνσεις με την


πρώτη να είναι κατά 8 θέσεις μεγαλύτερη από τη δεύτερη, αφού
ο ptr δείχνει σε double τύπο δεδομένων
Προγραμματισμός Ι
Δείκτες και τελεστές ++ ή -- (2/2)
 Η συνδυαστική χρήση των τελεστών ++ και -- με τον τελεστή *
είναι πολύ συνηθισμένη στη διαχείριση πινάκων με χρήση δείκτη

 Το αποτέλεσμα της έκφρασης εξαρτάται από τη θέση των τελεστών


με βάση τον πίνακα προτεραιοτήτων

 Ας δούμε τις διαφορετικές περιπτώσεις συνδυαστικής χρήσης του


τελεστή * με τον τελεστή ++ (αντίστοιχα είναι και για τον --)

 i = (*ptr)++; πρώτα εκχωρείται η τιμή του *ptr στο i και


μετά αυξάνεται κατά ένα η τιμή του *ptr
 i = *ptr++; πρώτα εκχωρείται η τιμή του *ptr στο i και μετά
αυξάνεται η τιμή του ptr, ανάλογα με τον τύπο του
 i = ++*ptr; πρώτα αυξάνεται κατά ένα η τιμή του *ptr και
μετά αυτή εκχωρείται στο i
 i = *++ptr; πρώτα αυξάνεται η τιμή του ptr και μετά
εκχωρείται στο i η τιμή του *ptr
Προγραμματισμός Ι

Αφαίρεση Δεικτών
 Το αποτέλεσμα της αφαίρεσης δεικτών είναι ο αριθμός των
στοιχείων που μεσολαβούν μεταξύ τους

 Οι δείκτες πρέπει να δείχνουν σε στοιχεία του ίδιου πίνακα ή στην


αμέσως επόμενη θέση από το τέλος του πίνακα

 Αν η τιμή του δείκτη που αφαιρείται είναι μεγαλύτερη, τότε το


αποτέλεσμα είναι το ίδιο, απλά με αρνητικό πρόσημο

 Για παράδειγμα, αν ο p1 δείχνει στο δεύτερο στοιχείο και ο p2


στο πέμπτο στοιχείο του ίδιου πίνακα, το αποτέλεσμα της πράξης
p2–p1 είναι 3, ενώ της πράξης p1–p2 είναι -3

Προγραμματισμός Ι
Σύγκριση Δεικτών
 Το αποτέλεσμα της σύγκρισης δύο δεικτών με τον τελεστή == ή τον
τελεστή != είναι πάντοτε αξιόπιστο

 Αντιθέτως, το αποτέλεσμα της σύγκρισης δύο δεικτών με τους


τελεστές <, <=, >, ή >= θεωρείται αξιόπιστο μόνο αν οι δείκτες
δείχνουν στο ίδιο αντικείμενο, π.χ. πίνακας ή δομή, αλλιώς είναι
απροσδιόριστο

 Π.χ., αν θέλουμε να ελέγξουμε αν δύο δείκτες ptr1 και ptr2


δείχνουν στην ίδια διεύθυνση μνήμης (ή όχι) μπορούμε να γράψουμε:
if(ptr1 == ptr2) ή αντίστοιχα: if(ptr1 != ptr2)

 Π.χ., αν ο p1 δείχνει στο


δεύτερο στοιχείο και ο p2
στο πέμπτο στοιχείο του
ίδιου πίνακα, το
αποτέλεσμα της πράξης
p2>p1 είναι 1, ενώ της
πράξης p1>p2 είναι 0 Προγραμματισμός Ι

Παρατηρήσεις
 Εκτός από τις λειτουργίες που ήδη περιγράφησαν, καμία άλλη
αριθμητική πράξη δεν επιτρέπεται να εκτελεστεί με τη συμμετοχή
κάποιου δείκτη

 Π.χ. για τη δήλωση double *ptr, *ptr1, *ptr2;

οι εντολές:

πολλαπλασιασμού ptr *= 2;

πρόσθεσης δεκαδικού ptr += 7.5;

πρόσθεσης δεικτών ptr1 + ptr2;

δεν είναι επιτρεπτές εκφράσεις ακόμα κι αν οι δείκτες αυτοί


δείχνουν σε στοιχεία του ίδιου πίνακα

Προγραμματισμός Ι
Παραδείγματα (Ι)
 Τι εμφανίζει η 2η printf() του παρακάτω προγράμματος ???

Έξοδος: Το τυχαίο περιεχόμενο των


επόμενων 4 bytes μετά τη θέση της
μεταβλητής j στη μνήμη. Αν τύχει να
είναι η διεύθυνση της μεταβλητής i,
τότε το πρόγραμμα θα εμφανίσει 10,
αλλιώς θα εμφανίσει μια τυχαία τιμή.
Προγραμματισμός Ι

Παραδείγματα (ΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: i=40 j=60 k=130

Προγραμματισμός Ι
Παραδείγματα (ΙII)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 100 120 150

Προγραμματισμός Ι

Παραδείγματα (ΙV)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: Value = 20

Προγραμματισμός Ι
Παραδείγματα (V)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: Value = 120

Προγραμματισμός Ι

Παραδείγματα (VI)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: Value = 300


Προγραμματισμός Ι
Παραδείγματα (VII)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 2 5
Προγραμματισμός Ι

Παραδείγματα (VIIΙ)
 Γράψτε ένα πρόγραμμα το οποίο να διαβάζει δύο ακέραιους, τους οποίους
να αποθηκεύει σε δύο ακέραιες μεταβλητές με χρήση δύο δεικτών σε
ακέραιους. Με χρήση αυτών των δεικτών, αποθηκεύστε το άθροισμά τους
σε μία τρίτη μεταβλητή με χρήση ενός επιπλέον δείκτη σε ακέραιο και
εμφανίστε τα περιεχόμενα της κάθε μεταβλητής με χρήση των παραπάνω
δεικτών.

Προγραμματισμός Ι
Δείκτες και Πίνακες - Εισαγωγή
 Τα στοιχεία ενός πίνακα αποθηκεύονται σε διαδοχικές θέσεις
μνήμης, με το πρώτο στοιχείο στη χαμηλότερη διεύθυνση

 Τα επόμενα στοιχεία του πίνακα αποθηκεύονται στις υψηλότερες


διευθύνσεις

 Το πόσο υψηλότερα, εξαρτάται από τον τύπο δεδομένων του


πίνακα (char, int, float, ..)

 Π.χ. σε έναν πίνακα χαρακτήρων (char), κάθε στοιχείο του


πίνακα βρίσκεται 1 byte μετά από το προηγούμενο στοιχείο και η
διεύθυνση κάθε στοιχείου είναι 1 θέση υψηλότερα από τη
διεύθυνση του προηγούμενου στοιχείου

 Παρομοίως, σε έναν πίνακα ακεραίων (int), κάθε στοιχείο του


πίνακα βρίσκεται 4 bytes μετά από το προηγούμενο στοιχείο και η
διεύθυνση κάθε στοιχείου είναι 4 θέσεις υψηλότερα από τη
διεύθυνση του προηγούμενου στοιχείου

Προγραμματισμός Ι

Παράδειγμα
 Έστω η δήλωση του πίνακα:

int arr[3];

 Αν θεωρήσουμε ότι η διεύθυνση του πρώτου στοιχείου είναι η θέση


100 στη μνήμη, τότε η διεύθυνση του δεύτερου στοιχείου είναι η
104 και του τρίτου η 108

 Αντίστοιχα, η τιμή του πρώτου στοιχείου του πίνακα (του arr[0])


αποθηκεύεται στις θέσεις 100 έως και 103, η τιμή του δεύτερου
στοιχείου (του arr[1]) στις θέσεις 104 έως και 107 και η τιμή
του τρίτου στοιχείου (του arr[2]) στις θέσεις 108 έως και 111

Προγραμματισμός Ι
Δείκτες και Πίνακες (Ι)
 Το όνομα ενός πίνακα (χωρίς αγκύλες) μπορεί να χρησιμοποιηθεί
ως δείκτης στο πρώτο του στοιχείο

 Με άλλα λόγια, η τιμή του ονόματος του πίνακα (χωρίς αγκύλες)


ισούται με τη διεύθυνση του πρώτου στοιχείου του πίνακα

 Π.χ. αν έχει δηλωθεί ο πίνακας

int arr[50];

τότε η τιμή του arr είναι ίση με τη διεύθυνση του πρώτου


στοιχείου του πίνακα (δηλ. ίση με &arr[0]) και αν η μνήμη του
υπολογιστή ήταν όπως αυτή του παρακάτω σχήματος, η τιμή τους
θα ήταν ίση με 100

 Συμπερασματικά, οι εκφράσεις arr και &arr[0] είναι ισοδύναμες

Προγραμματισμός Ι

Δείκτες και Πίνακες (ΙΙ)


 Υπενθυμίζεται από την αριθμητική δεικτών, ότι, όταν προστίθεται
ένας ακέραιος αριθμός n σε έναν δείκτη (που δείχνει σε στοιχείο
πίνακα), τότε ο δείκτης δείχνει σε μία νέα διεύθυνση που απέχει
(σε bytes):
n * μέγεθος του τύπου (στον οποίο δείχνει)
 Βάσει της λογικής αυτής, η έκφραση arr+1 μπορεί να
χρησιμοποιηθεί ως δείκτης που δείχνει στο δεύτερο στοιχείο του
πίνακα, άρα οι εκφράσεις arr+1 και &arr[1] είναι ισοδύναμες,
αφού και οι δύο είναι ίσες με τη διεύθυνση του δεύτερου στοιχείου
του πίνακα, η έκφραση arr+2 μπορεί να χρησιμοποιηθεί ως
δείκτης που δείχνει στο τρίτο στοιχείο του πίνακα κ.ο.κ.
 Δηλαδή, γενικά ισχύει ότι:
arr == &arr[0]
arr + 1 == &arr[1]
arr + 2 == &arr[2]

arr + n == &arr[n]
Προγραμματισμός Ι
Δείκτες και Πίνακες (ΙΙI)
 Έτσι, το παρακάτω πρόγραμμα εμφανίζει 4 φορές την ίδια τιμή

 Σημειώστε ότι παρόλο που η έκφραση &arr εμφανίζει την ίδια τιμή,
είναι διαφορετική απ’ τις υπόλοιπες
 Η έκφραση &arr είναι δείκτης σε ολόκληρο τον πίνακα, ενώ οι
υπόλοιπες είναι δείκτες στο πρώτο στοιχείο του πίνακα (ως τιμές είναι
ίδιες, αλλά διαφέρουν στον τύπο τους)
 Συγκεκριμένα, ο τύπος &arr είναι “δείκτης σε έναν πίνακα 5
ακεραίων”, ενώ ο τύπος των υπολοίπων είναι “δείκτης σε ακέραιο”.
Ζόρικο???
 Και για να το δυσκολέψουμε ακόμα περισσότερο, αν γράψουμε
&arr+1 αντί για &arr, ποια θα είναι η διαφορά με τις άλλες τιμές
(θα είναι ένα, τέσσερα, ή μήπως κάτι άλλο, ...)?
Για να σας δω...  Προγραμματισμός Ι

Δείκτες και Πίνακες (ΙV)


 Αφού το όνομα ενός πίνακα μπορεί να χρησιμοποιηθεί ως δείκτης
στο πρώτο στοιχείο του, τότε το περιεχόμενό του θα είναι ίσο με
την τιμή του πρώτου στοιχείου του

 Δηλαδή, ισχύει ότι το *arr είναι ίσο με arr[0]

 Αντίστοιχα, αφού το arr+1 είναι δείκτης στο δεύτερο στοιχείο του


πίνακα, τότε ισχύει ότι *(arr+1) είναι ίσο με arr[1], κ.ο.κ.
 Δηλαδή, γενικά ισχύει ότι (προσοχή στις παρενθέσεις):
*arr == arr[0]
*(arr + 1) == arr[1]
*(arr + 2) == arr[2]

*(arr + n) == arr[n]
 Ο τελεστής * έχει υψηλότερη προτεραιότητα απ΄τον τελεστή +
 Επομένως, οι εκφράσεις *(arr+n) και *arr + n δεν είναι
ισοδύναμες (αφού δεν αποτιμούνται με τον ίδιο τρόπο)
Προγραμματισμός Ι
Παράδειγμα (Ι)

Πιθανή
Έξοδος:

Προγραμματισμός Ι

Παράδειγμα (ΙΙ)

Πιθανή
Έξοδος:

Προγραμματισμός Ι
Παρατηρήσεις (Ι)
 Όταν το όνομα ενός πίνακα χρησιμοποιείται ως δείκτης, η C τον
μεταχειρίζεται ως const δείκτη, συνεπώς, δεν επιτρέπεται ούτε
να αλλάξει την τιμή του ούτε να δείξει σε κάποια άλλη διεύθυνση

 Το όνομα ενός πίνακα δεν είναι δηλαδή μία τροποποιήσιμη lvalue;


Η τιμή του θα είναι μόνιμα ίση με τη διεύθυνση του πρώτου του
στοιχείου
 Έτσι, αν στο τελευταίο παράδειγμα γράφαμε arr++; ή arr = &i;
ο μεταγλωττιστής θα εμφάνιζε μήνυμα λάθους για μη επιτρεπτή
ενέργεια
 Συνεπώς, μάλλον τώρα μπορείτε να καταλάβετε γιατί δεν
επιτρέπεται να γράψετε b = a; Για να αντιγράψετε τα στοιχεία
ενός πίνακα a στον αντίστοιχο πίνακα b
 Παρόλα αυτά, επιτρέπεται να αντιγράψουμε την τιμή του ονόματος
ενός πίνακα σε μία άλλη μεταβλητή δείκτη (όπως π.χ. κάναμε
γράφοντας ptr = arr; στο τελευταίο παράδειγμα) και να
χρησιμοποιήσουμε τον δείκτη ptr για να έχουμε πρόσβαση στα
στοιχεία του πίνακα Προγραμματισμός Ι

Παρατηρήσεις (ΙΙ)
 Παρόλο που υπάρχει στενή σχέση μεταξύ πινάκων και δεικτών,
πρέπει να είναι ξεκάθαρο ότι ένας πίνακας δεν είναι δείκτης
καθώς και ένας δείκτης δεν είναι πίνακας...

 Π.χ., οι δηλώσεις int a[50]; και int *a; είναι αρκετά


διαφορετικές
 Με την πρώτη δεσμεύεται μνήμη για 50 ακεραίους (π.χ. εδώ
δεσμεύονται 50*4 = 200 bytes) και το όνομα a αναφέρεται πάντα
στην ίδια θέση μνήμης. Όπως είπαμε, δεν μπορεί να αλλάξει η τιμή
του, δηλαδή δεν μπορούμε να γράψουμε a = arr;
 Με τη δεύτερη δήλωση δεσμεύεται μνήμη (τυπικά δεσμεύονται 4
bytes) για να αποθηκευτεί η τιμή του δείκτη. Η τιμή του μπορεί να
αλλάζει, ώστε να δείχνει σε διαφορετικές διευθύνσεις, δηλαδή
μπορούμε να γράψουμε a = arr;

Προγραμματισμός Ι
Παρατηρήσεις (ΙII)
 Απλά να θυμάστε ότι, όταν το όνομα ενός πίνακα χρησιμοποιείται
σε μία έκφραση, ο μεταγλωττιστής το μεταφράζει σε δείκτη στο
πρώτο του στοιχείο

 Πάντα; Όχι πάντα, υπάρχουν κάποιες εξαιρέσεις...

a. Όταν αποτελεί τον τελεστέο του τελεστή sizeof, αφού τότε


υπολογίζεται το μέγεθος όλου του πίνακα
b. Όπως ήδη αναφέραμε, όταν χρησιμοποιείται με τον τελεστή &,
όπου τότε λαμβάνουμε τη διεύθυνση του πίνακα και όχι τη
διεύθυνση του πρώτου του στοιχείου
c. Όταν ο πίνακας είναι ένα κυριολεκτικό αλφαριθμητικό που
χρησιμοποιείται σαν αρχική τιμή σε μία δήλωση

Προγραμματισμός Ι

Παρατηρήσεις (ΙV)
 Αν και μία μεταβλητή δείκτης δεν είναι πίνακας, μπορεί να
χρησιμοποιηθεί με σημειογραφία πίνακα

 Για παράδειγμα, το επόμενο πρόγραμμα χρησιμοποιεί τον δείκτη


ptr σαν να ήταν πίνακας, για να εμφανίσει τις διευθύνσεις μνήμης
και τις τιμές των στοιχείων του πίνακα arr

Προγραμματισμός Ι
Παραδείγματα (Ι)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: Val1 = 12 Val2 = 30

Προγραμματισμός Ι

Παραδείγματα (IΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: Val = 58

Προγραμματισμός Ι
Παραδείγματα (IIΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 3

Προγραμματισμός Ι

Παραδείγματα (IV)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 30 40 50 (και δύο τυχαίες τιμές)

Προγραμματισμός Ι
Παραδείγματα (V)
 Υπάρχει κάποιο bug στο παρακάτω πρόγραμμα ???

Απάντηση: Όχι...
«σκονάκι No1»: θυμηθείτε ότι *(arr+i)= arr[i]

Κι άλλη υπόδειξη???
«σκονάκι No2»: *(arr+i)= *(i+arr)

Κι άλλο???
«σκονάκι No3»: arr[i]=*(arr+i)= *(i+arr)= i[arr]
Προγραμματισμός Ι

Πίνακας Δεικτών
 Ένας πίνακας δεικτών είναι ένας πίνακας, όπου κάθε στοιχείο του
είναι ένας δείκτης σε έναν συγκεκριμένο τύπο δεδομένων

 Για να δηλώσουμε έναν πίνακα δεικτών χρησιμοποιούμε τον


τελεστή * πριν από το όνομα του πίνακα

Π.χ.
int *p[3];

Δήλωση ενός πίνακα δεικτών με όνομα p, ο οποίος περιέχει 3


στοιχεία και το καθένα από αυτά είναι ένας δείκτης σε μία
ακέραια μεταβλητή τύπου int

Π.χ. double *arr[5];

Δήλωση ενός πίνακα δεικτών με όνομα arr, ο οποίος περιέχει 5


στοιχεία και το καθένα από αυτά είναι ένας δείκτης σε έναν
δεκαδικό αριθμό τύπου double

Προγραμματισμός Ι
Παρατηρήσεις
 Για να μην τρομάζετε, το κάθε στοιχείο ενός πίνακα δεικτών,
μπορεί να θεωρηθεί ως μία απλή μεταβλητή δείκτη

 Προσοχή, μόνο, όταν δηλώνεται ένας πίνακας δεικτών, το όνομα


του πίνακα δεν πρέπει να περικλείεται σε παρενθέσεις

 Π.χ. με τη δήλωση:

int (*p)[3];

η μεταβλητή p δηλώνεται ως δείκτης προς έναν πίνακα τριών


ακεραίων και όχι σαν πίνακας τριών δεικτών

Προγραμματισμός Ι

Παραδείγματα (I)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 100 200 300

Προγραμματισμός Ι
Παραδείγματα (IΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 20 30 40

Προγραμματισμός Ι

Παραδείγματα (IΙΙ)
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: ο δεύτερος βρόχος εμφανίζει τρεις


φορές την τελευταία εισαχθείσα τιμή.

Προγραμματισμός Ι
Δείκτης σε Δείκτη
 Όταν δηλώνεται ένας δείκτης, ο μεταγλωττιστής, όπως κάνει για
οποιαδήποτε μεταβλητή, δεσμεύει τις απαραίτητες θέσεις μνήμης
για να αποθηκεύσει την τιμή του
 Επομένως, αφού έχει δεσμευτεί μία διεύθυνση μνήμης για έναν
δείκτη μπορούμε να δηλώσουμε έναν άλλον δείκτη που να δείχνει
σε αυτή τη διεύθυνση
 Για να δηλώσουμε έναν δείκτη σε κάποιον άλλον δείκτη
χρησιμοποιούμε δύο φορές τον τελεστή *

 Παραδείγματα Δηλώσεων «Δείκτη σε Δείκτη»

Προγραμματισμός Ι

Χρήση «Δείκτη σε Δείκτη»


 Αν έχουμε δηλώσει έναν δείκτη σε έναν δεύτερο δείκτη, τότε με
τον τελεστή * έχουμε πρόσβαση στη διεύθυνση του δεύτερου
δείκτη και με τον διπλό τελεστή ** έχουμε πρόσβαση στη
μεταβλητή που δείχνει ο δεύτερος δείκτης

Δήλωση Μεταβλητής (int)

Δήλωση Δείκτη (ptr1) σε int

Δήλωση Δείκτη (ptr) σε Δείκτη


(που δείχνει σε int)

O δείκτης ptr1 δείχνει στον i

O δεύτερος δείκτης ptr δείχνει


στον πρώτο δείκτη ptr1

**ptr = * (ptr1) = i

Προγραμματισμός Ι
Παράδειγμα
 Ποια είναι η έξοδος του παρακάτω προγράμματος ???

Έξοδος: 120 120 120

Προγραμματισμός Ι

Δείκτες και Διδιάστατοι Πίνακες (Ι)


 Όπως και στους μονοδιάστατους πίνακες, έτσι και στους
πολυδιάστατους, η κάθε διάσταση δηλώνεται μέσα σε αγκύλες []
 Π.χ. με την εντολή: int arr[2][3];
δηλώνεται ένας διδιάστατος πίνακας, ο οποίος αποτελείται από 2
γραμμές και 3 στήλες (δηλ. συνολικά περιέχει 6 ακέραιες
μεταβλητές) και σχηματικά απεικονίζεται όπως παρακάτω

arr[0][0] arr[0][1] arr[0][2]

arr[1][0] arr[1][1] arr[1][2]

 Τα στοιχεία του πίνακα αποθηκεύονται σε διαδοχικές θέσεις στη


μνήμη ξεκινώντας από τα στοιχεία της 1η γραμμής, συνεχίζοντας
με τα στοιχεία της 2ης γραμμής, κ.ο.κ.
 Άρα, η σειρά αποθήκευσης των στοιχείων του παραπάνω πίνακα
arr στη μνήμη είναι: arr[0][0], arr[0][1], arr[0][2],
arr[1][0], arr[1][1] και arr[1][2]
Προγραμματισμός Ι
Δείκτες και Διδιάστατοι Πίνακες (ΙΙ)
 Θυμηθείτε, επίσης, ότι η C χειρίζεται έναν διδιάστατο πίνακα σαν
μονοδιάστατο πίνακα, όπου το κάθε στοιχείο του είναι ένας
μονοδιάστατος πίνακας

 Στο προηγούμενο παράδειγμα, τα στοιχεία του arr είναι τα arr[0]


και arr[1], όπου το καθένα από αυτά είναι μονοδιάστατος πίνακας
τριών ακεραίων

 Για να χειριστούμε έναν διδιάστατο πίνακα με χρήση δεικτών, έστω


arr[Ν][Μ], μπορούμε να θεωρήσουμε ότι ο πίνακας arr αποτελείται
από έναν πίνακα δεικτών N στοιχείων, arr[0], arr[1], ...,
arr[N-1], όπου καθένα από αυτά είναι δείκτης σε έναν πίνακα Μ
στοιχείων
 Π.χ. με την εντολή:
int arr[2][3];
το arr[0] μπορεί να χρησιμοποιηθεί σαν δείκτης προς έναν
μονοδιάστατο πίνακα 3 ακεραίων που περιέχει τα στοιχεία της
πρώτης γραμμής, δηλαδή τα arr[0][0], arr[0][1] και
arr[0][2]
Προγραμματισμός Ι

Δείκτες και Διδιάστατοι Πίνακες (ΙΙΙ)


 Συγκεκριμένα, το arr[0] είναι δείκτης στο πρώτο στοιχείο του
πίνακα, δηλαδή στο arr[0][0], άρα, η τιμή του *arr[0] είναι
ίση με το arr[0][0]

 Επίσης, σύμφωνα με την αριθμητική δεικτών:


 το arr[0]+1 είναι δείκτης στο δεύτερο στοιχείο του πίνακα,
δηλαδή στο arr[0][1]
 το arr[0]+2 είναι δείκτης στο τρίτο στοιχείο του πίνακα,
δηλαδή στο arr[0][2], κ.ο.κ.
 ...
 συνεπώς, στη γενική περίπτωση ισχύει ότι το arr[0]+κ είναι
δείκτης στο στοιχείο arr[0][κ] της πρώτης γραμμής του
διδιάστατου πίνακα

 Δηλαδή, ισχύει ότι:


 το arr[0]+κ είναι ισοδύναμο με &arr[0][κ]
 η τιμή του *(arr[0]+κ) είναι ίση με arr[0][κ]

Προγραμματισμός Ι
Δείκτες και Διδιάστατοι Πίνακες (ΙV)
 Αντίστοιχα, το arr[1] μπορεί να χρησιμοποιηθεί σαν δείκτης
προς έναν πίνακα 3 ακεραίων που περιέχει τα στοιχεία της
δεύτερης γραμμής, δηλαδή τα arr[1][0], arr[1][1] και
arr[1][2]

 Συγκεκριμένα:
 το arr[1] είναι δείκτης στο πρώτο στοιχείο του πίνακα,
δηλαδή στο arr[1][0]
 Άρα, η τιμή του *arr[1] είναι ίση με το arr[1][0]

 Παρομοίως με πριν, ισχύει ότι το arr[1]+κ είναι δείκτης στο


στοιχείο arr[1][κ] της δεύτερης γραμμής του διδιάστατου πίνακα

 Δηλαδή, ισχύει ότι:


 το arr[1]+κ είναι ισοδύναμο με &arr[1][κ]
 η τιμή του *(arr[1]+κ) είναι ίση με arr[1][κ]

Προγραμματισμός Ι

Δείκτες και Διδιάστατοι Πίνακες (V)


 Γενικά, θεωρούμε ότι τα στοιχεία ενός πίνακα arr[N][M], είναι
τα arr[0], arr[1], ..., arr[N-1] τα οποία είναι δείκτες σε
πίνακες που περιέχουν τα Μ στοιχεία της αντίστοιχης γραμμής

 Δηλαδή,
 το πρώτο στοιχείο του πίνακα arr[N][M] είναι το arr[0], το
οποίο είναι δείκτης σε έναν πίνακα που περιέχει τα Μ στοιχεία
της πρώτης γραμμής
 το δεύτερο στοιχείο του πίνακα arr[N][M] είναι το arr[1],
το οποίο είναι δείκτης σε έναν πίνακα που περιέχει τα Μ
στοιχεία της δεύτερης γραμμής
 ...
 ενώ το τελευταίο στοιχείο είναι το arr[Ν-1], το οποίο είναι
δείκτης σε έναν πίνακα που περιέχει τα Μ στοιχεία της
τελευταίας (της Ν-οστής) γραμμής

Προγραμματισμός Ι
Παράδειγμα
 Τι κάνει το παρακάτω πρόγραμμα ???

Εμφανίζει τις τιμές όλων των στοιχείων του πίνακα με


χρήση δείκτη !!!

Προγραμματισμός Ι

Χειρισμός Διδιάστατου Πίνακα με «δείκτη σε δείκτη» (Ι)


 Ένας εναλλακτικός τρόπος για να διαχειριστούμε έναν διδιάστατο
πίνακα με χρήση δείκτη, είναι χρησιμοποιώντας το όνομα του
πίνακα

 Θυμηθείτε ότι το όνομα ενός πίνακα χωρίς τις αγκύλες είναι


ισοδύναμο με τη διεύθυνση του πρώτου στοιχείου του πίνακα

 Π.χ. αν θεωρήσουμε την παρακάτω δήλωση:


int arr[2][3];
το όνομα του πίνακα arr είναι δείκτης στο πρώτο στοιχείο του
πίνακα, δηλ. στο arr[0]

 Όμως, όπως είδαμε προηγουμένως, το πρώτο στοιχείο του πίνακα


(το arr[0]) είναι με τη σειρά του δείκτης σε έναν πίνακα που
περιέχει τα 3 στοιχεία της πρώτης γραμμής

 Συγκεκριμένα, το arr[0] είναι δείκτης στο πρώτο στοιχείο του


πίνακα, δηλαδή στο arr[0][0]
Προγραμματισμός Ι
Χειρισμός Διδιάστατου Πίνακα με «δείκτη σε δείκτη» (ΙΙ)
 Άρα, ισχύει ότι το arr είναι δείκτης στο arr[0] και το arr[0] είναι
δείκτης στο arr[0][0]

 Επομένως, πώς μπορούμε να χειριστούμε το όνομα του πίνακα arr ???

 Η απάντηση είναι ότι μπορούμε να το χειριστούμε σαν δείκτη προς δείκτη

 Π.χ. αφού το arr είναι δείκτης σε έναν δείκτη που δείχνει στη διεύθυνση
του στοιχείου arr[0][0], τότε το **arr είναι ίσο με την τιμή
arr[0][0]

 Παρομοίως, το arr+1 είναι δείκτης στο arr[1] και το arr[1] είναι


δείκτης που δείχνει στη διεύθυνση του στοιχείου arr[1][0]

 Άρα, το **(arr+1) είναι ίσο με την τιμή arr[1][0]

 Στη γενική περίπτωση, ισχύει ότι:


 το arr+κ είναι ισοδύναμο με &arr[κ]
 το *(arr+κ) είναι ισοδύναμο με arr[κ], δηλαδή με &arr[κ][0]
 το **(arr+κ) είναι ισοδύναμο με arr[κ][0]

Προγραμματισμός Ι

Παράδειγμα
 Τι κάνει το παρακάτω πρόγραμμα ???

Εμφανίζει τις τιμές όλων των στοιχείων του πίνακα με


χρήση του ονόματος του πίνακα ως «δείκτη σε δείκτη» !!!

Προγραμματισμός Ι
Παρατηρήσεις I
 Προφανώς, η διαχείριση των στοιχείων ενός διδιάστατου πίνακα
με χρήση δεικτών (είτε απλού δείκτη είτε «δείκτη σε δείκτη»)
οδηγεί σε δυσνόητο και μη ευανάγνωστο κώδικα

 Για τον λόγο αυτό, προτείνουμε η διαχείριση των στοιχείων να


γίνεται με τη χρήση των αγκυλών [][] και των αντιστοίχων
θέσεων στον πίνακα

Προγραμματισμός Ι

Παρατηρήσεις II
 Έστω ότι είχαμε τις ακόλουθες δηλώσεις:
int a[2][3], b[10];
 Ερώτηση: Όπως λέμε ότι το b μπορεί να χρησιμοποιηθεί σαν
δείκτης στο b[0], μπορούμε να πούμε ότι και το a είναι δείκτης
στο a[0][0];
Η απάντηση είναι ΟΧΙ, επειδή η C χειρίζεται τον a σαν
μονοδιάστατο πίνακα με στοιχεία πίνακες, οπότε το a είναι δείκτης
στο πρώτο στοιχείο του που είναι το a[0], άρα, αν θέλαμε να
εκχωρήσουμε το a σε έναν δείκτη, ποιος θα έπρεπε να είναι ο
τύπος του δείκτη; Θα έπρπε να είναι δείκτης σε πίνακα, π.χ.:
int (*p)[3]; /* Οι παρενθέσεις είναι απαραίτητες, γιατί
αλλιώς το p θα μεταφραζόταν σαν πίνακας τριών δεικτών σε
ακεραίους. */
p = a;

και τώρα το p δείχνει στο πρώτο στοιχείο της πρώτης γραμμής


του a, δηλαδή στο a[0][0]
Προγραμματισμός Ι
Παρατηρήσεις III
 Να θυμάστε: αν έχουμε τη δήλωση int x[5] ο πίνακας x μπορεί
να «υποβιβαστεί» σε δείκτη, ενώ αν έχουμε τη δήλωση
int y[5][3] ο πίνακας y δεν «υποβιβάζεται» σε δείκτη σε
δείκτη, αλλά σε δείκτη σε πίνακα

 Επίσης σημειώστε: με δεδομένες τις δηλώσεις int x[5] και


int y[5][3] η έκφραση &x είναι δείκτης σε έναν πίνακα 5
ακεραίων, ενώ η έκφραση &y είναι δείκτης σε έναν πίνακα 5
πινάκων που ο καθένας έχει 3 ακεραίους.

Προγραμματισμός Ι

Παράδειγμα
 Τι κάνει το παρακάτω πρόγραμμα ???

Σε κάθε επανάληψη, το arr[i] δείχνει στο πρώτο στοιχείο


της γραμμής i. Η έκφραση arr[i]+3 είναι ένας δείκτης στο
τέταρτο στοιχείο της γραμμής i. Συνεπώς, το *(arr[i]+3)
είναι ισοδύναμο με arr[i][3], οπότε, το πρόγραμμα
μηδενίζει τα στοιχεία της τέταρτης στήλης του πίνακα, δηλαδή
τα arr[0][3] και arr[1][3] γίνονται 0
Προγραμματισμός Ι
Δείκτης προς Συνάρτηση
 ΠΡΟΣΟΧΗ: Για να γίνει κατανοητή η συγκεκριμένη υποενότητα, θα πρέπει
να έχει διδαχθεί η ενότητα των Συναρτήσεων

 Όταν εκτελείται ένα πρόγραμμα, ο κώδικας για κάθε συνάρτηση που


περιέχει το πρόγραμμα φορτώνεται στη μνήμη αρχίζοντας σε μία
συγκεκριμένη διεύθυνση
 Ένας δείκτης προς μία συνάρτηση δείχνει στη διεύθυνση μνήμης, στην
οποία είναι αποθηκευμένος ο κώδικας της συνάρτησης

 Η γενική μορφή της δήλωσης ενός δείκτη προς μία συνάρτηση είναι:

τύπος_επιστροφής (*όνομα_δείκτη) (τύπος_παραμ_1 όνομα_1,


τύπος_παραμ_2 όνομα_2, ..., τύπος_παραμ_ν όνομα_ν);

 Ο τύπος_επιστροφής καθορίζει τον τύπο δεδομένων που επιστρέφει η


συνάρτηση στο πρόγραμμα που την κάλεσε

 Οι μεταβλητές όνομα_1, όνομα_2,..., όνομα_ν αποτελούν τις


παραμέτρους της συνάρτησης (εφόσον η συνάρτηση δέχεται παραμέτρους)

Προγραμματισμός Ι

Παραδείγματα δήλωσης Δείκτη προς Συνάρτηση

int (*ptr)(int arr[], int size); /* Η μεταβλητή ptr


δηλώνεται σαν δείκτης προς μία συνάρτηση, η οποία
δέχεται σαν παραμέτρους έναν πίνακα ακεραίων και
έναν ακέραιο και επιστρέφει μία ακέραια τιμή. */

void (*ptr)(double *arr[]); /* Η μεταβλητή ptr


δηλώνεται σαν δείκτης προς μία συνάρτηση, η οποία
δέχεται σαν παράμετρο έναν πίνακα δεικτών σε
πραγματικούς και δεν επιστρέφει τίποτα. */

int test(void (*ptr)(int a)); /* Η συνάρτηση test()


επιστρέφει μία ακέραια τιμή και δέχεται σαν
παράμετρο έναν δείκτη προς μία άλλη συνάρτηση, η
οποία δέχεται μία ακέραια παράμετρο και δεν
επιστρέφει τίποτα. */

Προγραμματισμός Ι
Παρατηρήσεις
 Το όνομα του δείκτη πρέπει να βρίσκεται ανάμεσα σε παρενθέσεις,
γιατί ο τελεστής * έχει χαμηλότερη προτεραιότητα από τις
παρενθέσεις που περιβάλλουν τη λίστα παραμέτρων της
συνάρτησης

 Π.χ. αν γράψουμε:

int *ptr(int a); αντί για int (*ptr)(int a);

τότε δηλώνεται μία συνάρτηση με όνομα ptr, η οποία δέχεται μία


ακέραια παράμετρο και επιστρέφει έναν δείκτη σε μία ακέραια
μεταβλητή

Προγραμματισμός Ι

Χρήση Δείκτη προς Συνάρτηση


 Η μόνη απαίτηση για να δείξει ένας δείκτης σε μία συνάρτηση
είναι ο τύπος επιστροφής της συνάρτησης και η λίστα παραμέτρων
της να είναι ίδια με τον τύπο επιστροφής και τη λίστα παραμέτρων
της δήλωσης του δείκτη
 Παράδειγμα:

Προγραμματισμός Ι
Παράδειγμα
Δημιουργήστε μία συνάρτηση που να δέχεται σαν παραμέτρους τους
βαθμούς δύο φοιτητών και να επιστρέφει τον μεγαλύτερο από αυτούς.
Στη συνέχεια
γράψτε ένα
πρόγραμμα το
οποίο να
διαβάζει δύο
βαθμούς και να
χρησιμοποιεί
έναν δείκτη
για να καλέσει
τη συνάρτηση
και να
εμφανίσει τον
μεγαλύτερο
βαθμό.
Προγραμματισμός Ι

Πίνακας Δεικτών σε Συναρτήσεις


 Ένας πίνακας δεικτών σε συναρτήσεις είναι ένας πίνακας όπου
κάθε στοιχείο του είναι δείκτης σε κάποια συνάρτηση

 Η δήλωσή του είναι παρόμοια με τη δήλωση ενός δείκτη σε


συνάρτηση με τη διαφορά ότι αντί για ένας δείκτης δηλώνεται ένας
πίνακας δεικτών

 Π.χ. η εντολή: void (*ptr[20])(int a);

δηλώνει έναν πίνακα δεικτών με όνομα ptr, του οποίου τα


στοιχεία είναι 20 δείκτες προς συναρτήσεις, οι οποίες δέχονται
μία ακέραια παράμετρο και δεν επιστρέφουν τίποτα

Προγραμματισμός Ι
Παράδειγμα
 Τι κάνει το παρακάτω πρόγραμμα ???

Προγραμματισμός Ι (συνέχεια προγράμματος) 

Παράδειγμα

Το πρόγραμμα ανάλογα με την τιμή


του i καλεί την αντίστοιχη
συνάρτηση μέσω του δείκτη που
δείχνει στη διεύθυνσή της.
Η τιμή επιστροφής της συνάρτησης
εμφανίζεται στην οθόνη.

Προγραμματισμός Ι

You might also like