Tutorial
7
Scritto da Roberto Navigli (roberto.navigli@iol.it)
Game Programming Italia: www.gpi.eden.it
Variabili
locali e variabili globali
Le
variabili possono essere locali o globali. Una variabile
globale è visibile all'interno di tutto il
listato mentre una variabile locale è visibile
solo all'interno del blocco all'interno del quale
essa è dichiarata. Ad esempio:
#include
<iostream>
using namespace std;
void
main()
{
// a è locale alla funzione main()
int a = 1;
{
// l'intero b è locale a questo blocco
int b = 5;
// ok, b è visibile in questo blocco
cout << b;
}
// errore: b non è visibile
cout << b;
// ok: a è visibile all'interno di main()
cout << a;
}
Vediamo
ora la definizione di una variabile globale:
#include
<iostream>
using namespace std;
int
globale = 5;
void
funzione() { globale++; }
void
main()
{
globale++;
cout << "Prima: " << globale
<< endl;
funzione();
cout << "Dopo: " << globale
<< endl;
}
Compilando
quest'ultimo programma si ottiene in output:
Prima:
6
Dopo: 7
È
bene evitare il più possibile l'uso delle variabili
globali, poiché esse provocano spiacevoli effetti
collaterali. Una funzione infatti può fare
un uso della stessa variabile
incompatibile con quello che ne fa un'altra funzione.
Inoltre spesso l'uso di variabili globali porta forti
limitazioni ai programmi che si scrivono. Ad esempio:
#include
<iostream>
using namespace std;
//
numero di cui calcolare il fattoriale
int numero;
//
restituisce numero! (ovvero 1*2*
*n)
int fattoriale()
{
int i, risultato = 1;
for (i = 2; i <= numero; i++) risultato *= i;
return risultato;
}
void
main()
{
numero = 5;
cout << "5! = " << fattoriale()
<< endl;
}
La
funzione fattoriale(), dato il fattoriale della variabile
intera numero. Dato n, il fattoriale di n (indicato
con n!) è:
n!
= 1 * 2 * ... * n
Dopo
aver compilato ed eseguito il programma si ottiene
in output:
5!
= 120
Nell'esempio,
la variabile numero è globale. Essa viene inizializzata
con l'intero 5 nella funzione main. Quindi si chiama
la funzione fattoriale che calcola numero!. Il punto
è che la funzione non può essere utilizzata
per calcolare il fattoriale di qualsiasi intero, ma
solo della variabile numero. Usando variabili globali
si può ottenere qualcosa di meglio:
#include
<iostream>
using namespace std;
//
restituisce numero!
int fattoriale(int numero)
{
int i, risultato = 1;
for (i = 2; i <= numero; i++) risultato *= i;
return risultato;
}
void
main()
{
cout << "5! + 3! = " << fattoriale(5)+fattoriale(3)
<< endl;
}
Compilando
ed eseguendo il programma, in output si ottiene:
5!
+ 3! = 126
La
versatilità di questa seconda soluzione, che
fa uso delle variabili locali, è infinitamente
superiore.
Array
Gli
array sono tipi di dato "aggregati". Gli
array possono avere qualsiasi dimensione. Un array
a una dimensione, o vettore, è una sequenza
di variabili dello stesso tipo. Ogni variabile che
costituisce l'array prende il nome di elemento. Un
array si dichiara utilizzando la sintassi che segue:
tipo
nome[dimensione];
dove
il tipo è un tipo di dato valido in C++, nome
è il nome dell'array e dimensione indica il
numero di elementi di cui si compone l'array. Ad esempio:
int
array[5];
dichiara
un array di 5 interi. Per accedere all'i-esimo elemento
dell'array è sufficiente scrivere:
array[i]
L'indicizzazione
degli elementi parte da 0, quindi in un array di dimensione
n gli elementi sono i seguenti:
array[0],
array[1], ..., array[n-1]
Essa
è a tutti gli effetti una variabile, quindi
può essere utilizzata allo stesso modo. Ad
esempio:
#include
<iostream>
using namespace std;
void
main()
{
int numeri[10];
for (int i = 0; i < 10; i++)
{
numeri[i] = i;
cout << numeri[i] << " ";
}
}
In
output si ottiene:
0
1 2 3 4 5 6 7 8 9
Il
C++ non effettua nessun controllo sui limiti degli
array, quindi è possibile richiede l'undicesimo
elemento dell'array numeri. Esso rappresenta il contenuto
della locazione immediatamente successiva all'ultimo
elemento dell'array. Nel migliore dei casi si ottiene
un valore insensato. Nel peggiore, si genera un errore
nell'applicazione. È sempre bene controllare
che il proprio programma non acceda a elementi che
non appartengono all'array.
Segue
un classico esempio di utilizzo degli array:
#include
<iostream>
using namespace std;
void
main()
{
int a[10] = { 5, 8, 3, 6, 2, 0, 4, 1, 7, 9 }, min,
i = 0;
for (int k = 0; k < 10; k++)
{
// inizializzato a un valore massimo
min = 100;
// preleva il minimo tra i rimanenti 10-k elementi
for (int j = k; j < 10; j++)
if (a[j] < min) { min = a[j]; i = j; }
// scambia l'elemento in k con quello in i
a[i] = a[k];
a[k] = min;
// stampa il k-esimo elemento ordinato
cout << a[k] << " ";
}
}
L'array
a è inizializzato specificando i valori tra
parentesi graffe. Esso viene ordinato utilizzando
l'algoritmo dell'insertion sort. L'algoritmo cerca
tra tutti gli elementi quello di valore minimo e lo
scambia con il primo, quindi cerca nuovamente il minimo
tra gli elementi rimanenti e lo scambio con il secondo
e così via.
In output si ottiene il vettore ordinato.
È
possibile definire array di qualsiasi dimensione.
La sintassi generale è la seguente:
tipo
nome[dimensione 1][dimensione 2]
[dimensione
n]
Ad
esempio:
void
main()
{
int tavola[10][10];
// riempie la tavola pitagorica
for (int x = 0; x < 10; x++)
for (int y = 0; y < 10; y++) tavola[y][x] = x*y;
}
Nell'array
tavola si ottiene la tavola pitagorica.
Stringhe
L'uso
più comune degli array unidimensionali è
per le stringhe. In C++, infatti, non esiste un tipo
di dato predefinito "stringa". Una stringa
è semplicemente un array di caratteri che termina
con il carattere ASCII 0. Ad esempio:
#include
<iostream>
using namespace std;
void
main()
{
char stringa[80];
cout << "Inserisci una stringa: ";
cin >> stringa;
cout << "Hai inserito la stringa: "
<< stringa << endl;
}
Nell'esempio
la stringa è un vettore di 80 caratteri. L'oggetto
predefinito cin permette di prelevare in input una
stringa e memorizzarla in un array (nell'esempio,
l'array omonimo). In output si ottiene:
Inserisci
una stringa: pippo
Hai inserito la stringa: pippo
Visualizziamo
solo i caratteri pari di una stringa:
#include
<iostream>
#include <string.h>
using namespace std;
void
main()
{
char stringa[80] = "u-n-a- -s-t-r-i-n-g-a";
for (int k = 0; k < strlen(stringa); k += 2)
cout << stringa[k];
}
Come
si può notare, viene incluso il file string.h.
Esso contiene le dichiarazioni delle funzioni C di
manipolazione delle stringhe. Nell'esempio viene utilizzata
la funzione strlen che restituisce la dimensione di
una stringa.
Ad ogni iterazione, la variabile k viene incrementata
di 2 (anziché di 1) in modo tale che si acceda
solo agli elementi pari dell'array di caratteri di
cui l'array è costituito.
In
output si ottiene:
una
stringa
Può
tornare utile copiare o modificare le stringhe agevolmente.
A questo scopo, le funzioni dichiarate nel file string.h
sono di grande utilità. Vediamo subito un esempio:
#include
<iostream>
#include <string.h>
using namespace std;
void
main()
{
char stringa[80];
// inizializza la stringa con "uno,"
strcpy(stringa, "uno,");
// aggiunge "due" alla stringa "uno,"
strcat(stringa, "due");
if (!strcmp(stringa, "uno"))
cout << "la stringa == \"uno\"";
else if (!strncmp(stringa, "uno", 3))
cout << "la stringa inizia con \"uno\"";
}
All'interno
del main si definisce una stringa lunga 80 caratteri.
Mediante la funzione strcpy, si copia la stringa "uno,"
all'interno della stringa. La funzione strcat, quindi,
aggiunge "due" in coda alla stringa (che
ora contiene "uno,due"). La linea successiva
utilizza la funzione strcmp per confrontare la stringa
con "uno". Questa funzione restituisce 0
se le due stringhe sono uguali (il che non è
vero nell'esempio), un numero diverso da 0 altrimenti.
Quindi, poiché il confronto non dà risultato
positivo, si utilizza la funzione strncmp, la quale
effettua il confronto sui primi n caratteri (dove
n è il terzo parametro della funzione, nell'esempio
3). Nell'esempio il confronto va a buon fine, quindi
la funzione restituisce 0 e stampa:
la
stringa inizia con "uno"
Poiché
tutte le stringhe terminano con il carattere ASCII
0, la rappresentazione della stringa in memoria è
la seguente:
posizione
0 1 2 3 4 5 6 7 8 9
79
carattere u n o , d u e \0 ? ?
?
La
tabella rappresenta la zona di memoria occupata dalla
variabile locale stringa. Nella prima posizione (ovvero
stringa[0]) si trova il carattere 'u', nella seconda
(stringa[1]) il carattere 'n' e così via fino
alla posizione 7 in cui si trova il carattere ASCII
0 (che indica la terminazione della stringa). I caratteri
dalla posizione 8 alla posizione 79 non sono definiti
(indicati con un ?).
È bene fare attenzione al fatto che le funzioni
di manipolazione delle stringhe non svolgono alcune
controllo sulle dimensioni delle stesse, quindi il
programmatore deve incaricarsi di effettuare i dovuti
controlli per evitare di incorrere in errori in fase
di esecuzione dovuti all'accesso di zone di memoria
esterne all'applicazione.
In
realtà le funzioni di manipolazione delle stringhe
sono un'eredità del linguaggio C. In effetti,
la libreria standard del C++ mette a disposizione
il tipo di dato string, più sicuro e più
semplice da usare. Sfortunatamente la maggior parte
del codice scritto finora fa ampio uso delle stringhe
così come furono concepite con il linguaggio
C ed è per questo che ne faremo largo uso anche
in seguito.
|