Οι δείκτες της C

Συζητήσεις για την γλώσσα C και C++

Συντονιστές: WebDev Moderators, Super-Moderators

Απάντηση
alex599
Δημοσιεύσεις: 66
Εγγραφή: 17 Δεκ 2008 01:11
Τοποθεσία: Πάτρα

Οι δείκτες της C

Δημοσίευση από alex599 » 30 Δεκ 2010 23:43

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

Ένας δείκτης πρόκειται για μια μεταβλητή η οποία δηλώνεται με τον κλασικό τρόπο με τον ειδικό χαρακτήρα * μπροστά από το όνομα της. Δηλαδή:

Κώδικας: Επιλογή όλων

 type *pointer; 
Ο τύπος type μπορεί να είναι οποιοσδήποτε βασικός τύπος ή κάποια δομή (ή κλάση για τη C++) ή ο ειδικός κενός τύπος void.

Η διαφορά με τις κλασικές μεταβλητές είναι το τι μπορεί να περιέχει ένας δείκτης. Έτσι, ένας δείκτης μπορεί να περιέχει την διεύθυνση μιας άλλης (κανονικής) μεταβλητής η οποία αποδίδεται με τον τελεστή &.

Άρα, ένας δείκτης μπορεί να πάρει τιμή ως εξής:

Κώδικας: Επιλογή όλων

 pointer=&variable; 
Ακόμη, μπορούμε να έχουμε και πολλαπλούς δείκτες οι οποίοι δείχνουν σε άλλους δείκτες με μια λιγότερη πολλαπλότητα. Για παράδειγμα:

Κώδικας: Επιλογή όλων

 type variable,*pointer,**d_pointer;
pointer=&variable;
d_pointer=&pointer; 
Αφού δημιουργήσουμε έναν δείκτη σε κάποια μεταβλητή, μπορούμε να αναφερθούμε στην τιμή που περιέχει με δύο τρόπους. Ο ένας είναι απ'ευθείας μέσω της μεταβλητής ή μέσω του δείκτη με τον τελεστή * ως εξής:

Κώδικας: Επιλογή όλων

*pointer;
Για παράδειγμα, το επόμενο πρόγραμμα θα τυπώσει διαδοχικά 1,1 και 2,2:

Κώδικας: Επιλογή όλων

main(){
   int a=1,*p=&a;
   printf("%d,%d\n",a,*p);
   ++(*p);
   printf("%d,%d\n",a,*p);
}
Αν θέλουμε ένας δείκτης να δείχνει στο κενό (να είναι μηδενικός) τότε μπορούμε να του δώσουμε την ειδική τιμή NULL η οποία πρακτικά είναι ίση με 0.

Στην περίπτωση που έχουμε ορίσει έναν δείκτη τύπου void, τότε μπορούμε να τους αποδώσουμε οποιαδήποτε τιμή μέσω type casting.

Αυτά τα λίγα προς το παρόν σχετικά με τους δείκτες. Μελλοντικά: Αριθμητική δεικτών, Συναρτήσεις και δείκτες, πίνακες και δείκτες, δομές και δείκτες, δυναμική μνήμη.
while(!dead()) ++knowledge;

Άβαταρ μέλους
dva_dev
Script Master
Δημοσιεύσεις: 3790
Εγγραφή: 16 Σεπ 2005 01:32
Επικοινωνία:

Οι δείκτες της C

Δημοσίευση από dva_dev » 31 Δεκ 2010 00:15

Για να είναι πιο κατανοητός ο κώδικας θα ήταν προτιμότερο να γραφεί κάπως έτσι:
(με την ευκαιρία η main επιστρέφει πάντα int και σωστότερο θα ήταν να γράφεται πάντα)

Κώδικας: Επιλογή όλων

int main()
{
    int a, *p;
    a = 1;
    p = &a;
    printf("%d,%d\n",a,*p);
    ++(*p);
    printf("%d,%d\n",a,*p);

    return 0;
}
Μια λίγο διαφορετική χρήση των pointers είχε συζητηθεί λίγο παλιότερα στο http://www.freestuff.gr/forums/viewtopic.php?t=39049
Υπάρχουν βέβαια και άλλα αντίστοιχα παραδείγματα αλλά λίγο πιο διασκορπισμένα.

alex599
Δημοσιεύσεις: 66
Εγγραφή: 17 Δεκ 2008 01:11
Τοποθεσία: Πάτρα

Οι δείκτες της C

Δημοσίευση από alex599 » 31 Δεκ 2010 11:06

Φίλε dva_dev, δεν ήξερα ότι είχαν συζητηθεί οι δέικτες και ήθελα να συλλέξω όλα όσα πρέπει να ξέρει κάποιος (αρχάριος) για τους δείκτες διότι συνήθως εκεί το χάνουν οι περισσότεροι.

Όσο για το παράδειγμα, ναι σίγουρα είναι πιο κατανοητό έτσι όπως το έγραψες αλλά οκ δεν παίζει ρόλο να δηλώνεται int η main(), μπορούμε να χρησιμοποιήσουμε και την exit(0) και φυσικά να κάνουμε #include <stdio.h>,<stdlib.h>.

Ευχαριστώ για τις παρατηρήσεις!
while(!dead()) ++knowledge;

alex599
Δημοσιεύσεις: 66
Εγγραφή: 17 Δεκ 2008 01:11
Τοποθεσία: Πάτρα

Οι δείκτες της C

Δημοσίευση από alex599 » 31 Δεκ 2010 11:25

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

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

Γενικά:

Κώδικας: Επιλογή όλων

++pointer; //pointer<-pointer+sizeof&#40;type&#41;;
pointer+=k; //pointer<-pointer+k*sizeof&#40;type&#41;;
Για παράδειγμα αν έχουμε έναν δείκτη που δείχνει σε ακέραιες μεταβλητές, τότε μια μοναδιαία αύξηση του θα αυξηθεί κατά sizeof(int)=4 έτσι ώστε να δείχνει στην αμέσως επόμενη "ακέραια" διεύθυνση:

Κώδικας: Επιλογή όλων

     Αρχικά     Διευθύνσεις         πράξη            Tελικά    Διευθύνσεις
                                                              
     pointer  ->   2000            ++pointer;                      2000
                   2001                                            2001
                   2002                                            2002
                   2003                                            2003 
                   2004                               pointer  ->  2004
Προφανώς αυτό ισχύει για οποιονδήποτε τύπο, δηλαδή ακόμη και για μια δομή η οποία καταλαμβάνεται πχ 64 bytes, τότε μια μοναδιαία αύξηση θα σήμαινε αύξηση κατά 64 θέσεις μνήμης.
while(!dead()) ++knowledge;

alex599
Δημοσιεύσεις: 66
Εγγραφή: 17 Δεκ 2008 01:11
Τοποθεσία: Πάτρα

Οι δείκτες της C

Δημοσίευση από alex599 » 31 Δεκ 2010 11:53

Μπορούμε να συνδυάσουμε τις συναρτήσεις μας με δείκτες ώστε να μπορέσουμε να επιστρέψουμε περισσότερα από ένα πράγματα από την συνάρτηση (αυτό μπορεί να γίνει και με μια δομή).

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

Κακό παράδειγμα:

Κώδικας: Επιλογή όλων

int *sum&#40;int a,int b&#41;&#123;
    int k=a+b;
    return &k;
&#125;
Εδώ επιστρέφουμε την διεύθυνση (δηλαδή δείκτη) μιας τοπικής μεταβλητής η οποία όμως μετά την επιστροφή της συνάρτησης δεν θα υπάρχει!

Το σημαντικό πλεονέκτημα των δεικτών είναι ότι συνδέουν το περιβάλλον κλήσης με την συνάρτηση που έχουμε καλέσει. Έτσι, όταν μεταβάλλουμε την τιμή μιας μεταβλητής δείκτη από την συνάρτηση αυτόματα μεταβάλλουμε την τιμή της και στην συνάρτηση κλήσης.

Για παράδειγμα:

Κώδικας: Επιλογή όλων

void sum&#40;int *s,int a,int b&#41;&#123;
    *s=a+b;
&#125;

int main&#40;&#41;&#123;
   int k;
   sum&#40;&k,1,1&#41;;
   printf&#40;"Άθροισμα&#58; %d",k&#41;;
   return 0;
&#125;
Το αποτέλεσμα του προγράμματος είναι 2 και αυτό διότι στέλνουμε στην συνάρτηση τον δείκτη μας (με την μορφή διεύθυνσης) και δύο αριθμούς. Το αποτέλεσμα της άθροισης τους που είναι 2 αποθηκεύεται στην διεύθυνση που έχει ο δείκτης s, δηλαδή στην διεύθυνση της μεταβλητής k!
while(!dead()) ++knowledge;

alex599
Δημοσιεύσεις: 66
Εγγραφή: 17 Δεκ 2008 01:11
Τοποθεσία: Πάτρα

Οι δείκτες της C

Δημοσίευση από alex599 » 31 Δεκ 2010 14:43

Οι πίνακες έχουν άμεση σχέση με τους δείκτες διότι το όνομα ενός πίνακα είναι ένας δείκτης!
Αν έχουμε μονοδιάστατους πίνακες τότε αυτός ο δείκτης δείχνει στο πρώτο στοιχείο του πινακα. Αυτό σημαίνει ότι εκτός από τον βασικό τρόπο αναφοράς στα στοιχεία ενος πίνακα array ο οποίος είναι ο: array, υπάρχει και ένας δευτερεύων τρόπος που χρησιμοποιεί δείκτες και είναι ο: *(array+i).

Για παράδειγμα δοκιμάστε το εξής πρόγραμμα που εμφανίζει τα στοιχεία ενός πίνακα Α:

Κώδικας: Επιλογή όλων

int main&#40;&#41;&#123;
    int i,A&#91;&#93;=&#123;1,2,3,4,5&#125;
    for &#40;i=0;i<5;i++&#41;&#123;
        printf&#40;"%d\n",*&#40;A+i&#41;&#41;;
    &#125;
&#125;
Σχόλιο: Εφόσον οι δείκτες είναι διευθύνσεις και στην ουσία η πράξη array+i μας κατευθύνει κατά i*sizeof(type) θέσεις μνήμης πάνω ή κάτω στην κύρια μνήμη μπορούμε ισοδύναμα να γράψουμε: array <-> *(array+i) <-> *(i+array) <-> i[array] !!!!!!

Το γεγονός ότι ένας πίνακας είναι δείκτης μας δίνει την δυνατότητα να γράψουμε συναρτήσεις που θα επεξεργάζονται τους πίνακες. Για παράδειγμα η επόμενη συνάρτηση κάνει αναζήτηση ενός κλειδιού σε έναν πίνακα:

Κώδικας: Επιλογή όλων

int search&#40;int *A,int N,int key&#41;&#123;
    int i;
    for &#40;i=0;i<N;i++&#41;&#123;
        if &#40;A&#91;i&#93;==key&#41; return 1;
    &#125;
    return 0;
&#125;
while(!dead()) ++knowledge;

Απάντηση

Επιστροφή στο “C, C++”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 0 επισκέπτες