User Tools

Site Tools


cpp:pointers

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Next revision Both sides next revision
cpp:pointers [2021/04/27 03:51]
gthanos [Αρχικοποίηση δεικτών]
cpp:pointers [2021/04/27 04:40]
gthanos [Δείκτες τύπου void]
Line 17: Line 17:
 ===== Αρχικοποίηση δεικτών ===== ===== Αρχικοποίηση δεικτών =====
  
-Η διεύθυνση μνήμης μίας μεταβλητής μπορεί να δοθεί με χρήση του τελεστή **&**. Οποιοσδήποτε δείκτης μπορεί να αρχικοποιηθεί λαμβάνοντας την διεύθυνση μνήμης μίας μεταβλητής ως εξής:+Η διεύθυνση μνήμης μίας μεταβλητής μπορεί να δοθεί με χρήση του τελεστή **&** (//reference operator//). Οποιοσδήποτε δείκτης μπορεί να αρχικοποιηθεί λαμβάνοντας την διεύθυνση μνήμης μίας μεταβλητής ως εξής:
 <code c> <code c>
 char c = 'a'; char *c_ptr = &a; char c = 'a'; char *c_ptr = &a;
Line 31: Line 31:
 </code> </code>
  
-==  Παράδειγμα αρχικοποίησης δεικτών ==+====  Παράδειγμα αρχικοποίησης δεικτών ====
  
 <code c> <code c>
Line 38: Line 38:
 </code> </code>
  
-Ο παραπάνω κώδικας αρχικοποίησης ενός δείκτη απεικονίζεται γραφικά για ένα σύστημα 32bit στο σχήμα που ακολουθεί. Οι διευθύνσεις μνήμης στις οποίες δηλώνονται οι μεταβλητές +Ο παραπάνω κώδικας αρχικοποίησης ενός δείκτη απεικονίζεται γραφικά για ένα σύστημα 32bit στο σχήμα που ακολουθεί. Η μεταβλητή **foo_ptr** αποθηκεύει τη διεύθυνση της μεταβλητής **foo** (//0x55884421//).
  
 {{ :cpp:reference_operator2.png? |}} {{ :cpp:reference_operator2.png? |}}
Line 44: Line 44:
 ===== Λήψη του περιεχομένου της τιμής ενός δείκτη (pointer dereference) ===== ===== Λήψη του περιεχομένου της τιμής ενός δείκτη (pointer dereference) =====
  
-Μπορείτε να χρησιμοποιήσετε την τιμή ενός δείκτη για να προσπελάσετε την μεταβλητή στην οποία αυτοί δείχνουν. Αυτό μπορεί να γίνει με χρήση του τελεστή * (dereference operator) πριν από το όνομα του δείκτη ως εξής:+Μπορείτε να χρησιμοποιήσετε την τιμή ενός δείκτη για να προσπελάσετε τη μεταβλητή στην οποία αυτός δείχνει με χρήση του τελεστή ** * ** (//dereference operator//) πριν από το όνομα του δείκτη ως εξής:
 <code c> <code c>
 char foo = 'a'; char foo = 'a';
Line 53: Line 53:
 {{ :cpp:dereference1_operator.png |}} {{ :cpp:dereference1_operator.png |}}
  
-Η μεταβλητή **foo_ptr** αποθηκεύει τη διεύθυνση της μεταβλητής **foo** (//0x55884421//). Η μεταβλητή **bar** αποθηκεύει το περιεχόμενο της διεύθυνσης που δείχνει η μεταβλητή **foo_ptr**, δηλ το περιεχόμενο της μεταβλητής **foo** που είναι ο χαρακτήρας 'a'.+ Η μεταβλητή **bar** αποθηκεύει το περιεχόμενο της διεύθυνσης που δείχνει η μεταβλητή **foo_ptr**, δηλ το περιεχόμενο της μεταβλητής **foo** που είναι ο χαρακτήρας 'a'.
  
 <WRAP center round tip 80%> <WRAP center round tip 80%>
Line 62: Line 62:
 ===== Αριθμητική δεικτών ===== ===== Αριθμητική δεικτών =====
  
-Η αριθμητική δεικτών περιορίζεται μόνο στις πράξεις της πρόσθεσης και της αφαίρεσης. Μπορούμε να αυξήσουμε ή να μειώσουμε την τιμή ενός δείκτη κατά μία ή περισσότερες θέσεις. Η μεταβολή της τελικής τιμής του δείκτη είναι συνυφασμένη με τον μέγεθος του τύπου δεδομένων στον οποίδείχνει ο δείκτης. Για παράδειγμα, ας υποθέσουμε ότι έχουμε τρεις (3) διαφορετικούς δείκτες όπως παρακάτω:+Η αριθμητική δεικτών περιορίζεται μόνο στις πράξεις της πρόσθεσης και της αφαίρεσης. Μπορούμε να αυξήσουμε ή να μειώσουμε την τιμή ενός δείκτη κατά μία ή περισσότερες θέσεις. Η μεταβολή της τελικής τιμής του δείκτη συνδέεται με τον μέγεθος του τύπου δεδομένων στον οποίο δείχνει ο δείκτης. Για παράδειγμα, ας υποθέσουμε ότι έχουμε τρεις δείκτες που δείχνουν σε διαφορετικούς τύπους μεταβλητών, όπως παρακάτω:
  
 <code c++> <code c++>
Line 71: Line 71:
 </code> </code>
  
-Αν και όλοι οι δείκτες καταλαμβάνουν τον ίδιο χώρο στη μνήμη (4-byte για συστήματα 32-bit και 8-byte για συστήματα 64-bit) διαφοροποιούνται ως προς το μέγεθος του περιεχόμενου των δεδομένων στα οποία δείχνουν. Στο παραπάνω παράδειγμα η μεταβλητή //mychar// δείχνει σε δεδομένα μήκους ενός byte. Ας υποθέσουμε επίσης ότι η μεταβλητή //myshort// δείχνει σε δεδομένα μήκους σε δεδομένα μήκους 2 byte (εξαρτάται από την μηχανή) και η μεταβλητή //myint// δείχνει σε δεδομένα μήκους 4byte (εξαρτάται και πάλι από την μηχανή). +Στο παραπάνω παράδειγμα η μεταβλητή //mychar// δείχνει σε δεδομένα μήκους ενός byte. Ας υποθέσουμε επίσης ότι η μεταβλητή //myshort// δείχνει σε δεδομένα μήκους 2 bytes (εξαρτάται από το hw) και η μεταβλητή //myint// δείχνει σε δεδομένα μήκους 4 bytes (εξαρτάται και πάλι από το hw). 
  
 Ας επιχειρήσουμε να μεταβάλλουμε τις τιμές των παραπάνω δεικτών κατά μία θέση ως εξής: Ας επιχειρήσουμε να μεταβάλλουμε τις τιμές των παραπάνω δεικτών κατά μία θέση ως εξής:
Line 84: Line 84:
   - η μεταβλητή myshort μετακινείται κατά //sizeof(short)// (2 bytes)   - η μεταβλητή myshort μετακινείται κατά //sizeof(short)// (2 bytes)
   - η μεταβλητή myint μετακινείται κατά //sizeof(int)// (4 bytes)   - η μεταβλητή myint μετακινείται κατά //sizeof(int)// (4 bytes)
 +
 +Θα πρέπει να έχετε υπόψη ότι όλοι οι δείκτες καταλαμβάνουν τον ίδιο χώρο στη μνήμη (4-byte για συστήματα 32-bit και 8-byte για συστήματα 64-bit) ανεξάρτητα από τον τύπο δεδομένων στον οποίο δείχνουν.
 ===== Δείκτες και πίνακες ===== ===== Δείκτες και πίνακες =====
  
-Η δήλωση ενός πίνακα ισοδυναμεί με ένα δείκτη στην πρώτη θέση του πίνακα. Στην πραγματικότητα ένας πίνακας μπορεί να αναθέσει την τιμή του σε ένα δείκτη του ιδίου τύπου ως εξής:+Η δήλωση ενός πίνακα ισοδυναμεί με ένα δείκτη που δείχνει στην πρώτη θέση του πίνακα. Για το λόγο αυτόένας πίνακας μπορεί να αναθέσει την τιμή του σε ένα δείκτη του ιδίου τύπου ως εξής:
  
 <code c> <code c>
Line 93: Line 95:
 </code>  </code> 
  
-Αν και ένας πίνακας μπορεί να αντιμετωπιστεί και ως δείκτης η βασική διαφορά πινάκων και δεικτών είναι ότι ο πίνακας δεν μπορεί να μεταβάλλει την διεύθυνση στην οποία δείχνει, δηλαδή πρόκειται για ένα δείκτη που έχει σταθερή τιμή. Για παράδειγμα η παρακάτω ανάθεση δεν είναι δυνατόν να συμβεί, διότι ο πίνακας δεν μπορεί να μεταβάλλει την διεύθυνση στην οποία δείχνει. +Αν και ένας πίνακας μπορεί να αντιμετωπιστεί και ως δείκτης η βασική διαφορά πινάκων και δεικτών είναι ότι ο πίνακας δεν μπορεί να μεταβάλλει την διεύθυνση στην οποία δείχνει, δηλαδή πρόκειται για ένα δείκτη σταθερής διεύθυνσης. Για παράδειγμα η παρακάτω ανάθεση δεν είναι δυνατόν να συμβεί
-<code c>myarray = myarray_ptr;</code>+ 
 +<code c>myarray = myarray_ptr+1;</code>
  
-Δείτε το παρακάτω παράδειγμα όπου χρησιμοποιούνται πίνακας από ακεραίους και ένας δείκτης σε ακέραιο που δείχνει στις επιμέρους τιμές του πίνακα. Στα σχόλια η θέση του πίνακα που προσπελαύνεται κάθε φορά.+Δείτε το παρακάτω παράδειγμα όπου χρησιμοποιούνται πίνακας από ακεραίους και ένας δείκτης σε ακέραιο που δείχνει στις επιμέρους τιμές του πίνακα. Τα σχόλια περιέχουν τη θέση του πίνακα που προσπελάζεται κάθε φορά.
  
 <code cpp pointers-arrays.cpp> <code cpp pointers-arrays.cpp>
Line 106: Line 109:
   int numbers[5];   int numbers[5];
   int * p;   int * p;
-  p = numbers;  *p = 10;      // numbers[0] +  p = numbers;  *p = 10;          // numbers[0] 
-  p++;  *p = 20;              // numbers[1] +  p++;  *p = 20;                  // numbers[1] 
-  p = &numbers[2];  *p = 30;  // numbers[2] +  p = &numbers[2];  *p = 30;      // numbers[2] 
-  p = numbers + 3;  *p = 40;  // numbers[3] +  p = numbers + 3;  *p = 40;      // numbers[3] 
-  p = numbers;  *(p+4) = 50;  // numbers[4]+  p = numbers;  *(p+4) = 50;      // numbers[4] 
 +  
   for (int n=0; n<5; n++)   for (int n=0; n<5; n++)
     cout << numbers[n] << ", ";     cout << numbers[n] << ", ";
Line 119: Line 123:
  
 <WRAP tip 80% center round> <WRAP tip 80% center round>
-Οι δείκτες οφείλουν να δείχνουν σε διευθύνσεις μνήμης που ανήκουν στην διεργασία που εκτελείται. Στην πραγματικότητα όμως οι δείκτες μπορούν να πάρουν οποιαδήποτε τιμή ακόμη και τιμές εκτός των ορίων της που έχουν ανατεθεί από το λειτουργικό στην τρέχουσα διεργασία. Η προσπέλαση δεικτών που δεν ανήκουν στη διεργασία δημιουργεί σφάλμα που τερματίζει το πρόγραμμα. Δείτε το παρακάτω παράδειγμα όπου ένας δείκτης αρχικοποιείται σε τιμή εκτός των ορίων του πίνακα //intArray//.+Οι δείκτες οφείλουν να δείχνουν σε διευθύνσεις μνήμης που ανήκουν στη διεργασία που εκτελείται. Στην πραγματικότητα όμως οι δείκτες μπορούν να πάρουν οποιαδήποτε τιμή ακόμη και τιμές εκτός του εύρους διευθύνσεων που έχουν ανατεθεί από το λειτουργικό σύστημα στην τρέχουσα διεργασία. Η ανάγνωση από διεύθυνση ή η εγγραφή σε διεύθυνση που δεν ανήκει στη διεργασία δημιουργεί σφάλμα που τερματίζει το πρόγραμμα. Δείτε το παρακάτω παράδειγμα όπου ένας δείκτης αρχικοποιείται σε τιμή εκτός των ορίων του πίνακα //intArray//. 
 <code c++ intArrayPointer.cpp> <code c++ intArrayPointer.cpp>
 #include <iostream> #include <iostream>
Line 131: Line 136:
 </WRAP> </WRAP>
  
-===== Δείκτες αμετάβλητου περιεχομένου και δείκτες αμετάβλητης διεύθυνσης (const) =====+===== Δείκτες αμετάβλητου περιεχομένου =====
  
 Είδαμε ότι οι δείκτες είναι μεταβλητές που περιέχουν διευθύνσεις μνήμης στις οποίες αποθηκεύονται άλλες μεταβλητές. Μέσω των δεικτών μπορούμε να διαβάσουμε και να γράψουμε το περιεχόμενο μία διεύθυνσης μνήμης. Υπάρχουν όμως περιπτώσεις που ένας δείκτης είναι επιθυμητό να διαβάζει μόνο τα περιεχόμενα των διευθύνσεων μνήμης στα οποία δείχνει χωρίς να μπορεί να τα μεταβάλλει. Σε αυτή την περίπτωση αρκεί να δηλώσετε τον τύπο δεδομένων στον οποίο δείχνει ο δείκτης ως //const// ως εξής: Είδαμε ότι οι δείκτες είναι μεταβλητές που περιέχουν διευθύνσεις μνήμης στις οποίες αποθηκεύονται άλλες μεταβλητές. Μέσω των δεικτών μπορούμε να διαβάσουμε και να γράψουμε το περιεχόμενο μία διεύθυνσης μνήμης. Υπάρχουν όμως περιπτώσεις που ένας δείκτης είναι επιθυμητό να διαβάζει μόνο τα περιεχόμενα των διευθύνσεων μνήμης στα οποία δείχνει χωρίς να μπορεί να τα μεταβάλλει. Σε αυτή την περίπτωση αρκεί να δηλώσετε τον τύπο δεδομένων στον οποίο δείχνει ο δείκτης ως //const// ως εξής:
Line 175: Line 180:
 </code> </code>
  
-Από τον παραπάνω παράδειγμα παρατηρήστε ότι ο δείκτης //start// στη συνάρτηση //print_all// δεν μεταβάλλει τα δεδομένα στη μνήμηόμως το περιεχόμενο του δεν παραμένει σταθερό, αλλά μεταβάλλεται. Στην περίπτωση που επιθυμείτε τη χρήση ενός δείκτη ο οποίος δεν μεταβάλλει τη διεύθυνση στην οποία δείχνει, αλλά είναι σε θέση να μεταβάλλει το περιεχόμενο της διεύθυνσης αυτής τότε μπορείτε να δηλώσετε ένα δείκτη της μορφή +Από τον παραπάνω παράδειγμα παρατηρήστε ότι ο δείκτης //start// στη συνάρτηση //print_all// μεταβάλλει την τιμή τουαλλά δεν μεταβάλλει τα περιεχόμενα των διευθύσεων στα οποία δείχνει
 + 
 +===== Δείκτες αμετάβλητης διεύθυνσης ===== 
 + 
 +Υπάρχουν περιπτώσεις που είναι επιθυμητή η χρήση δεικτών που δεν μεταβάλλουν τη διεύθυνση στην οποία δείχνουν, αλλά μπορούν να μεταβάλλει το περιεχόμενο της διεύθυνσης αυτής. Σε αυτή την περίπτωση η δήλωση //const// δίνεται αμέσως μετά τον τύπο του δείκτη, όπως παρακάτω:
  
 <code c> <code c>
Line 182: Line 191:
 </code> </code>
  
-όπως στο παρακάτω παράδειγμα:+Το παρακάτω παράδειγμα είναι ενδεικτικό της χρήσης ενός τέτοιου δείκτη
  
 <code c const_address.cpp> <code c const_address.cpp>
Line 203: Line 212:
 </code> </code>
  
-Τέλος εάν επιθυμείτε να έχετε ένα δείκτη ο οποίος δεν μεταβάλλει τη διεύθυνση στην οποία δείχνει και δεν μπορεί να μεταβάλλει το περιεχόμενο της διεύθυνσης αυτής τότε μπορείτε να τον δηλώσετε ως εξής:+===== Δείκτες αμετάβλητης διεύθυνσης και αμετάβλητου περιεχομένου ===== 
 + 
 +Στην περίπτωση που είναι επιθυμητή η χρήση ενός δείκτη που δεν μεταβάλλει τη διεύθυνση στην οποία δείχνει ούτε το περιεχόμενο τηςτότε μπορείτε να δηλώσετε τον δείκτη ως εξής:
  
 <code c>char c = 'a'; const char * const ptr = &c;</code> <code c>char c = 'a'; const char * const ptr = &c;</code>
Line 239: Line 250:
  
  
-<WRAP center round tip 80%>+<WRAP center round tip 95%>
 Συμπερασματικά, η χρήση του προσδιοριστή //const// μπορεί να δημιουργήσει δείκτες της εκάστοτε κατηγορίας ως εξής: Συμπερασματικά, η χρήση του προσδιοριστή //const// μπορεί να δημιουργήσει δείκτες της εκάστοτε κατηγορίας ως εξής:
 <code c> <code c>
 int x; int x;
-      int *       p1 = &x;  // non-const pointer to non-const int +      int *       p1 = &x;  // δείκτης χωρίς περιορισμούς 
-const int *       p2 = &x;  // non-const pointer to const int +const int *       p2 = &x;  // δείκτης αμετάβλητου περιεχομένου 
-      int * const p3 = &x;  // const pointer to non-const int +      int * const p3 = &x;  // δείκτης αμετάβλητης διεύθυνσης 
-const int * const p4 = &x;  // const pointer to const int +const int * const p4 = &x;  // δείκτης αμετάβλητου περιεχομένου και αμετάβλητης διεύθυνσης
 </code> </code>
 </WRAP> </WRAP>
Line 284: Line 295:
 ===== Δείκτες τύπου void ===== ===== Δείκτες τύπου void =====
  
-Ο δείκτης τύπου //void// είναι ένας ειδικός τύπος δείκτη ο οποίος έχει το πλεονέκτημα ότι μπορεί να μετατραπεί σε οποιονδήποτε τύπο δείκτη. Επίσης οποιοσδήποτε τύπος δείκτη μπορεί να μετατραπεί σε δείκτη τύπου //void//. Ο περιορισμός των δεικτών τύπου void είναι ότι δεν υπάρχει τρόπος να προσπελάσουμε το περιεχόμενο ενός ''void*'' δείκτη χωρίς προηγουμένως να τον μετατρέψουμε σε ένα διαφορετικό τύπο δείκτη. Δείτε το παρακάτω παράδειγμα που   χρησιμοποιεί ένα δείκτη ''void*'' για να προσπελάσει τα δεδομένα του.+Ο δείκτης τύπου //void// είναι ένας ειδικός τύπος δείκτη ο οποίος έχει το πλεονέκτημα ότι μπορεί να μετατραπεί σε οποιονδήποτε τύπο δείκτη. Επίσης οποιοσδήποτε τύπος δείκτη μπορεί να μετατραπεί σε δείκτη τύπου //void//. Ο περιορισμός των δεικτών τύπου void είναι ότι δεν υπάρχει τρόπος να προσπελάσουμε άμεσα το περιεχόμενο τους. Για την προσπέλαση τους θα πρέπει προηγούμενα να μετατραπούν σε ένα διαφορετικό τύπο δείκτη. Δείτε το παρακάτω παράδειγμα που χρησιμοποιεί ένα δείκτη ''void*'' για να προσπελάσει τα δεδομένα του.
  
 <code c void_ptr.cpp> <code c void_ptr.cpp>
Line 312: Line 323:
  
 <WRAP center round tip 80%> <WRAP center round tip 80%>
-Δεν επιτρέπεται η αριθμητική δεικτών σε δείκτες τύπου ''void*''.+Δεν υφίσταται η αριθμητική δεικτών σε δείκτες τύπου ''void*''.
 </WRAP> </WRAP>
  
cpp/pointers.txt · Last modified: 2023/04/25 13:14 by gthanos