Δείκτες στο C: Τι είναι το Pointer στον προγραμματισμό C; Τύποι

⚡ Έξυπνη Σύνοψη

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

  • 📌 Ορισμός: Ένας δείκτης περιέχει τη διεύθυνση μνήμης μιας άλλης μεταβλητής.
  • 🔧 Operators: Ο τελεστής & επιστρέφει μια διεύθυνση, ενώ ο τελεστής * την αποαναφέρει.
  • 🧷 Δήλωση: Γράψτε data_type *name και, στη συνέχεια, αρχικοποιήστε με τη διεύθυνση μιας μεταβλητής.
  • 🏷️ τύποι: Οι δείκτες null, void και wild συμπεριφέρονται διαφορετικά και χρειάζονται φροντίδα.
  • Αριθμητική: Η αύξηση ενός δείκτη τον μετακινεί κατά το μέγεθος του βασικού του τύπου.
  • 🤖 Βοήθεια AI: Τα εργαλεία τεχνητής νοημοσύνης εντοπίζουν σφάλματα τμηματοποίησης και επισημαίνουν κρεμαστούς δείκτες κατά την αναθεώρηση.

Δείκτες σε C

Τι είναι ένας δείκτης στη C;

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

Πώς να χρησιμοποιήσετε τους δείκτες στο C

Αν δηλώσουμε μια μεταβλητή v τύπου int, η μεταβλητή v αποθηκεύει μια τιμή. Κάθε μεταβλητή, εκτός από την τιμή της, έχει και μια διεύθυνση, στην οποία βρίσκεται στη μνήμη. Η διεύθυνση μπορεί να ανακτηθεί τοποθετώντας ένα σύμβολο & πριν από το όνομα της μεταβλητής. Αν εκτυπώσετε τη διεύθυνση μιας μεταβλητής, μοιάζει με τυχαίο αριθμό που μπορεί να διαφέρει από εκτέλεση σε εκτέλεση.

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

int *y = &v;
ΜΕΤΑΒΛΗΤΟΣ ΔΕΙΚΤΗΣ
A αξία αποθηκεύονται σε ένα το όνομά του διεύθυνση αποθήκευσης/μνήμης. A μεταβλητή that δείχνει προς τη διεύθυνση αποθήκευσης/μνήμης του άλλος μεταβλητός.

Δήλωση δείκτη

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

data_type *pointer_variable_name;

Εδώ, Τύπος δεδομένων είναι ο βασικός τύπος του δείκτη και υποδεικνύει τον τύπο της μεταβλητής στην οποία δείχνει ο δείκτης. Ο αστερίσκος (*), το ίδιο σύμβολο που χρησιμοποιείται για τον πολλαπλασιασμό, είναι ο έμμεσος τελεστής που δηλώνει έναν δείκτη. Ορισμένες έγκυρες δηλώσεις δείκτη είναι:

int    *ptr_thing;       /* pointer to an integer */
int    *ptr1, thing;     /* ptr1 points to an int; thing is an int */
double *ptr2;           /* pointer to a double */
float  *ptr3;           /* pointer to a float */
char   *ch1;            /* pointer to a character */

Αρχικοποίηση ενός δείκτη

Αφού δηλώσουμε έναν δείκτη, τον αρχικοποιούμε με μια διεύθυνση μεταβλητής. Εάν ένας δείκτης δεν έχει αρχικοποιηθεί και χρησιμοποιηθεί, τα αποτελέσματα μπορεί να είναι απρόβλεπτα και ενδεχομένως καταστροφικά. Για να λάβετε τη διεύθυνση μιας μεταβλητής, χρησιμοποιήστε τον τελεστή ampersand (&) πριν από το όνομα της μεταβλητής:

pointer = &variable;

Ένα απλό πρόγραμμα που απεικονίζει έναν δείκτη δίνεται παρακάτω:

#include <stdio.h>
int main()
{
   int a = 10;     // variable declaration
   int *p;         // pointer variable declaration
   p = &a;        // store address of a in pointer p
   printf("Address stored in p is: %x\n", p);  // access the address
   printf("Value stored in p is: %d\n", *p);    // access the value
   return 0;
}

Παραγωγή:

Address stored in p is: 60ff08
Value stored in p is: 10
OperaTor Νόημα
* Εξυπηρετεί δύο σκοπούς: δηλώνει έναν δείκτη και επιστρέφει την τιμή της αναφερόμενης μεταβλητής (αποαναφορά).
& Εξυπηρετεί έναν σκοπό: επιστρέφει τη διεύθυνση μιας μεταβλητής.

Τύποι δεικτών στο C

Μηδενικός δείκτης

Δημιουργούμε έναν μηδενικό δείκτη αντιστοιχίζοντας την τιμή NULL κατά τη δήλωση. Αυτό είναι χρήσιμο όταν δεν έχει ακόμη αντιστοιχιστεί διεύθυνση στον δείκτη. Ένας μηδενικός δείκτης περιέχει πάντα την τιμή 0:

#include <stdio.h>
int main()
{
   int *p = NULL;     // null pointer
   printf("The value inside variable p is: %x", p);
   return 0;
}

Κενός δείκτης

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

#include <stdio.h>
int main()
{
   void *p = NULL;    // void pointer
   printf("The size of pointer is: %d\n", sizeof(p));
   return 0;
}

Άγριο Πόιντερ

Ένας δείκτης ονομάζεται wild δείκτης (wild pointer) αν δεν έχει αρχικοποιηθεί σε τίποτα. Αυτοί οι δείκτες δεν είναι αποτελεσματικοί επειδή μπορεί να δείχνουν σε μια άγνωστη θέση μνήμης, κάτι που μπορεί να προκαλέσει προβλήματα και να καταρρεύσει το πρόγραμμα. Να είστε πάντα προσεκτικοί με τους wild δείκτες:

#include <stdio.h>
int main()
{
   int *p;          // wild pointer
   printf("%d", *p);
   return 0;
}

Άλλοι τύποι δεικτών στη C περιλαμβάνουν τον κρεμαστό δείκτη, τον σύνθετο δείκτη, τον κοντινό δείκτη, τον μακρινό δείκτη και τον τεράστιο δείκτη.

Άμεσοι και έμμεσοι δείκτες πρόσβασης

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

#include <stdio.h>
int var = 1;        /* declare and initialize an int variable */
int *ptr;           /* declare a pointer to int */
int main(void)
{
   ptr = &var;       /* point ptr to var */
   printf("\nDirect access, var = %d", var);
   printf("\nIndirect access, var = %d", *ptr);
   *ptr = 48;        /* change var through the pointer */
   printf("\nIndirect access, var = %d", *ptr);
   return 0;
}

Αριθμητική δείκτη σε C

Όταν εργάζεστε με δείκτες, τηρείτε αυτούς τους κανόνες προτεραιότητας. Οι τελεστές * και & έχουν την ίδια προτεραιότητα με τους μοναδιαίους τελεστές (άρνηση !, αύξηση ++, μείωση –). Στην ίδια παράσταση, οι μοναδιαίοι τελεστές *, &, !, ++ και — αξιολογούνται από δεξιά προς τα αριστερά. Εάν ένας δείκτης P δείχνει σε μια μεταβλητή X, τότε το *P μπορεί να χρησιμοποιηθεί όπου μπορεί να γραφτεί το X. Οι ακόλουθες παραστάσεις είναι ισοδύναμες:

Έκφραση Ισοδύναμη έκφραση
Υ = *P + 1 Υ = Χ + 1
*P = *P + 10 Χ = Χ + 10
*P += 2 Χ += 2
++*Σελ ++Χ
(*P)++ X++

Στην τελευταία περίπτωση, απαιτούνται παρενθέσεις: επειδή οι μοναδιαίοι τελεστές * και ++ αξιολογούνται από δεξιά προς τα αριστερά, χωρίς παρενθέσεις ο δείκτης P θα αυξανόταν, όχι το αντικείμενο στο οποίο δείχνει. Οι αριθμητικές πράξεις που είναι διαθέσιμες στους δείκτες είναι η ανάθεση, η αύξηση και η μείωση, και η πρόσθεση ή η αφαίρεση.tracting μια ακέραια μετατόπιση, η οποία μετακινεί τον δείκτη κατά τόσα στοιχεία (κάθε βήμα ισούται με το μέγεθος σε bytes του βασικού τύπου).

Δείκτες C και πίνακες με παραδείγματα

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

#include <stdio.h>
int main()
{
   int a[5] = {1, 2, 3, 4, 5};   // array initialization
   int *p;                       // pointer declaration
   p = a;                       // p points to the first element; same as &a[0]
   for (int i = 0; i < 5; i++)
   {
      printf("%d\n", *p);            // print element
      p++;                      // move to the next element
   }
   return 0;
}

Η προσθήκη ενός αριθμού σε έναν δείκτη τον μετακινεί τόσα στοιχεία προς τα εμπρός. Αν το p δείχνει στη θέση μνήμης 0, τότε το p + 1 κάνει τον δείκτη να δείχνει στη θέση μνήμης 1.

C Δείκτες και συμβολοσειρές με παραδείγματα

Μια συμβολοσειρά είναι ένας πίνακας από αντικείμενα char που τελειώνουν με έναν μηδενικό χαρακτήρα '\0'. Μπορούμε να χειριστούμε συμβολοσειρές χρησιμοποιώντας δείκτες:

#include <stdio.h>
#include <string.h>
int main()
{
   char str[] = "Hello Guru99!";
   char *p;
   p = str;
   printf("First character is: %c\n", *p);
   p = p + 1;
   printf("Next character is: %c\n", *p);
   p = str;                     // reset the pointer
   for (int i = 0; i < strlen(str); i++)
   {
      printf("%c\n", *p);
      p++;
   }
   return 0;
}

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

#include <stdio.h>
int main()
{
   char *materials[] = {"iron", "copper", "gold"};
   for (int i = 0; i < 3; i++)
      printf("%s\n", materials[i]);
   return 0;
}

Πλεονεκτήματα των δεικτών στο C

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

Μειονεκτήματα των Pointers στο C

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

Συχνές Ερωτήσεις

Ένας κρεμαστός δείκτης δείχνει σε μνήμη που έχει ελευθερωθεί ή είναι εκτός εμβέλειας. Αποφύγετε το ορίζοντας τον δείκτη σε NULL αμέσως μετά την κλήση της λειτουργίας "free" και ελέγχοντας για NULL πριν από τη χρήση.

Η κατάργηση αναφοράς ενός δείκτη NULL προκαλεί σφάλμα τμηματοποίησης και διακόπτει τη λειτουργία του προγράμματος κατά το χρόνο εκτέλεσης. Πάντα να ελέγχετε ότι ένας δείκτης δεν είναι NULL πριν χρησιμοποιήσετε τον τελεστή * για ανάγνωση ή εγγραφή.

Ένας διπλός δείκτης, που δηλώνεται ως int **pp, αποθηκεύει τη διεύθυνση ενός άλλου δείκτη. Χρησιμοποιείται για δυναμικούς δισδιάστατους πίνακες και για την τροποποίηση ενός ορίσματος δείκτη μέσα σε μια συνάρτηση.

Ένας δείκτης αποθηκεύει μια διεύθυνση μνήμης και οι διευθύνσεις έχουν το ίδιο πλάτος σε μια δεδομένη πλατφόρμα, συνήθως 4 byte σε 32-bit και 8 byte σε 64-bit, ανεξάρτητα από τον τύπο στον οποίο δείχνει.

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

Συναρτήσεις όπως το malloc και το calloc επιστρέφουν έναν δείκτη στη μνήμη που έχει εκχωρηθεί στο σωρό κατά τον χρόνο εκτέλεσης. Έχετε πρόσβαση σε αυτήν τη μνήμη μέσω του δείκτη και την αποδεσμεύετε με free για να αποφύγετε διαρροές.

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

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

Συνοψίστε αυτήν την ανάρτηση με: