6 #.Μάθημα Προγραμματισμού με τη C++ ΣΥΝΑΡΤΗΣΕΙΣ

 

ΣΥΝΑΡΤΗΣΕΙΣ
Οι συναρτήσεις ομαδοποιούν έναν αριθμό εντολών σχηματίζοντας μία μονάδα και δίνουν στην ομάδα εντολών ένα όνομα. Έπειτα, κάθε φορά που θα χρειαστεί ο προγραμματιστής να κάνει χρήση αυτής της ομάδας εντολών, χρησιμοποιεί το όνομα αυτής της μονάδας. Οι συναρτήσεις βοηθούν στην καλύτερη οργάνωση του προγράμματος και στην εξοικονόμηση χρόνου κατά την πληκτρολόγησή του.

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


Ορισμός συνάρτησης
Όπως και με τις μεταβλητές, πριν κάνετε χρήση κάποιας συνάρτησης θα πρέπει να δώσετε οδηγίες στη γλώσσα για τη μορφή της ώστε να ξέρει πως θα τη χρησιμοποιήσει. Ο ορισμός μιας απλής συνάρτησης είναι ο ακόλουθος:
void typestars ()
{
for (int i=0; i<20; i++)
   cout << ‘*’;
cout << endl;
}
Για να ορίσουμε μία συνάρτηση πρώτα δηλώνουμε τον τύπο των δεδομένων που θα επιστρέφει, μετά το όνομά της και τέλος τα ορίσματα που θα δέχεται μέσα σε παρενθέσεις. Ας τα εξετάσουμε ένα προς ένα:
Εάν η συνάρτηση δεν επιστρέφει κάποια τιμή στο κυρίως πρόγραμμα που την καλεί, τότε πριν το όνομά της γράφουμε τη λέξη void (κενό). Όταν η συνάρτηση δεν επιστρέφει κάποια τιμή που να τη χρησιμοποιεί το κυρίως πρόγραμμα, λειτουργεί ως διαδικασία, δηλαδή απλώς κάτι κάνει. Στην περίπτωσή μας τυπώνει 20 φορές το σύμβολο του αστεριού (*) στην οθόνη. Εάν όμως η συνάρτηση επιστρέφει κάποια τιμή στο σημείο του προγράμματος που την καλεί τότε θα πρέπει πριν το όνομά της να δηλώσουμε τον τύπο των δεδομένων που επιστρέφει. Έτσι αν επιστρέφει ακέραια τιμή θα γράφαμε πρώτα int και μετά το όνομά της, αν επιστρέφει πραγματικό αριθμό θα γράφαμε πρώτα float και μετά το όνομά της κ.λ.π.
Το σημαντικό που θα πρέπει να παρατηρήσετε στις περιπτώσεις που η συνάρτηση επιστρέφει τιμή είναι η εντολή return μέσα στο σώμα της συνάρτησης. Μετά τη δεσμευμένη λέξη return ακολουθεί η παράσταση με την επιστρεφόμενη τιμή. Φυσικά ο τύπος δεδομένων της τιμής που επιστρέφεται θα πρέπει να είναι ίδιος με τον τύπο δεδομένων που προηγείται του ονόματος της συνάρτησης.
Εάν πριν από το όνομα της συνάρτησης δεν υπάρχει η λέξη void αλλά ένας τύπος δεδομένων είτε από τους ενσωματωμένους της γλώσσας είτε ορισμένος από το χρήστη, τότε μέσα στο σώμα της συνάρτησης θα πρέπει να υπάρχει εντολή return.
Η τιμή αυτή που επιστρέφεται μπορεί να χρησιμοποιηθεί στον υπολογισμό μιας παράστασης ή να αποδοθεί σε μια μεταβλητή.

Για το όνομα της συνάρτησης ισχύει ότι και για τα ονόματα των μεταβλητών. Θα πρέπει να χρησιμοποιείται πάντα το όνομα της συνάρτησης όπως είναι στο σημείο ορισμού της και να μη μπερδεύετε μικρούς με κεφαλαίους χαρακτήρες.
Μετά το όνομα της συνάρτησης υπάρχουν τα ορίσματά της μέσα σε παρενθέσεις. Ακόμα και όταν δεν υπάρχουν ορίσματα είμαστε υποχρεωμένοι να γράφουμε πάντα τις παρενθέσεις. Οι παρενθέσεις είναι τα σύμβολα που ξεχωρίζουν τις συναρτήσεις από τις μεταβλητές.
Στο παραπάνω παράδειγμα η συνάρτηση typestars () τυπώνει 20 φορές ένα σύμβολο στην οθόνη. Εάν την αλλάξουμε λίγο έτσι ώστε ο χρήστης να αποφασίζει πόσες φορές θα τυπωθεί το σύμβολο, θα γράφαμε:
void typestars (int n)
{
for (int i=0; i<n; i++)//n επαναλήψεις του βρόχου
   cout << ‘*’;
cout << endl;
}

Τα ορίσματα τα οποία εσωκλείονται μέσα σε παρενθέσεις είναι μεταβλητές οι οποίες δέχονται τιμές από το σημείο κλήσης της συνάρτησης. Μέσα στις παρενθέσεις γίνεται δήλωση των μεταβλητών, για αυτό και η σημειογραφία:
int n
Εάν είναι περισσότερα του ενός, τα ορίσματα χωρίζονται μεταξύ τους με κόμμα. Τα ορίσματα είναι που κάνουν τις συναρτήσεις να εκτελούν διαφορετικά πράγματα, ανάλογα με την είσοδο που δέχονται. Ειδάλλως χωρίς ορίσματα, οι συναρτήσεις θα επαναλάμβαναν τις ίδιες εντολές κάθε φορά που θα τις καλούσαμε.

 Κλήση συνάρτησης

Για να καλέσουμε μία συνάρτηση θα πρέπει προηγουμένως να την έχουμε ορίσει, όπως κάναμε παραπάνω γράφοντας το σώμα της συνάρτησης, ή απλώς να την έχουμε δηλώσει, το οποίο θα εξετάσουμε παρακάτω.
Η κλήση της συνάρτησης γίνεται γράφοντας το όνομά της και τις παρενθέσεις με τα ορίσματα, αν υπάρχουν:
#include <iostream>
void typestars (int n)
{
  for (int i=0; i<n; i++)
  cout << ‘*’;
  cout << endl;
}

void main()
{
   typestars (15) ; //Κλήση συνάρτησης
   ...
}
Όταν καλέσουμε μια συνάρτηση τότε ο έλεγχος του προγράμματος κάνει ένα άλμα, αφήνει το σημείο που την καλέσαμε και εισέρχεται μέσα στο σώμα της συνάρτησης. Αποδίδει στα ορίσματα της συνάρτησης τις τιμές που δώσαμε από το σημείο κλήσης και εκτελεί της εντολές που υπάρχουν στο σώμα της συνάρτησης. Όταν τελειώσει η συνάρτηση και εκτελεστούν οι εντολές της τότε κάνει ένα δεύτερο άλμα και συνεχίζει από το σημείο που την καλέσαμε και κάτω.


 Μεταβίβαση ορισμάτων κατά τιμή

Πολλές φορές όταν καλούμε μια συνάρτηση στη θέση των ορισμάτων μεταβιβάζουμε μεταβλητές αντί σταθερών. Τότε οι τιμές των μεταβλητών αποδίδονται με τη σειρά που της έχουμε τοποθετήσει μέσα στις παρενθέσεις, στα ορίσματα της συνάρτησης. Έτσι εάν αλλάξουμε λίγο το παραπάνω παράδειγμα, έτσι ώστε η συνάρτηση να δέχεται ως όρισμα την τιμή μιας μεταβλητής:
#include <iostream>
void typestars (int n)
{
  for (int i=0; i<n; i++)
  cout << ‘*’;
  cout << endl;
}

void main()
{
  int x;
  cout << “Δώσε έναν ακέραιο αριθμό”;
  cin >> x;
  typestars (x) ; //Κλήση συνάρτησης
  ...
}
Σε αυτή την περίπτωση ο χρήστης δίνει μια τιμή η οποία αποθηκεύεται μέσα στη μεταβλητή x. Την μεταβλητή αυτή την περνάμε ως όρισμα στη συνάρτηση typestars() όταν την καλούμε. Ο έλεγχος του προγράμματος τώρα περνά στη συνάρτηση, που έχει ως όρισμα τη μεταβλητή n. Έτσι δημιουργείται αυτή η μεταβλητή στη μνήμη, της αποδίδεται η τιμή που ήταν αποθηκευμένη μέσα στη x και ξεκινά η εκτέλεση των εντολών της συνάρτησης χρησιμοποιώντας την τοπική μεταβλητή n.
Ο παραπάνω τρόπος κατά τον οποίο δημιουργούνται οι τοπικές μεταβλητές της συνάρτησης και παίρνουν ως αρχικές τιμές τις τιμές των μεταβλητών που συμπεριλάβαμε στο σημείο κλήσης, λέγεται μεταβίβαση μεταβλητών με τιμή. Οι αλλαγές που επιτελούνται μέσα στη συνάρτηση επηρεάζουν τις τοπικές μεταβλητές και οι τυχόν αλλαγές των τιμών τους, δεν έχουν αντίκτυπο στις μεταβλητές με τις οποίες την καλέσαμε.
Στο παρακάτω παράδειγμα η τιμή της μεταβλητής x της συνάρτησης main() δεν αλλάζει μέσα στη συνάρτηση changeval():
#include <iostream>
void changeval(int i)
{
i = i+1; //Η τοπική μεταβλητή i αυξάνεται κατά 1
}

void main()
{
 int x = 3;
 changeval(x) ; //Κλήση συνάρτησης
 cout << x ;   // το x εξακολουθεί να είναι 3
              // άλλαξε το αντίγραφο της x, η i
  ...
}


 Μεταβίβαση ορισμάτων κατά αναφορά

Προηγουμένως μελετώντας τον ορισμό μιας συνάρτησης, είπαμε ότι με την εντολή return επιστρέφουμε μια τιμή από τη συνάρτηση. Φυσικά ο τύπος των δεδομένων της παράστασης που ακολουθεί τη λέξη return θα πρέπει να είναι ίδιος με τον τύπο δεδομένων που προηγείται του ονόματος της συνάρτησης, όπως δείξαμε. Όταν η συνάρτηση δεν έχει εντολή return και δεν επιστρέφει κάποια τιμή τότε στον ορισμό της συνάρτησης μπροστά από το όνομα της γράφουμε void.
Έτσι λοιπόν από μία συνάρτηση με μία εντολή return επιστρέφεται μία μόνο τιμή. Τι γίνεται όμως όταν θέλουμε η συνάρτηση να κάνει και κάποιες άλλες αλλαγές, εκτός από το να επιστρέφει μία τιμή, οι οποίες θα ισχύουν και με το πέρας των εντολών της συνάρτησης;
Σε αυτή την περίπτωση μεταβιβάζουμε ορίσματα κατά αναφορά. Όταν μεταβιβάζουμε ορίσματα κατά αναφορά δεν δημιουργούνται αντίγραφα των μεταβλητών που μεταβιβάζουμε, αλλά δημιουργείται μία αναφορά προς τις αρχικές αυτές μεταβλητές. Έτσι οι τυχόν αλλαγές που συντελούνται μέσα στη συνάρτηση στις τοπικές μεταβλητές της, ουσιαστικά είναι αλλαγές των αρχικών μεταβλητών με τις οποίες την καλέσαμε.
Για να μεταβιβάσουμε μεταβλητές κατά αναφορά στον ορισμό της συνάρτησης πριν από το όνομα της μεταβλητής, εισάγουμε το σύμβολο &. Για παράδειγμα:
#include <iostream>
void change2val(int i, int & j)
{
i=10;//το10 αποθηκεύεται στο i,το αντίγραφο του x
j=20;//το 20 αποθηκεύεται στην αναφορά του j,το y
}
void main()
{
int x = 3;
int y = 5;
changeval(x,y) ; //Κλήση συνάρτησης

cout << x << endl; // το x εξακολουθεί να είναι 3

cout << y ; // το y είναι πλέον 20
   ...
}
Η συνάρτηση δημιουργεί τις τοπικές μεταβλητές i και j. Η μεταβλητή i είναι το αντίγραφο της μεταβλητής x και αρχικοποιείται με την τιμή που φέρει η x. Με το πέρας της συνάρτησης όταν θα αποδεσμευτεί η μνήμη για τη μεταβλητή i, εάν υπάρξει κάποια αλλαγή σε αυτή, το αποτέλεσμα θα χαθεί.
Η μεταβλητή x δεν επηρεάζεται. Η μεταβλητή j όμως, είναι μία αναφορά προς τη μεταβλητή y. Έτσι η όποια αλλαγή της τιμής της j, είναι ουσιαστικά αλλαγή της y