Πίνακας ως Δείκτης στην γλώσσα προγραμματισμού C

Πίνακας ως Δείκτης στην γλώσσα προγραμματισμού C

Τρόποι ορισμού πινάκων

Στη C, με τον όρο “πίνακας ως δείκτης”, εννοούμε ότι η μεταβλητή ενός πίνακα είναι ένας δείκτης σε μια μεταβλητή του ίδιου τύπου με τον πίνακα. Αυτός ο δείκτης δείχνει στο πρώτο στοιχείο του πίνακα και όλα τα υπόλοιπα στοιχεία του βρίσκονται στις επόμενες θέσεις της μνήμης.

Ένας πίνακας ακεραίων (integer) είναι ένας pointer τύπου integer, ένας πίνακας χαρακτήρων (char) είναι ένας pointer τύπου char, κ.α. Αυτός ο pointer δείχνει στο πρώτο στοιχείο του πίνακα.

Στη γλώσσα προγραμματισμού C μπορούμε να ορίσουμε ένα πίνακα με τους 3 παρακάτω τρόπους:

  • int pin[10];
  • int pin[] = {στοιχείο1, στοιχείο2, ..., στοιχείοΝ};
  • int* pin;

Σε όλες τις παραπάνω δηλώσεις η μεταβλητή pin είναι pointer τύπου int.

Σχετικά με τον πρώτο τρόπο

Όταν δηλώνουμε ένα πίνακα όπως στη 1η περίπτωση (int pin[10];) μέσα σε κάποια μέθοδο, μόνο στην ίδια μέθοδο η έκρφραση sizeof(pin) επιστρέφει το μήκος όλων των μεταβλητών μαζί. Δηλαδή στη περίπτωση αυτή, με 10 θέσεις ακεραίων, επιστρέφει 40. Άρα μπορούμε να υπολογίσουμε το μήκος ενός πίνακα με τη παρακάτω έκφραση:

int size = sizeof(pin)/sizeof(int)

όπου αντί για int βάζουμε τον τύπο του πίνακα όπως όταν τον ορίσαμε.

Όμως, η έκφραση sizeof(pin) έξω από τη συνάρτηση στην οποία ορίστηκε, δεν επιστρέφει το μέγεθος σε bytes όλων των στοιχείων αλλά το μέγεθος ενός δείκτη σε ακέραιο.

Πίνακας ως δείκτης και πέρασμα πίνακα ως παράμετρο

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

Με τον όρο “πίνακας ως δείκτης” εννοούμε ότι δεν υπάρχουν πίνακες όπως τους φανταζόμαστε, αλλά ότι απλά στη μνήμη, οι τιμές ενός πίνακα είναι αποθηκευμένες η μία μετά την άλλη με τη σωστή σειρά (η δεύτερη μετά τη πρώτη, η τρίτη μετά τη δεύτερη, κ.ο.κ.) και ότι η διεύθυνση της πρώτης μεταβλητής είναι αποθηκευμένη σε μια μεταβλητή pointer την οποία την αποκαλούμε πίνακα.

Η διεύθυνση μιας θέσης του πίνακα

Για να αναφερθούμε στη διεύθυνση μιας θέσης του πίνακα απλά προσθέτουμε στη μεταβλητή του τον αριθμό των θέσεων που βρίσκεται η ζητούμενη θέση μετά απο αυτόν. Για παράδειγμα για την πέμπτη θέση πρέπει να προσθέσουμε 4 (το άθροισμα είναι και αυτό τύπου pointer).

Για την τιμή της θέσης αρκεί να απο-αναφοροποιήσουμε το προηγούμενο άθροισμα με τον τελεστή. Για παράδειγμα αν ο πίνακας έχει όνομα pin τότε pin + x είναι η θέση του x+1 στοιχείου και *(pin + x) είναι η τιμή του x+1 στοιχείου. Επίσης η έκφραση pin[x] αναφέρεται στη τιμή της θέσης του x+1 στοιχείου.

Μεταβλητή πίνακας ως παράμετρο

Δε γίνεται να “μεταφέρουμε” την πληροφορία του μεγέθους του (size) μέσω της μεταβλητής (pin) του πίνακα έξω από τη συνάρτηση στην οποία ορίστηκε. Δηλαδή δεν μπορούμε να περάσουμε ως παράμετρο ένα πίνακα μόνο μέσω της μεταβλητής του. (Έτσι περνάμε ΜΟΝΟ το δείκτη στο πρώτο στοιχείο του).

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

Ακολουθεί παράδειγμα:

#include <stdio.h>  //χρειάζεται για την printf
#include <stdlib.h> //χρειάζεται για την malloc
// Συνάρτηση με παράμετρο πίνακα
// Και οι 3 παρακάτω τρόποι ορισμού της είναι σωστοί
int sinarthsh(int* pin, int c)
//int sinarthsh(int pin[], int c)
//int sinarthsh(int pin[10], int c)
{
    int i;
    for(i=0;i<c;i++)
    {
        //εκτελείται για κάθε στοιχείο
    }
}
int main()
{
    //int pin[10];
    //int pin[] = {στοιχείο1, στοιχείο2, ..., στοιχείοΝ};
    int* pin;    //πίνακας ως δείκτης
    //αρχικοποίηση του πίνακα pin
    pin = (int*)malloc(10 * sizeof(int));

    //αναφορά σε στοιχεία του πίνακα
    printf("%p", pin + 3); // Εμφάνιση της θέσης του 4ου στοιχείου
    *(pin + 3) = 5; // Καταχώριση της τιμής 5 στη 4η θέσητου πίνακα
    // οι εκφράσεις *(pin+3) και pin[3] είναι ισοδύναμες
    printf("%d", *(pin+3) == pin[3]) // εμφανίζει πάντα 1

    //κλήση συνάρτησης με πέρασμα πίνακα ως παράμετρο
    int apotelesma = sinarthsh(pin, 10);
}

Όταν ορίζουμε ένα πίνακα με τον 3ο τρόπο, πρέπει να οριοθετούμε μνήμη για όσες θέσεις θα χρειαστούμε. Σε αυτή τη περίπτωση θέλουμε ο πίνακας pin να έχει 10 θέσεις τύπου int άρα θα χρειαστούμε 10 * το μέγεθος του τύπου int.

Η συνάρτηση malloc()

Η συνάρτηση malloc ορίζει θέσεις στη μνήμη μετρώντας τες σε bytes. Η μοναδική παράμετρός της αναφέρεται στο πλήθος των bytes που πρέπει να οριοθετηθούν. Επιστρέφει τη διεύθυνση της πρώτης θέσης. Για να την καταχωρήσουμε στη μεταβλητή pin, πρέπει να τη μετατρέψουμε στον τύπο μεταβλητής pointer (int*).

Δισδιάστατοι πίνακες: Πίνακες πινάκων

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

Η πιο απλή περίπτωση είναι ο δισδιάστατος πίνακας, ο οποίος είναι ένας πίνακας πινάκων. Δηλαδή, ένας δισδιάστατος πίνακας ακεραίων είναι ένας πίνακας πινάκων ακεραίων, ένας δισδιάστατος πίνακας χαρακτήρων είναι ένας πίνακας πινάκων χαρακτήρων κ.α.

Πολυδιάστατοι πίνακες

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

Ας σκεφτούμε τον δισδιάστατο πίνακα σαν μια “ομαδοποίηση” πολλών πίνακων του ίδιου τύπου Α. Αυτός ο πίνακας είναι τύπου πίνακας τύπου Α ή αλλιώς είναι δισδιάστατος πίνακας τύπου Α. Αυτό σημαίνει ότι η κάθε μεταβλητή του είναι pointer και δείχνει σε ένα πίνακα τύπου Α.

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

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

Σύνταξη δισδιάστατων πινάκων σε παράδειγμα

#include <stdlib.h> //χρειάζεται για την malloc

int main()
{
    int i, j;    //μεταβλητές μετρητές
    
    //ορισμός δισδιάστατου ακέραιου πίνακα ως δείκτης σε δείκτη σε ακέραιο
    int** pin2d;
    
    //αρχικοποίηση του πατρικού πίνακα του δισδιάστατου ακέραιου πίνακα
    pin2d = (int**)malloc(3 * sizeof(int*));
    
    //αρχικοποίηση του κάθε υποπίνακα του δισδιάστατου ακέραιου πίνακα
    for (i=0;i<3;i++)
    {
        pin2d[i] = (int*)malloc(5 * sizeof(int));
    }
    
    pin2d[0,0] = 456;    //πρώτο στοιχείο
    pin2d[2,4] = 123;    //τελευταίο στοιχείο

    //αναφορά όλων των στοιχείων με εμφωλευμένο for
    for (i=0;i<3;i++)
    {
        for (ξ=0;i<4;i++)
        {
            pin2d[i][j] = i * j;
        }
    }
}

Απάντηση

Αυτός ο ιστότοπος χρησιμοποιεί το Akismet για να μειώσει τα ανεπιθύμητα σχόλια. Μάθετε πώς υφίστανται επεξεργασία τα δεδομένα των σχολίων σας.