Puntatori e memoria

Puntatori a funzione e callback in C e C++

In questa lezione passerai una funzione come parametro a un'altra funzione con i puntatori a funzione in C e le lambda in C++.

Introduzione

Il callback è la tecnica di passare una funzione a un'altra funzione, che la richiama quando serve. Così un algoritmo generico può eseguire il comportamento scelto da chi lo chiama senza conoscere i dettagli di implementazione. Per esempio, la stessa funzione apply può elevare al quadrato, raddoppiare o fare qualsiasi altra trasformazione.

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 square(int value) { return value * value; }
int apply(int value, int (*operation)(int)) { return operation(value); }

int main(void) {
    printf("%d\n", apply(6, square));
    return 0;
}
Risultato dell'esecuzione
36
C++
Esegui →
main.cpp
#include <functional>
#include <iostream>

int apply(int value, const std::function<int(int)>& operation) {
    return operation(value);
}

int main() {
    int factor = 6;
    std::cout << apply(factor, [](int value) { return value * value; }) << '\n';
}
Risultato dell'esecuzione
36

Confronto tra C e C++

In C, il parametro int (*operation)(int) è un puntatore a funzione che accetta funzioni con un parametro int e un valore di ritorno int, quindi apply(6, square) restituisce 36. In C++, le lambda e std::function possono contenere una varietà più ampia di oggetti richiamabili (callable). Nel codice che richiede alte prestazioni, i template sono spesso una scelta migliore di std::function.

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.

Esercitati con una sfida

Usa ciò che questa lezione spiega per risolvere un problema reale. Ogni sfida gira nel browser, con test immediati e una revisione dell'IA.

Tutte le sfide