DedSec Project

Pointer — DedSec Project

Δείκτης μνήμης — όρια, κοινή αναφορά και διάρκεια ζωής Τι σημαίνει στην πράξη Ένας δείκτης περιέχει πληροφορία για τη θέση ενός αντικειμένου στη μνήμη. Δεν εγγυάται από μόνος του ότι το…

Δείκτης μνήμης — όρια, κοινή αναφορά και διάρκεια ζωής

Τι σημαίνει στην πράξη

Ένας δείκτης περιέχει πληροφορία για τη θέση ενός αντικειμένου στη μνήμη. Δεν εγγυάται από μόνος του ότι το αντικείμενο ζει ακόμη, έχει τον σωστό τύπο ή αποτελεί πίνακα συγκεκριμένου μήκους. Στη C, επιτρέπεται να δείχνει σε στοιχείο πίνακα και υπό προϋποθέσεις να σχηματίζεται δείκτης μία θέση μετά το τέλος, αλλά εκείνος δεν επιτρέπεται να αποαναφερθεί. Η ανάγνωση αποδεσμευμένης μνήμης, αντικειμένου εκτός διάρκειας ζωής ή στοιχείου έξω από τα όρια αποτελεί απροσδιόριστη συμπεριφορά.

Εκτελέσιμο παράδειγμα C11

Αποθήκευσε ως pointer.c. Δοκίμασε gcc -std=c11 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined pointer.c -o pointer όπου υποστηρίζονται οι sanitizers και μετά ./pointer. Ο κώδικας δεν επιχειρεί επικίνδυνη πρόσβαση. Οι assertions ελέγχουν την κοινή αναφορά και την απόρριψη λανθασμένων δεικτών και θέσεων.

#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>

static bool replace_at(int *data, size_t length, size_t index, int value) {
    if (data == NULL || index >= length) return false;
    data[index] = value;
    return true;
}
int main(void) {
    int numbers[] = {3, 5, 8};
    size_t count = sizeof(numbers) / sizeof(numbers[0]);
    int *second = &numbers[1];
    assert(*second == 5);
    assert(replace_at(numbers, count, 1, 9));
    assert(*second == 9);
    assert(!replace_at(numbers, count, count, 10));
    assert(numbers[2] == 8);
    assert(!replace_at(NULL, 0, 0, 1));
    puts("PASS pointer: alias, lifetime-safe scope, bounds, NULL");
}

Ιδιοκτησία και εγγυήσεις

Ο second δείχνει στο numbers[1]. Αν αλλάξουμε αυτή τη θέση μέσω της replace_at, ο second βλέπει την ίδια νέα τιμή. Παραμένει έγκυρος όσο υπάρχει ο τοπικός πίνακας της main. Μία συνάρτηση που λαμβάνει int* δεν μπορεί να υπολογίσει από τον δείκτη το πλήρες μήκος του πίνακα: αυτό πρέπει να μεταβιβαστεί χωριστά. Η C δεν ελέγχει αυτόματα όρια. Ο έλεγχος NULL βοηθά αλλά δεν αποδεικνύει ότι κάθε μη κενή διεύθυνση είναι έγκυρη. Η επιστροφή διεύθυνσης τοπικής μεταβλητής είναι σφάλμα διάρκειας ζωής, ακόμη και αν η διεύθυνση τυπωθεί κανονικά.

Συγκεκριμένες ασκήσεις

  1. Δοκίμασε index ίσο με count: η συνάρτηση πρέπει να αποτύχει και να αφήσει τον πίνακα αμετάβλητο. Μην δοκιμάσεις πραγματική ανάγνωση εκτός ορίων.
  2. Εξήγησε θεωρητικά γιατί δείκτης σε τοπική μεταβλητή δεν μπορεί να χρησιμοποιηθεί μετά την επιστροφή της συνάρτησης. Αντιπαράβαλε με πίνακα ιδιοκτησίας καλούντος.
  3. Σύγκρινε const int * με int *const και σημείωσε αν περιορίζεται η μεταβολή της τιμής ή του ίδιου του δείκτη.
  4. Σύγκρινε μεταγλώττιση με και χωρίς βελτιστοποίηση, χωρίς να συμπεράνεις ασφάλεια μόνο από την απουσία crash.

Παραδοτέα: PASS, έκδοση compiler, σωστό μήκος πίνακα και σαφής εξήγηση διάρκειας ζωής και ιδιοκτησίας.