Puntatori e memoria

Usare const correttamente in C e C++

In questa lezione userai la parola chiave const per dichiarare dati di sola lettura e distinguerai i diversi usi di const con i puntatori.

Introduzione

La parola chiave const permette a una funzione di promettere che non modificherà i dati che riceve. Se qualcuno modifica per sbaglio un dato marcato const, il compilatore segnala subito l'errore. Così il codice è più sicuro e chi legge capisce facilmente quali funzioni possono modificare i dati.

Punti importanti

  • Un puntatore deve puntare solo a un oggetto che esiste ancora, del tipo giusto, oppure essere nullo.
  • Ogni area di memoria allocata va liberata esattamente una volta.
  • Rendi chiaro quale funzione possiede la memoria (ownership) e quali dati sono solo letti (const).

Esempi di codice C e C++

C
Esegui →
main.c
#include <stdio.h>

int sum(const int *values, size_t count) {
    int total = 0;
    for (size_t i = 0; i < count; ++i) total += values[i];
    return total;
}

int main(void) {
    const int values[] = {2, 4, 6};
    printf("%d\n", sum(values, 3));
    return 0;
}
Risultato dell'esecuzione
12
C++
Esegui →
main.cpp
#include <array>
#include <iostream>

int sum(const std::array<int, 3>& values) {
    int total = 0;
    for (int value : values) total += value;
    return total;
}

int main() {
    const std::array values{2, 4, 6};
    std::cout << sum(values) << '\n';
}
Risultato dell'esecuzione
12

Confronto tra C e C++

Il parametro const int *values indica che la funzione sum legge solo gli elementi dell'array senza modificarli. Conviene leggere la dichiarazione partendo dal nome della variabile: un puntatore a dato const (const int *) protegge il valore puntato, mentre un puntatore const (int * const) non può puntare a un altro indirizzo. C++ ha anche le funzioni membro const, che promettono di non cambiare lo stato dell'oggetto.

C

Il possesso e la liberazione della memoria sono interamente a carico del programmatore.

C++

Contenitori e RAII liberano automaticamente le risorse quando l'oggetto arriva alla fine della sua vita.

Esercizi di approfondimento

Esegui entrambe le versioni e poi modifica il codice per osservare la differenza di sicurezza che offre ciascun linguaggio.

  • Prova con puntatori nulli e allocazioni di memoria fallite.
  • Esegui il programma con AddressSanitizer e UndefinedBehaviorSanitizer per trovare errori di memoria.
  • Annota chiaramente quale puntatore possiede la memoria e quale serve solo per leggere.