Track
/
C++
C++
/
Esercizi
/
Fondazione Speedywagon
Fondazione Speedywagon

Fondazione Speedywagon

Esercizio di apprendimento

Introduzione

Come molti altri linguaggi, il C++ ha i puntatori. Sai già che i riferimenti e i puntatori sono simili, ma considerali un livello più vicino al funzionamento interno del tuo computer. I puntatori sono variabili che contengono indirizzi di oggetti. Servono a interagire direttamente con gli oggetti, consentendo l'allocazione dinamica della memoria e una manipolazione efficiente dei dati in C++.

Se è la prima volta che incontri i puntatori, possono sembrare un po' misteriosi, ma una volta che ci prendi la mano diventano piuttosto semplici.

Sono una parte cruciale del C++, quindi prenditi un po' di tempo per capirli davvero. La versione essenziale in questo concetto è anche chiamata dumb pointer o raw pointer. Con il C++ moderno esistono anche gli smart pointer: il tipo di base non è affatto intelligente e devi gestire tutto il lavoro manualmente.

Prima di addentrarci nei dettagli, vale la pena capire a cosa servono i puntatori. I puntatori sono un modo per condividere l'indirizzo di un oggetto con altre parti del nostro programma, il che è utile per due motivi principali:

  1. Come i riferimenti, i puntatori evitano le copie e aiutano a ridurre l'impronta di risorse del programma.
  2. A differenza dei riferimenti, i puntatori possono essere riassegnati ad oggetti diversi.
  3. I puntatori possono anche puntare a un valore nullo, per indicare che al momento non puntano a nessun oggetto.

Sintassi generale

La dichiarazione di un puntatore in C++ prevede di specificare il tipo di dato a cui il puntatore punta, seguito da un asterisco (*) e dal nome del puntatore. Quando si dichiara un puntatore, questo non viene inizializzato automaticamente. Senza un'assegnazione esplicita, un puntatore contiene di norma un valore indeterminato, spesso indicato come «indirizzo spazzatura». Alcuni compilatori potrebbero inizializzare i puntatori a nullptr, ma questo comportamento non è garantito su tutti i compilatori, quindi è essenziale non farci affidamento. La buona pratica è inizializzare esplicitamente i raw pointer e verificarne lo stato non nullo prima di utilizzarli, per evitare potenziali problemi.

int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid

Per assegnare l'indirizzo di una variabile ad un puntatore, si usa l'operatore di indirizzo (&). Dereferenziare un puntatore si fa con l'operatore di indirezione (*).

std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};

Attenzione: la dereferenziazione deve essere fatta esplicitamente, mentre i riferimenti funzionavano semplicemente come un alias.

Aritmetica dei puntatori

L'aritmetica dei puntatori ti permette di eseguire operazioni aritmetiche sui puntatori, cosa particolarmente utile quando si lavora con gli array. Aggiungere un intero a un puntatore lo fa puntare a un elemento diverso.

// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
Caution

L'aritmetica dei puntatori in C++ può facilmente portare a comportamento indefinito se non viene gestita con attenzione. Il comportamento indefinito può manifestarsi in esiti imprevisti del programma, arresti anomali o persino vulnerabilità di sicurezza. Un famoso esempio delle conseguenze del comportamento indefinito si è verificato nel 1996 con l'esplosione del razzo Ariane 5, quando un'eccezione software causata dalla conversione di un numero in virgola mobile a 64 bit in un intero con segno a 16 bit portò a un fallimento catastrofico.

Accedere alle variabili membro

In C++, l'operatore -> serve ad accedere ai membri di un oggetto attraverso un puntatore a quell'oggetto. È una scorciatoia che semplifica l'accesso ai membri degli oggetti puntati da un puntatore. Per esempio, se ptr è un puntatore a un oggetto con una variabile membro x, invece di usare (*ptr).x puoi usare direttamente ptr->x. Questo operatore migliora la leggibilità del codice e riduce la verbosità quando si lavora con puntatori a oggetti.

Ecco un breve esempio, con una struct Superhero che ha una variabile membro superpower. La funzione main crea un puntatore dianaPrince a un oggetto Superhero (che rappresenta Wonder Woman). L'operatore -> viene usato per accedere alla variabile membro superpower, mostrando il famoso «Lasso of Truth» di Wonder Woman.

struct Superhero {
    std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;

Puntatori e riferimenti a confronto

Sia i puntatori che i riferimenti consentono un accesso indiretto agli oggetti, ma differiscono per capacità e considerazioni sulla sicurezza. I puntatori offrono la flessibilità di cambiare l'oggetto a cui puntano e possono essere impostati a null. Tuttavia, questa flessibilità introduce dei rischi, come dereferenziare puntatori nulli o creare puntatori pendenti. I riferimenti, invece, non possono essere nulli e vengono legati a oggetti validi al momento della creazione, evitando questi rischi. Data la loro natura più sicura, conviene preferire i riferimenti ai puntatori, a meno che non siano necessarie le funzionalità aggiuntive offerte dai puntatori.

Istruzioni

Benvenuto, ingegnere! Sei uno degli ultimi veterani della Fondazione Speedywagon, un'organizzazione segreta che, per decenni, ha combattuto antiche minacce come i Pillar Men. Nel corso di questo impegno, hai passato anni a mantenere i sistemi tecnologici della Fondazione, costruiti con un mix di tecnologia all'avanguardia e librerie ormai datate.

Tuttavia, di recente i sensori che tracciano le attività dei Pillar Men non funzionano più correttamente. I sistemi della Fondazione sono vecchi, e il codice interagisce con una libreria C++ legacy che non può essere aggiornata. Il tuo compito è implementare quattro funzioni principali che monitorano l'attività dei sensori per i Pillar Men usando una libreria antiquata basata sui puntatori.

Da ingegnere C++ moderno, preferiresti usare gli smart pointer, ma ahimè, il codice legacy esige rispetto per i vecchi metodi. Il destino dell'umanità potrebbe dipendere da questi puntatori, quindi procedi con cautela, e che l'energia Hamon ti guidi.

Note

Dato che le letture dei sensori possono essere enormi, ti forniamo una struct di esempio, quella usata nella libreria reale. Il codice è già stato implementato per te nel file di intestazione.

struct pillar_men_sensor {
    int activity{};
    std::string location{};
    std::vector<int> data{};
};

1. Verifica della connessione del sensore (connection_check)

Il tuo primo compito è assicurarti che il sensore per i Pillar Men sia connesso correttamente. Non possiamo avere falsi allarmi innescati da sensori scollegati. Scriverai una funzione connection_check, che verifica se il puntatore del sensore è valido controllando che non sia nullptr.

  • Definisci una funzione che accetta un puntatore a una struct pillar_men_sensor.
  • La funzione deve restituire true se il puntatore del sensore non è nullo, e false in caso contrario.
pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false

2. Conteggio dell'attività dei sensori (activity_counter)

I Pillar Men si nascondono nell'ombra, e dobbiamo sapere se i sensori hanno rilevato qualche attività. Scriverai la funzione activity_counter, che riceve un array di sensori e una capacità che indica il numero di sensori nell'array.

  • Definisci una funzione che accetta un puntatore al primo elemento di un array e la capacità dell'array.
  • Usa l'aritmetica dei puntatori per scorrere l'array dei sensori e accumulare le letture di attività.
  • Restituisci l'attività accumulata.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123

3. Controllo dell'allarme (alarm_control)

Non ogni sensore deve innescare un allarme, a meno che non ci sia un pericolo reale. La funzione alarm_control fa sì che un sensore inneschi un allarme solo se il suo livello di attività è maggiore di 0. Questa funzione deve anche verificare che i sensori non siano nulli, per evitare crash del sistema.

  • Definisci una funzione che accetta il puntatore a un pillar_men_sensor.
  • La funzione deve prima verificare se il sensore è nullptr. Se il sensore è nullptr, restituisci false.
  • Se il sensore è valido e la sua attività è maggiore di 0, restituisci true; in caso contrario, restituisci false.
pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true

4. Verifica delle anomalie nei dati con la funzione uv_alarm

In questo compito implementerai la funzione uv_alarm per determinare se deve essere innescato un allarme in base ai livelli di esposizione ai raggi UV e all'attività del sensore. La funzione uv_alarm deve usare la funzione uv_light_heuristic fornita, che opera su un vettore di dati e restituisce un valore in base a determinate soglie. Questa è una versione di esempio del codice complesso che verrà eseguito in produzione: non cambiare l'interfaccia.

Definisci la funzione uv_alarm nel namespace speedywagon. Deve:

  • Accettare come parametro un puntatore a una struct pillar_men_sensor.
  • Restituire false se il puntatore del sensore è nullo.
  • Chiamare la funzione uv_light_heuristic, passando l'indirizzo dell'array data del sensore.
  • Restituire true se il valore restituito da uv_light_heuristic è maggiore del livello sensor->activity, altrimenti restituire false.
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
C++ Exercism

Vuoi iniziare Fondazione Speedywagon?

Iscriviti a Exercism per imparare e padroneggiare C++ con 19 concetti100 esercizi e il mentoring di persone reali, tutto gratis.