This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
|
cpp:pointers [2017/04/11 07:55] gthanos [Δείκτες σε συναρτήσεις] |
cpp:pointers [2021/04/27 04:42] gthanos [null pointers] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== Δείκτες ====== | ====== Δείκτες ====== | ||
| - | Κάθε μεταβλητή ξεκινάει από μία διεύθυνση στη μνήμη και καταλαμβάνει ένα ή περισσότερα bytes. | + | Οι δείκτες είναι μεταβλητές που προσδιορίζουν τιμές διευθύνσεων στη μνήμη αντί για τιμές μεταβλητών. Η δήλωση ενός δείκτη γίνεται ως εξής: |
| <code c> | <code c> | ||
| Line 17: | Line 17: | ||
| ===== Αρχικοποίηση δεικτών ===== | ===== Αρχικοποίηση δεικτών ===== | ||
| - | Η διεύθυνση μνήμης μίας μεταβλητής μπορεί να δοθεί με χρήση του τελεστή **&**. Οποιοσδήποτε δείκτης μπορεί να αρχικοποιηθεί λαμβάνοντας την διεύθυνση μνήμης μίας μεταβλητής ως εξής: | + | Η διεύθυνση μνήμης μίας μεταβλητής μπορεί να δοθεί με χρήση του τελεστή **& |
| <code c> | <code c> | ||
| char c = ' | char c = ' | ||
| Line 31: | Line 31: | ||
| </ | </ | ||
| - | == Παράδειγμα αρχικοποίησης δεικτών == | + | ==== Παράδειγμα αρχικοποίησης δεικτών |
| <code c> | <code c> | ||
| char foo = ' | char foo = ' | ||
| char *foo_ptr = &foo; | char *foo_ptr = &foo; | ||
| - | char bar = foo; | ||
| </ | </ | ||
| - | Ο παραπάνω κώδικας αρχικοποίησης ενός δείκτη απεικονίζεται γραφικά για ένα σύστημα 32bit στο σχήμα που ακολουθεί. | + | Ο παραπάνω κώδικας αρχικοποίησης ενός δείκτη απεικονίζεται γραφικά για ένα σύστημα 32bit στο σχήμα που ακολουθεί. |
| - | {{ :cpp:reference_operator1.png? |}} | + | {{ :cpp:reference_operator2.png? |}} |
| ===== Λήψη του περιεχομένου της τιμής ενός δείκτη (pointer dereference) ===== | ===== Λήψη του περιεχομένου της τιμής ενός δείκτη (pointer dereference) ===== | ||
| - | Μπορείτε να χρησιμοποιήσετε την τιμή ενός δείκτη για να προσπελάσετε την μεταβλητή στην οποία αυτοί δείχνουν. Αυτό μπορεί να γίνει με χρήση του τελεστή * (dereference operator) πριν από το όνομα του δείκτη ως εξής: | + | Μπορείτε να χρησιμοποιήσετε την τιμή ενός δείκτη για να προσπελάσετε τη μεταβλητή στην οποία αυτός δείχνει με χρήση του τελεστή |
| <code c> | <code c> | ||
| char foo = ' | char foo = ' | ||
| Line 54: | Line 53: | ||
| {{ : | {{ : | ||
| - | Η μεταβλητή **foo_ptr** αποθηκεύει τη διεύθυνση της μεταβλητής **foo** (// | + | Η μεταβλητή **bar** αποθηκεύει το περιεχόμενο της διεύθυνσης που δείχνει η μεταβλητή **foo_ptr**, |
| <WRAP center round tip 80%> | <WRAP center round tip 80%> | ||
| Line 63: | Line 62: | ||
| ===== Αριθμητική δεικτών ===== | ===== Αριθμητική δεικτών ===== | ||
| - | Η αριθμητική δεικτών περιορίζεται μόνο στις πράξεις της πρόσθεσης και της αφαίρεσης. Μπορούμε να αυξήσουμε ή να μειώσουμε την τιμή ενός δείκτη κατά μία ή περισσότερες θέσεις. Η μεταβολή της τελικής τιμής του δείκτη | + | Η αριθμητική δεικτών περιορίζεται μόνο στις πράξεις της πρόσθεσης και της αφαίρεσης. Μπορούμε να αυξήσουμε ή να μειώσουμε την τιμή ενός δείκτη κατά μία ή περισσότερες θέσεις. Η μεταβολή της τελικής τιμής του δείκτη συνδέεται με τον μέγεθος του τύπου δεδομένων στον οποίο δείχνει ο δείκτης. Για παράδειγμα, |
| <code c++> | <code c++> | ||
| Line 72: | Line 71: | ||
| </ | </ | ||
| - | Αν και όλοι οι δείκτες καταλαμβάνουν τον ίδιο χώρο στη μνήμη (4-byte για συστήματα 32-bit και 8-byte για συστήματα 64-bit) διαφοροποιούνται ως προς το μέγεθος του περιεχόμενου των δεδομένων στα οποία δείχνουν. | + | Στο παραπάνω παράδειγμα η μεταβλητή //mychar// δείχνει σε δεδομένα μήκους ενός byte. Ας υποθέσουμε επίσης ότι η μεταβλητή //myshort// δείχνει σε δεδομένα μήκους 2 bytes (εξαρτάται από το hw) και η μεταβλητή //myint// δείχνει σε δεδομένα μήκους |
| Ας επιχειρήσουμε να μεταβάλλουμε τις τιμές των παραπάνω δεικτών κατά μία θέση ως εξής: | Ας επιχειρήσουμε να μεταβάλλουμε τις τιμές των παραπάνω δεικτών κατά μία θέση ως εξής: | ||
| Line 85: | Line 84: | ||
| - η μεταβλητή myshort μετακινείται κατά // | - η μεταβλητή myshort μετακινείται κατά // | ||
| - η μεταβλητή myint μετακινείται κατά // | - η μεταβλητή myint μετακινείται κατά // | ||
| + | |||
| + | Θα πρέπει να έχετε υπόψη ότι όλοι οι δείκτες καταλαμβάνουν τον ίδιο χώρο στη μνήμη (4-byte για συστήματα 32-bit και 8-byte για συστήματα 64-bit) ανεξάρτητα από τον τύπο δεδομένων στον οποίο δείχνουν. | ||
| ===== Δείκτες και πίνακες ===== | ===== Δείκτες και πίνακες ===== | ||
| - | Η δήλωση ενός πίνακα ισοδυναμεί με ένα δείκτη στην πρώτη θέση του πίνακα. | + | Η δήλωση ενός πίνακα ισοδυναμεί με ένα δείκτη |
| <code c> | <code c> | ||
| Line 94: | Line 95: | ||
| </ | </ | ||
| - | Αν και ένας πίνακας μπορεί να αντιμετωπιστεί και ως δείκτης η βασική διαφορά πινάκων και δεικτών είναι ότι ο πίνακας δεν μπορεί να μεταβάλλει την διεύθυνση στην οποία δείχνει, | + | Αν και ένας πίνακας μπορεί να αντιμετωπιστεί και ως δείκτης η βασική διαφορά πινάκων και δεικτών είναι ότι ο πίνακας δεν μπορεί να μεταβάλλει την διεύθυνση στην οποία δείχνει, |
| - | <code c> | + | |
| - | Δείτε το παρακάτω παράδειγμα όπου χρησιμοποιούνται πίνακας από ακεραίους και ένας δείκτης σε ακέραιο που δείχνει στις επιμέρους τιμές του πίνακα. Στα σχόλια η θέση του πίνακα που προσπελαύνεται κάθε φορά. | + | <code c> |
| - | < | + | Δείτε το παρακάτω παράδειγμα όπου χρησιμοποιούνται πίνακας από ακεραίους και ένας δείκτης σε ακέραιο που δείχνει στις επιμέρους τιμές του πίνακα. Τα σχόλια περιέχουν τη θέση του πίνακα που προσπελάζεται κάθε φορά. |
| + | |||
| + | < | ||
| #include < | #include < | ||
| using namespace std; | using namespace std; | ||
| Line 107: | Line 109: | ||
| int numbers[5]; | int numbers[5]; | ||
| int * p; | int * p; | ||
| - | p = numbers; | + | p = numbers; |
| - | p++; *p = 20; // numbers[1] | + | p++; *p = 20; // numbers[1] |
| - | p = & | + | p = & |
| - | p = numbers + 3; *p = 40; // numbers[3] | + | p = numbers + 3; *p = 40; // numbers[3] |
| - | p = numbers; | + | p = numbers; |
| + | | ||
| for (int n=0; n<5; n++) | for (int n=0; n<5; n++) | ||
| cout << numbers[n] << ", "; | cout << numbers[n] << ", "; | ||
| Line 120: | Line 123: | ||
| <WRAP tip 80% center round> | <WRAP tip 80% center round> | ||
| - | Οι δείκτες οφείλουν να δείχνουν σε διευθύνσεις μνήμης που ανήκουν στην διεργασία που εκτελείται. Στην πραγματικότητα όμως οι δείκτες μπορούν να πάρουν οποιαδήποτε τιμή ακόμη και τιμές εκτός των ορίων της | + | Οι δείκτες οφείλουν να δείχνουν σε διευθύνσεις μνήμης που ανήκουν στη διεργασία που εκτελείται. Στην πραγματικότητα όμως οι δείκτες μπορούν να πάρουν οποιαδήποτε τιμή ακόμη και τιμές εκτός του εύρους διευθύνσεων που έχουν ανατεθεί από το λειτουργικό |
| <code c++ intArrayPointer.cpp> | <code c++ intArrayPointer.cpp> | ||
| #include < | #include < | ||
| Line 132: | Line 136: | ||
| </ | </ | ||
| - | ===== Δείκτες αμετάβλητου περιεχομένου | + | ===== Δείκτες αμετάβλητου περιεχομένου ===== |
| Είδαμε ότι οι δείκτες είναι μεταβλητές που περιέχουν διευθύνσεις μνήμης στις οποίες αποθηκεύονται άλλες μεταβλητές. Μέσω των δεικτών μπορούμε να διαβάσουμε και να γράψουμε το περιεχόμενο μία διεύθυνσης μνήμης. Υπάρχουν όμως περιπτώσεις που ένας δείκτης είναι επιθυμητό να διαβάζει μόνο τα περιεχόμενα των διευθύνσεων μνήμης στα οποία δείχνει χωρίς να μπορεί να τα μεταβάλλει. Σε αυτή την περίπτωση αρκεί να δηλώσετε τον τύπο δεδομένων στον οποίο δείχνει ο δείκτης ως //const// ως εξής: | Είδαμε ότι οι δείκτες είναι μεταβλητές που περιέχουν διευθύνσεις μνήμης στις οποίες αποθηκεύονται άλλες μεταβλητές. Μέσω των δεικτών μπορούμε να διαβάσουμε και να γράψουμε το περιεχόμενο μία διεύθυνσης μνήμης. Υπάρχουν όμως περιπτώσεις που ένας δείκτης είναι επιθυμητό να διαβάζει μόνο τα περιεχόμενα των διευθύνσεων μνήμης στα οποία δείχνει χωρίς να μπορεί να τα μεταβάλλει. Σε αυτή την περίπτωση αρκεί να δηλώσετε τον τύπο δεδομένων στον οποίο δείχνει ο δείκτης ως //const// ως εξής: | ||
| Line 176: | Line 180: | ||
| </ | </ | ||
| - | Από τον παραπάνω παράδειγμα παρατηρήστε ότι ο δείκτης //start// στη συνάρτηση // | + | Από τον παραπάνω παράδειγμα παρατηρήστε ότι ο δείκτης //start// στη συνάρτηση // |
| + | |||
| + | ===== Δείκτες αμετάβλητης διεύθυνσης ===== | ||
| + | |||
| + | Υπάρχουν περιπτώσεις | ||
| <code c> | <code c> | ||
| Line 183: | Line 191: | ||
| </ | </ | ||
| - | όπως στο παρακάτω παράδειγμα: | + | Το παρακάτω παράδειγμα |
| <code c const_address.cpp> | <code c const_address.cpp> | ||
| Line 204: | Line 212: | ||
| </ | </ | ||
| - | Τέλος εάν επιθυμείτε να έχετε | + | ===== Δείκτες αμετάβλητης διεύθυνσης και αμετάβλητου |
| + | |||
| + | Στην περίπτωση που | ||
| <code c>char c = ' | <code c>char c = ' | ||
| Line 240: | Line 250: | ||
| - | <WRAP center round tip 80%> | + | <WRAP center round tip 95%> |
| Συμπερασματικά, | Συμπερασματικά, | ||
| <code c> | <code c> | ||
| int x; | int x; | ||
| - | int * p1 = & | + | int * p1 = & |
| - | const int * p2 = & | + | const int * p2 = & |
| - | int * const p3 = & | + | int * const p3 = & |
| - | const int * const p4 = & | + | const int * const p4 = & |
| </ | </ | ||
| </ | </ | ||
| Line 264: | Line 274: | ||
| cout << i << ". " << str[i] << endl; | cout << i << ". " << str[i] << endl; | ||
| } | } | ||
| - | |||
| </ | </ | ||
| Line 271: | Line 280: | ||
| Μπορείτε να έχετε δείκτες οι οποίοι δείχνουν σε άλλους δείκτες οι οποίοι με την σειρά τους δείχνουν σε δεδομένα. Ενδεικτικά δείτε το παρακάτω παράδειγμα δεικτών. Οι τέσσερις τελευταίες γραμμές του παρακάτω κώδικα είναι ισοδύναμες και θέτουν το περιεχόμενο της μεταβλητής '' | Μπορείτε να έχετε δείκτες οι οποίοι δείχνουν σε άλλους δείκτες οι οποίοι με την σειρά τους δείχνουν σε δεδομένα. Ενδεικτικά δείτε το παρακάτω παράδειγμα δεικτών. Οι τέσσερις τελευταίες γραμμές του παρακάτω κώδικα είναι ισοδύναμες και θέτουν το περιεχόμενο της μεταβλητής '' | ||
| - | < | + | < |
| char a = ' | char a = ' | ||
| char *b = & | char *b = & | ||
| Line 286: | Line 295: | ||
| ===== Δείκτες τύπου void ===== | ===== Δείκτες τύπου void ===== | ||
| - | Ο δείκτης τύπου //void// είναι ένας ειδικός τύπος δείκτη ο οποίος έχει το πλεονέκτημα ότι μπορεί να μετατραπεί σε οποιονδήποτε τύπο δείκτη. Επίσης οποιοσδήποτε τύπος δείκτη μπορεί να μετατραπεί σε δείκτη τύπου //void//. Ο περιορισμός των δεικτών τύπου void είναι ότι δεν υπάρχει τρόπος να προσπελάσουμε το περιεχόμενο | + | Ο δείκτης τύπου //void// είναι ένας ειδικός τύπος δείκτη ο οποίος έχει το πλεονέκτημα ότι μπορεί να μετατραπεί σε οποιονδήποτε τύπο δείκτη. Επίσης οποιοσδήποτε τύπος δείκτη μπορεί να μετατραπεί σε δείκτη τύπου //void//. Ο περιορισμός των δεικτών τύπου void είναι ότι δεν υπάρχει τρόπος να προσπελάσουμε |
| <code c void_ptr.cpp> | <code c void_ptr.cpp> | ||
| Line 314: | Line 323: | ||
| <WRAP center round tip 80%> | <WRAP center round tip 80%> | ||
| - | Δεν | + | Δεν |
| </ | </ | ||
| Line 323: | Line 332: | ||
| <code c++ nullptr.cpp> | <code c++ nullptr.cpp> | ||
| #include < | #include < | ||
| + | |||
| char *p = 0; | char *p = 0; | ||
| char *q = NULL; | char *q = NULL; | ||
| Line 333: | Line 343: | ||
| ===== Δείκτες σε συναρτήσεις ===== | ===== Δείκτες σε συναρτήσεις ===== | ||
| - | Η C++ επιτρέπει την χρήση δεικτών σε συναρτήσεις. Οι δείκτες σε συναρτήσεις ορίζονται όπως οι συναρτήσεις με την διαφορά ότι το όνομα του δείκτη περικλύεται από παρενθέσεις και πριν από το όνομα προηγείται ο τελεστής | + | Η C++ επιτρέπει την χρήση δεικτών σε συναρτήσεις. Οι δείκτες σε συναρτήσεις ορίζονται όπως οι συναρτήσεις με την διαφορά ότι το όνομα του δείκτη περικλύεται από παρενθέσεις και πριν από το όνομα προηγείται ο τελεστής *. Δείτε το παρακάτω παράδειγμα ορισμού και χρήσης ενός δείκτη σε συνάρτηση. |
| <code c++ functionPointer.cpp> | <code c++ functionPointer.cpp> | ||
| Line 342: | Line 352: | ||
| int subtraction (int a, int b){ return a-b; } | int subtraction (int a, int b){ return a-b; } | ||
| - | int operation (int x, int y, int (*functocall)(int, | + | int operation (int x, int y, int (*functioncall)(int, |
| - | int r = (*functocall)(x,y); | + | int r = (*functioncall)(x,y); |
| return r; | return r; | ||
| } | } | ||
| Line 357: | Line 367: | ||
| } | } | ||
| </ | </ | ||
| + | |||
| + | <WRAP center round tip 80%> | ||
| + | Παρατηρήστε ότι κατά τον ορισμό ενός δείκτη σε συνάρτηση είναι απαραίτητο να ορίσετε τον τύπο και τη σειρά των ορισμάτων που λαμβάνει η συνάρτηση. | ||
| + | </ | ||
| + | |||