Array C-Style, Puntatori, Reference e Funzioni
Important
L’indice degli array in C++ parte sempre da 0! (v1[0], v1[1], v1[2]).
In C++, non c’è nessun controllo sui limiti dell’array a runtime per massimizzare le performance.
#include <iostream>
int main() {
float vect[3] = {0.4, 1.34, 56.156};
1 for(int i = 0; i < 5; ++i) {
std::cout << "vect[" << i << "]: " << vect[i] << std::endl;
}
return 0;
}i=4 ma l’array arriva solo a i=2.
Warning
Si accede a porzioni di memoria adiacenti (Buffer Over-read/Over-write). Può stampare valori casuali o causare un crash critico (Segmentation Fault).
Possiamo definire array a più dimensioni. In memoria, la matrice 2D è comunque immagazzinata come un singolo blocco 1D contiguo (“Row-major order”).
#include <iostream>
int main() {
1 double matrix[2][3] = {
{1.0, 2.0, 3.0}, // Riga 0
{4.0, 5.0, 6.0} // Riga 1
};
// Accesso agli elementi: matrix[riga][colonna]
2 std::cout << "Elemento (1, 2): " << matrix[1][2] << std::endl;
// Somma di tutti gli elementi
double sum = 0.0;
for(int i = 0; i < 2; ++i) {
for(int j = 0; j < 3; ++j) {
sum += matrix[i][j];
}
}
return 0;
}6: cout non mostra gli zeri finali di un double (per cambiare formato serve <iomanip>).
&) è un alias (un altro nome) per la stessa identica variabile.*) è una variabile che contiene l’indirizzo di memoria di un’altra variabile.b è un puntatore. Contiene l’indirizzo di a.
x è un reference. È un sinonimo di a.
b (quindi modifica a!)
x (che essendo un sinonimo di a, modifica a!)
*).
*).
I puntatori possono puntare a locazioni di memoria invalide, causando disastri:
0 o NULL).
Segmentation fault (core dumped), ma non è garantito dallo standard — il compilatore, sapendo che non può accadere, è autorizzato a ottimizzare via il codice che lo circonda.
In C++ moderno si preferiscono sempre i Reference, usando i Puntatori solo se strettamente necessario.
| Caratteristica | Reference (&) |
Puntatore (*) |
|---|---|---|
| Inizializzazione | Deve essere assegnato subito | Può essere dichiarato vuoto (nullptr) |
| Riassegnazione | NON può cambiare bersaglio | Può essere riassegnato |
| Sintassi | Usato come una normale variabile | Richiede dereferenziazione (*a = 5;) |
| Sicurezza | Non può essere nullo | Rischio di Memory Leak e Crash |
Note
Quando usare un puntatore? Quando l’oggetto “può non esistere” (nullptr), o per gli array dinamici del C.
void.#include <iostream>
double calcola_energia_cinetica(double massa, double velocita) {
return 0.5 * massa * velocita * velocita;
}
void print_risultato(double res) {
std::cout << "Risultato: " << res << " J\n";
}
int main() {
double E = calcola_energia_cinetica(2.0, 10.0);
print_risultato(E);
return 0;
}Di default in C++, i parametri vengono passati Per Valore. Ciò significa che la funzione riceve una copia della variabile. Le modifiche fatte dentro la funzione non hanno alcun effetto sulla variabile esterna.
10!
Warning
Contro: Copiare oggetti di grandi dimensioni (es. Matrici 1000x1000) devasta le performance!
Se usiamo un Reference (&), passiamo un alias della variabile originale. Non viene fatta alcuna copia, e possiamo modificare la variabile originale.
&!
Questo approccio è utile per restituire “più di un risultato” da una funzione.
Quando vogliamo passare un oggetto “pesante” (come una Rete Neurale o una Matrice) ma NON vogliamo modificarlo, il Pass-by-Value è lentissimo. La soluzione universale è il Pass-by-Const-Reference.
#include <iostream>
#include <string>
1void analizza_testo(const std::string& testo_molto_lungo) {
std::cout << "Lunghezza: " << testo_molto_lungo.size();
2 // testo_molto_lungo += "a";
}
int main() {
std::string testo = "..."; // Molti MB di dati
analizza_testo(testo); // Zero copie, zero rischi!
return 0;
}const vieta le modifiche, & evita la copia in memoria!
const).
Arrivano dopo: la sintassi completa di const nella Lezione 6, std::string nella Lezione 11.
In C++ non si può passare un array nativo per valore. Gli array decadono (decay) sempre a un Puntatore al loro primo elemento.
// Sembra che chiediamo un array, ma il compilatore lo traduce in:
// void stampa_array(int* array, int lunghezza)
#include <iostream>
1void stampa_array(int array[], int lunghezza) {
for(int i=0; i<lunghezza; ++i) {
std::cout << array[i] << " ";
}
}
int main() {
int dati[5] = {1, 2, 3, 4, 5};
2 stampa_array(dati, 5);
return 0;
}int* array.
&dati[0].
In C++, il compilatore legge il file dall’alto verso il basso. Non può usare una funzione di cui non conosce l’esistenza!
Sbagliato:
#include <iostream>
int main() {
1 print();
return 0;
}
void print() {
std::cout << "Ciao";
}In C++ possiamo avere più funzioni con lo stesso nome, a patto che abbiano una firma (signature) diversa (cioè parametri diversi in numero o tipo).
// Funzione 1
#include <iostream>
void stampa(int x) {
std::cout << "Intero: " << x << std::endl;
}
// Funzione 2
void stampa(double x) {
std::cout << "Reale: " << x << std::endl;
}
// Funzione 3
void stampa(int x, int y) {
std::cout << "Coordinate: (" << x << ", " << y << ")" << std::endl;
}
int main() {
stampa(5); // Chiama Funzione 1
stampa(3.14); // Chiama Funzione 2
stampa(10, 20); // Chiama Funzione 3
return 0;
}Le funzioni possono avere valori predefiniti per gli ultimi argomenti della lista. Se l’utente non li fornisce, usa quelli di default.
z è opzionale. Se omesso, varrà 0.0.
z=0.0.
Warning
Regola ferrea: i parametri di default devono sempre stare in fondo alla lista. Con void f(int a=1, int b); il compilatore risponde error: default argument missing for parameter 2 of 'void f(int, int)'.
Scrivi un programma in C++ che:
void normalizza(double& x, double& y).main, definisci due variabili \(x=3.0, y=4.0\).normalizza.(Suggerimento: includi la libreria <cmath> per la funzione std::sqrt())
#include <iostream>
#include <cmath>
1void normalizza(double& x, double& y) {
double norma = std::sqrt(x*x + y*y);
if(norma > 0) {
x = x / norma;
y = y / norma;
}
}
int main() {
double x = 3.0;
double y = 4.0;
std::cout << "Prima: (" << x << ", " << y << ")\n";
2 normalizza(x, y);
std::cout << "Dopo : (" << x << ", " << y << ")\n";
return 0;
}& (reference) perché vogliamo modificare le variabili del main.
Dopo : (0.6, 0.8)