«UMBERTO I - ELISA VILLA» ANNO SCOLASTICO 2012 / 2013 SCUOLA PARITARIA.
alberto davide emma elisa luigi · "alberto" "davide" emma"elisa" "luigi" "marta" " " i i=0 i=1 i=2...
Transcript of alberto davide emma elisa luigi · "alberto" "davide" emma"elisa" "luigi" "marta" " " i i=0 i=1 i=2...
Vettori paralleli
Gli array paralleli sono 2 o più array che hanno lo stesso indice e contengono elementi in relazione
fra loro anche di tipologia differente. Per esempio:
int size=6;
string nome[size];
int eta[size];
i i=0 i=1 i=2 i=3 i=4 i=5
string
nome[size]
"alberto" "davide" "elisa" "luigi" "marta" "emma"
i i=0 i=1 i=2 i=3 i=4 i=5
int
eta[size]
17 16 15 18 16 17
Il primo array contiene stringhe di nomi di persone.
Il secondo array contiene interi che rappresentano le rispettive età delle persone del primo
array.
nome[0]= "alberto";
eta[0]=17;
nome[1]= " davide";
eta[1]=16;
nome[2]= " elisa";
eta[3]=15;
nome[3]= " luigi";
eta[3]=18;
nome[4]= " marta";
eta[4]=16;
nome[5]= "emma";
eta[5]=17;
Quando si devono risolvere problemi in cui le informazioni da elaborare non sono di tipo omogeneo,
la struttura dati di tipo vettore risulta inadeguata. Se, per esempio, per un elenco di alunni si devono
memorizzare sia il nome sia la media, risulta evidente che non esiste alcun tipo di dato elementare
in grado di memorizzare contemporaneamente un stringa (nome) e un valore numerico reale
(età). In questi tipi di problemi si utilizzano vettori paralleli. Ciascun vettore contiene informazioni
fra di loro omogenee e il legame è stabilito dall’indice comune, che rappresenta la posizione degli
elementi corrispondenti nei due o più array.
E’ importante ricordare che i due vettori devono avere la stessa dimensione!
Nella soluzione di problemi che richiedono l’uso di vettori paralleli può risultare necessario ordinare
i dati per produrre elenchi ordinati o per fornire programmi più efficienti. Anche in questo caso si
possono utilizzare gli algoritmi di ordinamento (per selezione o bubble sort) tenendo però presente
che si procederà all’ordinamento di un solo vettore per volta e che, per mantenere la relazione tra
i vettori paralleli, a ogni scambio di due componenti sul primo vettore si dovrà provvedere a
scambiare anche gli elementi corrispondenti degli altri vettori.
Si vogliono gestire le prenotazioni delle visite in ospedale da parte degli utenti. Per ogni
prenotazione siamo interessati al nominativo dell’utente (nome e cognome), alla visita (tipo di visita
prenotata), alla data (giorno, mese, anno) e all’ora di prenotazione.
Esempio
Esercizio 1
1. Creare la seguente tabella in C++ utilizzando gli array paralleli:
ID NOMINATIVO VISITA DATA ORA
0
1
….
9
int DIM=10; //10 righe
string nominativo[DIM];
2. Creare lo script che consenta all’operatore di inserire i dati relativi alle DIM prenotazioni
nella tabella.
3. Visualizzare i nominativi che hanno prenotato una certa visita (visita è il parametro di ricerca
digitabile dall’operatore).
4. Visualizzare il numero di visite prenotate da un certo nominativo.
5. Visualizzare il numero di visite prenotate in un determinato giorno.
6. Visualizzare le visite (e i nominativi) prenotate in un determinato giorno.
7. Come è possibile modificare il programma per consentire all’operatore di aggiungere una
nuova prenotazione.
Esercizio 2
I dati relativi alle vendite di una giornata nei 5 reparti di un supermercato sono memorizzati in tre
vettori paralleli:
articoli,
reparto,
valore.
Scrivere un programma che visualizzi i dati relativi alle vendite di ogni reparto e alla sua percentuale
rispetto al totale.
Esercizio 3
I dati relativi al campionato di calcio sono memorizzati in tre vettori paralleli:
squadre contenente il nome della squadra;
serie contenente "a" o "b";
punti contenente il punteggio ottenuto nel campionato
Scrivere un programma con le seguenti richieste:
a)stampare un tabulato contenente l'elenco delle squadre di serie a
nella forma:
elenco squadre serie a
squadra punti
......... .......
b)dato in input il nome della squadra, stampare la serie e i punti realizzati. Se la squadra non esiste
stampare una segnalazione di errore.
c)costruire e stampare un altro vettore classifb contenente solo le squadre di serie b.
d)cercare e stampare il nome della squadra campione di serie a.
include<stdio.h>
#include<string.h> //libreria usata per l'istruzione STRCMP
#define MAX 10 //numero massimo di squadre inserite (puoi mettere qualsiasi altro numero)
main(){
char *squadra[MAX];
char *serie[MAX];
int punti[MAX],i;
// esercizio a
for(i=0;i<MAX;i++){
if(serie[i]=="A")
printf("%s : %d\n",squadra[i],punti[i]);
}
// esercizio b
char squadraS;
int j=0;
printf("inserisci la squadra");
scanf("%s",&squadraS);
for(i=0;i<MAX;i++){
if(strcmp(squadraS,squadra[i])==0){
j++;
printf("%d : %c",punti[i],serie[i]);}
if(j==0)
printf("la squadra selezionata non e' nell'elenco");
}
//esercizio c
int cont=1,k=MAX,sup,temp,h=0;
char temp1;
int punti1[MAX];
char *squadra1[MAX];
for(i=0;i<MAX;i++){
if(serie[i]=="B"){
squadra1[h]=squadra[i];
punti1[h]=punti[i];
j++;
}
}
//ordinamento della classifica
while(cont=1){
sup=k;
cont=0;
for(j=0;j<MAX;j++){
if(punti1[j]>punti1[j+1]){
temp=punti1[j];
temp1=squadra1[j];
punti1[j]=punti1[j+1];
squadra1[j]=squadra1[j+1];
punti1[j+1]=temp;
squadra1[j+1]=temp1;
cont=1;
k=j;
}
}
}
//stampa classifica
for(i=0;i<MAX;i++){
printf("%s",squadra1[i]);
}
//esercizio d
è come l'esercizio b, al posto di "B" metti "A" e invece di stampare tutto il vettore stampi solo la
prima posizione.
}
Esercizio 4
Array
Gli array sono sequenze di variabili dello stesso tipo che vengono situate
consecutivamente nella memoria ed alle quali è possibile accedere usando uno stesso
nome (identificatore) a cui viene aggiunto un indice.
Questo significa, ad esempio, che possiamo memorizzare 5 valori di tipo int senza
bisogno di dichiarare cinque diverse variabili con cinque diversi identificatori. Per fare
questo è sufficiente dichiarare un array di cinque elementi dello stesso tipo int con un
solo identificatore.
Ad esempio un array di nome billy contenente 5 valori di tipo int si può rappresentare
nel seguente modo:
in cui ogni cella rappresenta un elemento dell'array. Gli elementi sono numerati
da 0 a 4 in quanto, in un array, l'indice del primo elemento è sempre 0 (e
non 1).
Come tutte le variabili anche gli array devono essere dichiarati prima di poterli usare. Un
esempio di dichiarazione di un array in C++ è:
tipo nome [dimensione];
dove tipo è il tipo degli elementi ( int, float ...) detto anche tipo base
dell'array , nome è un identificatore e dimensione, che deve essere racchiuso tra
parentesi quadre [], è la dimensione, ossia il numero di elementi,
dell'array.
La dichiarazione dell'array billy è:
int billy [5];
ATTENZIONE: Il campo dimensione deve essere un valore costante in quanto gli array sono blocchi di
memoria di dimensione prefissata ed il compilatore deve conoscere esattamente quanta memoria serve
per l'array prima che il programma venga eseguito.
Nella libreria standard vector (che si può includere con #include <vector>) è definito
un nuovo tipo, di nome vector, che si comporta come un array intelligente (con più
operazioni e meno restrizioni). Li vedremo nella Sezione 3.6 Vector e String.
Inizializzazione degli array.
Come per le variabili semplici, anche per gli array è possibile specificare un valore
iniziale. Ad esempio, con la dichiarazione:
int billy [5] = { 16, 2, 77, 40, 12071 };
l'array viene inizializzato come segue:
Il numero di valori usati per l'inizializzazione (quelli posti tra le parentesi grafe {}) deve
essere esattamente uguale alla dimensione dell'array. In C++ è possibile anche usare la
notazione:
int billy [] = { 16, 2, 77, 40, 12071 };
ed in questo caso viene assunto implicitamente come dimensione dell'array il numero
di valori della lista di inizializzazione.
Naturalmente, qualora venga dichiarato un array senza inizializzazione il valore iniziale
dei suoi elementi risulta indeterminato (i bit della memoria riservata per l'array
conservano i valori lasciati dai programmi precedenti che la hanno usata).
Accesso ai valori di un Array.
In ogni punto del programma in cui un array risulta visibile possiamo accedere
individualmente ad uno degli elementi dell'array per leggerlo o modificarlo
esattamente come esso fosse una normale variabile. Il formato è il seguente:
name[index]
Proseguendo l'esempio dell'array billy di 5 elementi di tipo int, i nomi mediante i
quali possiamo accedere a ciascun elemento dell'array sono quelli indicati sopra le
singole celle nella figura seguente:
Ad esempio, se vogliamo memorizzare il valore 75 nel
terzo elemento di billy possiamo usare l'assegnazione:
billy[2] = 75;
oppure, per copiare il valore del terzo elemento nella variabile a possiamo usare:
a = billy[2];
Dunque, a tutti gli effetti, billy[2] si comporta come una variabile di tipo int.
Notiamo che il terzo elemento di billy è billy[2], infatti billy[0] è il primo, billy[1] è
il secondo e di conseguenza billy[2] è il terzo. Per la stessa ragione l'ultimo elemento
è billy[4] . Se scriviamo billy[5], noi accediamo al sesto elemento dell'array che non
è previsto incorrendo molto probabilmente in un errore di esecuzione.
A questo punto occorre notare i due diversi usi delle parentesi quadre [ ] con gli array:
nella dichiarazione di un array esse sono usate per indicare la dimensione dell'array
mentre in tutti gli altri contesti esse vengono usate per specificare un indice per
individuare un particolare elemento dell'array. Ad esempio:
int billy[5]; // dichiarazione di un nuovo array di 5 elementi
billy[2] = 75; // accesso ad un elemento particolare dell'array: quello
di indice 2.
Altre possibili operazioni con gli array sono:
billy[0] = a; billy[a] = 75; b = billy [a+2]; billy[billy[a]] = billy[2] + 5;
// esempio con gli array
#include <iostream.h>
int billy [] = {16, 2, 77, 40,
12071};
int n, risultato=0;
int main ()
{
for ( n=0 ; n<5 ; n++ )
{
risultato += billy[n];
}
cout << risultato;
return 0;
}
12206
Array multidimensionali Un array multidimensionale si può pensare come un array di array di array di .... Ad
esempio, un array bidimensionale si può pensare come una tabella bidimensionale i cui
elementi appartengono tutti allo stesso tipo.
jimmy è un array bidimensionale di 3 per 5 valori di tipo int. Esso si può pensare come
un array di 5 elementi, ciscuno dei quali è a sua volta un array di 3 elementi (le
colonne). Esso si dichiara come segue:
int jimmy [3][5];
e, per riferirsi all'elemento nella seconda riga e nella quarta colonna si usa la
notazione:
jimmy[1][3]
(ricordiamo che gli indici degli array iniziano sempre con 0).
Gli array multidimensionali possono avere più di due indici (due dimensioni). Possono
avere quanti indici vogliamo ma è molto raro che servano più di 3 dimensioni. La
memoria necessaria per un array multidimensionale può essere eccessiva. Ad
esempio:
char secolo [100][365][24][60][60];
richiede memoria per un valore char per ogni secondo contenuto in un secolo, più di 3
miliardi di char ! Il che richiede più di 3 gigabytes di memoria RAM.
Gli elementi di un array multidimensionale sono memorizzati nella RAM uno di seguito
all'altro come per gli array semplici. Potremmo ottenere lo stesso risultato usando un
array semplice di dimensione il prodotto delle dimensioni dell'array multidimensionale:
int jimmy [3][5]; è equivalente a int jimmy [15];
con l'unica differenza che il compilatore gestisce per noi la suddivisione in righe e
colonne. Il seguente esempio mostra l'equivalenza delle due soluzioni:
// array multidimensionale
#include <iostream.h>
#define COLONNE 5
#define RIGHE 3
int jimmy [RIGHE][COLONNE];
int n,m;
int main ()
{
for (n=0;n<RIGHE;n++)
for (m=0;m<COLONNE;m++)
{
jimmy[n][m]=(n+1)*(m+1);
}
return 0;
}
// array pseudo-multidimensionale
#include <iostream.h>
#define COLONNE 5
#define RIGHE 3
int jimmy [RIGHE * COLONNE];
int n,m;
int main ()
{
for (n=0;n<RIGHE;n++)
for (m=0;m<COLONNE;m++)
{
jimmy[n * COLONNE +
m]=(n+1)*(m+1);
}
return 0;
}
entrambi i programmi assegnano i seguenti valori al blocco di memoria riservato
per jimmy:
Abbiamo usato la definizione di costanti (#define) per facilitare future modifiche al
programma nel caso, ad esempio, che decidessimo di allargare l'array aggiungendo una
riga. Basta in questo caso cambiare la riga:
#define RIGHE 3
con
#define RIGHE 4
e non serve nessun'altra modifica del
programma.
Array come parametri.
Potremmo voler passare un array come parametro ad una funzione. In C++ non è
possibile passare come parametro ad una funzione il valore di un array (ossia l'insieme
dei valori di tutti i suoi elementi). Possiamo però passare come parametro ad una
funzione l'indirizzo dell'array, il che ai fini pratici funziona altrettanto bene ed è una
operazione molto più veloce.
Per indicare che un argomento di una funzione rappresenta un parametro di tipo array
basta scrivere il tipo degli elementi dell'array (il tipo base dell'array) seguito da una
coppia di parentesi quadre []. Ad esempio la funzione::
void procedura (int arg[])
aspetta un argomento arg di tipo "Array di int ". Per passare alla funzione l'array:
int mioarray [40];
è sufficiente scrivere una chiamata della funzione del tipo:
procedura (mioarray);
Ecco un esempio
completo:
// array come parametri
#include <iostream.h>
void stampaarray (int arg[], int
lunghezza) {
int n;
for (n=0; n<lunghezza; n++)
cout << arg[n] << " ";
cout << "\n";
}
int main ()
{
int primoarray[] = {5, 10, 15};
int secondoarray[] = {2, 4, 6, 8,
10};
5 10 15 2 4 6 8 10
stampaarray (primoarray,3);
stampaarray (secondoarray,5);
return 0;
}
Come si vede il primo argomento (int arg[]) ammette qualsiasi array con tipo base int,
indipendentemente dalla sua dimensione. Ed è per tale ragione che abbiamo messo anche
un secondo argomento: per dire alla funzione quale sia la dimensione dell'array passato
come primo argomento. La dimensione dell'array è necessaria perché il ciclo for possa
stampare il giusto numero di elementi.
In una dichiarazione di funzione si possono anche mettere array multidimensionali come
argomenti. La forma dell'argomento per un array tri-dimensionale è:
tipo_base [][d2][d3]
in cui devono essere specificate tutte le dimensioni ad esclusione della prima. Tale
argomento ammette un qualsiasi array tridimensionale avente seconda e terza
dimensione d2 e d3 prefissate e prima dimensione qualsiasi. Ad esempio:
void procedura (int mioarray[][3][4])
La ragione per cui d2 e d3 devono essere fissate è che il compilatore, per determinare la
posizione in memoria dell'elemento mioarray[i][j][k] usa la formula i*3*4+j*4+k .
Il passaggio di array (semplici o multidimensionali) come parametri di funzioni è spesso
sorgente di errori per i programmatori inesperti. Per una comprensione più precisa di
come funzionano gli array occorre conoscere i puntatori (che vedremo al capitolo 3.3,
Puntatori )
Array
Cos'è un array ?
Un array é un insieme di variabili che occupano locazioni
consecutive in memoria e sono caratterizzate dall'appartenere tutte
allo stesso tipo, detto tipo dell'array (può anche essere
un tipo astratto).
Ogni variabile di tale insieme é detta elemento dell'array ed é
identificata dalla sua posizione d'ordine nell'array (indice).
L'intero array é identificato da un nome (che va specificato
secondo le regole generali di specifica degliidentificatori).
Il numero di elementi di un array (detto dimensione dell'array )
é predefinito e invariabile. In C++ (come in C) l'indice può
assumere valori compresi fra zero e il numero di elementi meno 1.
Definizione e inizializzazione di
un array
Per definire un array bisogna specificare prima il tipo e poi il
nome dell'array, seguito dalla sua dimensione fra parentesi
quadre (la dimensione deve essere espressa da una costante).
Es. int valori[30];
In fase di definizione un array può essere anche inizializzato. I
valori iniziali dei suoi elementi devono essere specificati fra
parentesi graffe e separati l'un l'altro da una virgola; inoltre
la dimensione dell'array, essendo determinata automaticamente,
può essere omessa (non però le parentesi quadre, che costituiscono
l'operatore di dichiarazione dell'array).
Es. int valori[] = {32, 53, 28, 85, 21};
nel caso dell'esempio la dimensione 5 é automaticamente
calcolata.
L'operatore [ ]
L'operatore binario [ ] richiede come left-operand il nome di
un array e come secondo operando (racchiuso fra le due
parentesi quadre) una qualunque espressione con
risultato intero (interpretato come indice dell'array).
Il significato dell'operatore [ ] é duplice:
usato per restituire un l-value, é
un operatore di inserimento di dati nell'array.
Es. valori[3] = 45;
(il numero 45 viene assegnato alla variabile identificata
dall'indice 3 dell'array valori)
usato per restituire un r-value, é
un operatore di estrazione di dati dall'array.
Es. a = valori[4] ;
(il contenuto della variabile identificata
dall'indice 4 dell'array valori viene assegnato alla
variabile a)
Array multidimensionali
In C++ (come in C) sono possibili array con qualsivoglia numero
di dimensioni; tali array vanno definiti come nel seguente
esempio (array tridimensionale): float tabella[3][4][2];
NOTA : la formulazione appare un po' "strana", ma chiarisce il
fatto che un array multidimensionale è da intendersi come
un array di array. Nell'esempio: tabella è
un array di 3 elementi, ciascuno dei quali è
un array di 4elementi, ciascuno dei quali è
un array di 2 elementi di tipo float.
A differenza dal FORTRAN, in C++ (come
in C) gli array multidimensionali sono memorizzati con
gli indici meno significativi a destra ("per riga", nel caso
di array bidimensionali).
Per esempio, dato l'array A[2][3], i suoi elementi sono
memorizzati nel seguente ordine:
A[0][0] , A[0][1] , A[0][2] , A[1][0] , A[1][1] , A[1][2]
Per inizializzare un array multidimensionale, bisogna innestare
tanti gruppi di parentesi graffe quante sono le singole
porzioni monodimensionali dell'array, ed elencare
gli elementi nello stesso ordine in cui saranno memorizzati.
Esempio, nel caso bidimensionale: int dati[3][2] = { {8, -
5} , {4, 0} , {-2, 6 } };
L'operatore sizeof e gli array
L'operatore sizeof, se l'operando é il nome di un array,
restituisce il numero di bytes complessivi dell'array, che é dato
dal numero degli elementi moltiplicato per la lunghezza in byte di
ciascun elemento (la quale ovviamente dipende
dal tipo dell'array).
[p16]
Array multidimensionali:
dichiarazione
Un array multidimensionale è un array i cui elementi sono a
loro volta array.
Il caso più semplice è quello in cui gli elementi sono array
monodimensionali. In questo caso si ottengono delle strutture
chiamate matrici, di dimensione 2. Le matrici sono l'unico caso
di array multidimensionale che analizziamo.
La dichiarazione di un array multidimensionale è del tutto
analoga a quella di un array monodimensionale, con la differenza
che bisogna riportare tante coppie di parentesi quadre quante sono
le dimensioni dell'array.
Un esempio di dichiarazione di array bidimensionale
int[][] matrix = new int[4][5];
//equivalentemente:
int[][] matrix;
matrix = new int[4][5];
dichiara una matrice di interi di dimensioni 4x5 (quattro righe e
cinque colonne).
Array
multidimensionali:
accesso
Si può accedere ad un singolo elemento di un array
bidimensionale usando doppi indici (per righe e colonne). Ad
esempio matrix[3][1] seleziona il secondo elemento della quarta
riga.
Usando un solo indice si seleziona una riga della matrice. Ad
esempio, matrix[3] seleziona la quarta riga di matrix ed è perciò un
array di interi di lunghezza matrix[3].length (cioè 5).
Organizzazione in
memoria
Un array multidimensionale viene memorizzato come un array di
array.
Quando si crea un array multidimensionale con new, si può lasciare
non specificata una dimensione (insieme alle dimensioni che
seguono): in questo caso gli elementi corrispondenti alle
dimensioni non specificate sono riferimenti vuoti (null).
int[][] a = new int[3][];
int[][] b = new int[][3]; // NON CONSENTITO!
String[][][] bigArray = new String[2][][];
Il primo esempio dichiara una matrice a di 3 elementi, ciascuno
dei quali è un riferimento ad un array monodimensionale di
lunghezza non specificata. Sarà quindi necessario inizializzare
ogni elemento usando l'operatore new.
// Per esempio possiamo
// usare la stessa dimensione
// per tutti gli elementi:
a[0] = new int[4];
a[1] = new int[4];
a[2] = new int[4];
// Oppure possiamo usare
// dimensioni diverse
// per ogni elemento:
a[0] = new int[3];
a[1] = new int[5];
a[2] = new int[7];
Array multidimensionali:
inizializzazione
Analogamente agli array monodimensionali, anche gli array
multidimensionali possono essere inizializzati in fase di
dichiarazione.
public class TavolaPitagorica {
/* Stampa il numero delle righe, la lunghezza
della prima riga e il quarto elemento della terza
riga di una tavola pitagorica di dimensioni 3x4.
*/
public static void main (String args []){
int tavola [][] = {
{ 1*1, 1*2, 1*3, 1*4 },
{ 2*1, 2*2, 2*3, 2*4 },
{ 3*1, 3*2, 3*3, 3*4 }
};
System.out.println (tavola.length);
System.out.println (tavola[0].length);
System.out.println (tavola[2][3]);
}
}
Spesso è conveniente usare dei cicli annidati per inizializzare /
accedere gli elementi di un array multidimensionale. Come
esempio vediamo la versione "parametrica" del programma
precedente:
public class TavolaPitagoricaFor {
/* Stampa il numero delle righe, la lunghezza
della prima riga e il quarto elemento della terza
riga di una tavola pitagorica di dimensioni 3x4.
*/
public static void main (String args []){
final int ROW = 3;
final int COL = 4;
int[][] tavola = new int[ROW][COL];
for (int i=0 ; i<tavola.length ; i++)
for (int j=0 ; j<tavola[i].length ; j++)
tavola[i][j] = (i+1)*(j+1);
System.out.println (tavola.length);
System.out.println (tavola[0].length);
System.out.println (tavola[2][3]);
}
}
Operare su matrici: un
esempio
L'uso di cicli annidati consente di scrivere in maniera naturale gli
algoritmi che operano su matrici (o più in generale, che operano
su array multidimensionali). Ad esempio, vediamo come scrivere
un programma che calcola la somma di tutti gli elementi di una
matrice e stampa il risultato:
public class SommaMatrice {
// Somma gli elementi della matrice di interi A.
// Si noti che funziona qualunque sia la dimensione della
// matrice, e anche se le righe hanno dimensione diversa
public static void main (String [] args) {
int [][] a = {
{1, 2, 3, 4, 5},
{4, 8, 3, 2, 7},
{10, 7, 3, 6, 8},
{9, 2, 8, 3, 1}
};
int somma=0;
for (int i=0; i< a.length; i++)
for (int j=0; j<a[i].length; j++)
somma = somma + a[i][j];
System.out.println (somma);
}
}
Per convenienza, riportiamo sotto la matrice sulla quale opera
l'algoritmo dell'esempio.
Array statici
Prima di poter usare un vettore in un programma è necessario dichiararlo (esattamente come
per normali variabili). Nella dichiarazione di un vettore occorre specificarne
la dimensione (ovvero il numero di elementi). Tale dimensione è rappresentata sempre da un numero intero e costante:
int numeri[40];
double valori[100]; char stringa[80];
I valori 40, 100 e 80 rappresentano rispettivamente le dimensioni dei tre vettori numeri, valori e stringa.
La dimensione dev'essere prefissata al momento della dichiarazione del vettore e non può
essere modificata durante l'esecuzione del programma. Un array di questo tipo si dice statico.
Per un array statico in C++ non è lecito fare una cosa di questo genere:
int dim;
cout<<"Di quanti elementi hai bisogno? ";
cin>>dim; double x[dim]; //questa istruzione non è corretta: è un errore!!!
x[0]=10;
x[1]=12; ...
Lo spezzone di programma qui sopra è sbagliato in quanto dim è una variabile e non è possibile usare una variabile per specificare la dimensione di un vettore.
Si noti, per inciso, che alcuni compilatori (fra i quali anche quello di Dev-C++) potrebbero
anche accettare una scrittura del genere, benché si tratti di istruzioni non contemplate dallo
standard del C++. Non è comunque una buona pratica di programmazione utilizzare codice non standard!
Array dinamici
Un array dinamico è un vettore la cui dimensione può essere stabilita run-time, cioè in fase
di esecuzione del programma. Riprendendo il nostro esempio precedente, il modo corretto (e aderente agli standard) per realizzare un vettore dinamico in C++ è il seguente:
double *x; //questa istruzione dichiara x come puntatore a double
cout<<"Di quanti elementi hai bisogno? "; cin>>dim;
x = new double[dim];
x[0]=10;
x[1]=12;
...
Senza entrare nei dettagli del significato delle singole istruzioni, in pratica la dichiarazione del vettore si effettua in due fasi:
1. prima si dichiara un puntatore al tipo desiderato (int, double) con la seguente sintassi:
tipo *nome_vettore; (esempio 'double *numeri')
2. poi si dimensiona il vettore associato al puntatore in questo modo:
nome_vettore = new tipo[dimensioni]; (esempio 'numeri = new double[var]')
Overflow di vettori
La dimensione di un vettore è il numero di elementi che quel vettore contiene e non può mai
essere superata.
Questo significa che, dichiarando per esempio un vettore (statico o dinamico, non fa
differenza) con dimensione 100, gli elementi disponibili vanno da 0 a 99 e basta: non è
possibile usare elementi con indice superiore.
Se si supera il massimo numero di elementi dichiarato per un certo vettore si provoca un
errore di overflow (termine inglese che si può tradurre in italiano con straripamento). In
pratica si va a usare un'area di memoria esterna al vettore, cioè una zona di memoria che non appartiene al vettore.
L'errore di overflow di un vettore è particolarmente insidioso in quanto non viene diagnosticato
dal compilatore C++ e può causare i più diversi malfunzionamenti al momento dell'esecuzione del programma.
Per esempio lo spezzone di programma seguente contiene un errore di overflow. Tuttavia non
è possibile prevedere a priori come si comporterà il programma in esecuzione e in quale modo
si manifesterà l'errore (il programma potrebbe bloccarsi o i risultati potrebbero essere
sbagliati):
int i,num[10];
for (i=0;i<20;i++) num[i]=0;
L'unico modo per evitare gli errori di overflow consiste nel controllare sempre che l'indice di un
vettore non superi le dimensioni assegnate per quel certo vettore (per esempio mediante la condizione di ripetizione del ciclo o inserendo un if di controllo all'interno del ciclo stesso).
http://www.programmiamo.altervista.org/C/array/array5.html
Gli array in C++
Gli array descritti finora sono quelli "in stile C". Nei programmi
in C++ ad alto livello sono scarsamente utilizzati. Al loro posto si
preferisce usare alcune classi della Libreria Standard (come
vedremo) che offrono flessibilità molto maggiori (per esempio
la dimensione è modificabile dinamicamente e inoltre,
negli array multidimensionali, si possono definire singole
porzioni monodimensionali con dimensioni diverse).
Vettor i (array monodimens ional i )
In genere i dati sono le informazioni rappresentate in forma trattabile con il computer.
In molti problemi si ha la necessità di aggregare molti dati di tipo semplice, per facilitarne
la rappresentazione e rendere più veloce il loro ritrovamento.
I dati sono cioè organizzati in un insieme che prende il nome di struttura dati.
IL VETTORE È UN INSIEME DI ELEMENTI OMOGENEI TRA LORO . CON UNA VARIABILE
POSSIAMO INDICARE SOLO UN DATO, CON IL VETTORE POSSIAMO INDICARE TANTI
DATI DELLO STESSO TIPO CON UN SOLO NOME COLLETTIVO DI
VARIABILE :L'IDENTIFICATORE DEL VETTORE. GLI ELEMENTI SI DISTINGUONO UNO
DALL'ALTRO ATTRAVERSO L 'INDICE CHE VIENE ASSEGNATO NEL VETTORE , E CHE
VIENE POSTO ACCANTO ALL'IDENTIFICATORE DEL VETTORE.
Il vettore è quindi un insieme omogeneo di dati: è un esempio di struttura di dati. Il vettore
si ottiene in C++ aggregando variabili dello stesso tipo.
Un vettore si definisce con la seguente sintassi:
tipo nomeVettore[dimensione];
Alla normale dichiarazione di variabile si aggiunge semplicemente tra parentesi quadre il
numero di elementi (dimensione) che compongono il vettore. Per esempio la seguente
dichiarazione crea un vettore di dieci coefficienti di tipo intero:
int T[10];
Le componenti di un vettore possono essere non solo numeriche, ma di uno qualsiasi dei
tipi standard del linguaggio C (int,char, float).
Se una variabile è definita di tipo vettore, deve essere sempre usata accompagnando
l'identificatore della variabile con un indice. L'indice è solitamente una variabile di tipo
intero.
Un importante considerazione sui vettori riguarda il passaggio dei parametri alle funzioni.
Quando un vettore viene passato come parametro ad una funzione, in realtà viene passata
la locazione (cioè l'indirizzo) della prima componente del vettore. All'interno della
funzione, il parametro diventa una variabile come le altre il nome del vettore è a tutti gli
effetti una variabile contenente un indirizzo cioè un puntatore. Di conseguenza quando
occorre effettuare il passaggio per referenza di un vettore alla funzione, basta indicare il
nome del vettore. Si deve osservare che il passaggio per referenza è il solo modo di
passare un vettore come parametro.
Un VETTORE, in generale, è una SEQUENZA FINITA DI ELEMENTI OMOGENEI FRA
LORO (tutti dello stesso tipo) e CONTIGUI (giacenti in memoria uno accanto all'altro). Di
solito un vettore viene rappresentato come una successione di celle:
Una MATRICE è un vettore a più dimensioni. Nel caso di due dimensioni, la matrice viene
rappresentata con una tabella.
U t i l i t à d e l v e t t o r e
Talvolta una sequenza di valori può essere gestita senza necessariamente memorizzarli
tutti, ma nella maggior parte dei casi è necessario conservarli per operare su di loro anche
più volte, o in tempi differenti.
Il vettore rende disponibile una serie di celle, nelle quali possono trovare posto i valori da
memorizzare, quindi il vettore è utile perché può conservare tutti i valori di una sequenza.
È molto utile anche il fatto che per l'intera sequenza di valori ci sia un'unica entità a cui far
riferimento. Proviamo ad esempio a mettere in ordine crescente 4 valori: servono 4
variabili e una rete di controlli non banale.
Immaginiamo ora di dover mettere in ordine crescente 100 valori: avremmo bisogno di
100 variabili, oltre a un'enorme rete di controlli. Con il vettore, invece, è sufficiente
definire una variabile vettore, con una variabile indice (che indica di volta in volta quale
cella intendiamo utilizzare) e un controllo opportunamente ripetuto in modo ciclico.
G l i e l e m e n t i f o n d a m e n t a l i p e r g e s t i r e u n v e t t o r e :
d i m e n s i o n e , n u m e r o d i e l e m e n t i u t i l i , i n d i c e
Innanzitutto bisogna dichiarare quanti elementi al massimo potranno trovare posto nel
vettore, e di quale tipo.
Questa operazione viene definita DIMENSIONAMENTO DEL VETTORE.
Di solito il numero massimo di elementi emerge da una serie di considerazioni, e
dall'ipotesi di future modifiche al programma.
Quando il testo del problema non specifica la dimensione massima del vettore, l'alunno
può definirla a piacere, purché secondo logica.
Il numero massimo di elementi dev'essere espresso attraverso una costante intera (non è
variabile a piacere). Di solito, si definisce in cima al programma una costante dal nome
autoesplicativo ("MaxNumeri", ad esempio) che costituirà un punto di riferimento
all'interno dell'intero programma, ma in modo particolare laddove verrà dichiarato il
vettore.
Spesso non è necessario gestire tutti gli elementi del vettore: di solito le celle a partire dal
fondo non vengono utilizzate, perciò bisogna definire il numero di elementi utili, ossia
quanti elementi saranno effettivamente utilizzati.
Per esempio: dato un vettore di 100 elementi, quelli utili potranno essere i primi N
(ovviamente 0<=100). Il numero di elementi utili è variabile, e normalmente viene definito
tramite acquisizione (più raramente con un'inizializzazione).
Dato che un vettore di solito contiene più valori, si pone il problema di indicare a quale di
essi ci vogliamo riferire.
È sempre necessario accompagnare il vettore con una variabile numerica intera, detta
indice, che di volta in volta indica la posizione dell'elemento a cui ci si riferisce. Ad
esempio: Vettore[ Indice ], o Vett [ i]. Attenzione: contando in modo abituale, si
tende a partire da 1, per cui si può pensare che l'indice associato al primo elemento sia 1.
In realtà, IN LINGUAGGIO C GLI INDICI DI VETTORI E MATRICI PARTONO DA 0. Per
evitare ambiguità, il primo elemento si definisce 0-esimo ("zeresimo"), il secondo 1-esimo
("unesimo"), ecc.
D i c h i a r a z i o n e d i v e t t o r i e m a t r i c i
La sintassi generale è:
tipo "nome del vettore" [ "numero massimo di elementi" ] ;
ESEMPIO 1a - dichiarazione di un vettore con dimensionamento tramite costante
espressamente dichiarata
#define MaxValori 500
float Vett[MaxValori]; /* Vettore di 500 valori float (si
potrà ridimensionare, cambiando la costante) */
int i;/* i è l'indice del vettore */
/* e poi, nel codice, ad esempio azzeriamo tutte le celle
del vettore*/
i = 0; /* ideve iniziare da 0 */
while (i < MaxValori){/*la costante può essere ancora
utile*/
Vett[i]=0;/* Inserisce il valore 0 nella cella i-esima */
i=i+1;/* Aggiorna l'indice i, che dovrà variare fra 0 e
MaxValori - 1 */
}/* Il ciclo terminerà quando l'indice ivale MaxValori,
ossia 500 */
ESEMPIO 1b - dichiarazione del medesimo vettore, ma utilizzando una costante
immediata
float Vett[500]; /* L'indice dovrà variare fra 0 a 499 */
ESEMPIO 2 - dichiara due vettori con dimensioni differenti, più tre variabili singole, tutto
sulla stessa riga
int V1[10], V2[50], A, B, C = 0;
ESEMPIO 3 - dichiara un vettore di 8 interi e contestualmente lo inizializza
int Fib[8] = { 0, 1, 1, 2, 3, 5, 8, 13 };
ESEMPIO 4 - dichiara un vettore di caratteri che, data l'inizializzazione, viene
dimensionato automaticamente a 4
char Oper[ ] = {'+', ' -', '*' , '/'}; /* Dimensionamento
automatico */
ESEMPIO 5 - dichiara un vettore di 10 interi, attribuendo valori solo ai primi 4 elementi
(sovradimensionamento)
int Vett[10] = { 10, -1, 20, -3 }; /* Sovradimensionamento
*/
ESEMPIO 6 - dichiara una matrice di interi, con 10 righe (numerate da 0 a 9) e 8 colonne
(numerate da 0 a 7)
int Mat[10][8];
/* La matrice è un vettore a due dimensioni, e serve per
memorizzare tabelle */
ESEMPIO 7 - dichiara una matrice 2x3 di interi, e contestualmente la inizializza (riga per
riga)
int Mat[2][3] = { {1, 2, 15}, {22, 41, 17} };
ESEMPIO 8 - dichiara una matrice cubica 4x4x4
int Mat[4][4][4];
S c a n s i o n e d i u n v e t t o r e
"Scansione" deriva dal verbo SCANDIRE, ossia visitare ognuna delle celle del vettore,
indicandole tramite un'opportuna variazione dell'indice.
Il vettore può essere visitato in molti modi: in modo ascendente (a partire dall'inizio),
discendente (a partire dall'ultimo elemento utile), a celle alternate, ecc., anche in due modi
contemporaneamente, secondo il procedimento che dobbiamo codificare.
SCANSIONE ASCENDENTE. L'indice parte da 0 e viene portato avanti di una cella per
volta. Ammettiamo di scandire un vettore "Vett" di n=6 elementi interi:
Aggiornando l'indice con l'istruzione i=i+1 o i++ , si punta alla cella successiva:
Ripetendo il procedimento, potremo portare l'indice fino all'ultimo elemento utile, o anche
fino in fondo al vettore.
La scansione ascendente è la più usata e avviene di norma usando il ciclo for.
Ad es. per inizializzare tutte le celle di un vettore T di n componenti a 0 si usa:
for(i=0;i < n;i++) T[i]=0;
SCANSIONE DISCENDENTE. L'indice parte dall'ultimo elemento utile e viene fatto
arretrare di una cella per volta.
Dato che N = 6, l'indice parte da 5:
Aggiornando l'indice con l'istruzione i=i - 1 o i- -, si punta alla cella precedente:
Ripetendo il procedimento, potremo portare l'indice fino al primo elemento.
C a s i d i e r r o r e
L'errore che più comunemente si compie consiste nel far variare l'indice fino a oltre
l'ultima cella del vettore: in questo caso si ha il cosiddetto sconfinamento, che, accedendo
al vettore, può provocare conseguenze imprevedibili.
In questo schema è illustrato un esempio di sconfinamento: l'indice i dovrebbe andare da 0
a 9 ma, trovandosi a 10, va a indicare qualcosa che si trova al di là del vettore (di solito
un'altra variabile), per cui Vett[ i ] non è un valore utile, anzi modificando Vett [ i ] (con
i=10), si va a modificare una variabile che non è Vett.
Un sottocaso di sconfinamento consiste nel far variare l'indice al di là dell'ultimo elemento
utile, anche se internamente al vettore.
Ammettiamo che gli elementi significativi siano solo 6: l'indice posizionato oltre la 5-
esima cella punta ad un valore indefinito. Anche in questo caso, un accesso a questo
elemento avrebbe conseguenze imprevedibili:
Conclusione: in tutti i casi, quando l'indice si trova fuori posizione (come normalmente
succede alla fine di un ciclo di scansione), dobbiamo evitare di accedere al vettore prima di
aver riposizionato l'indice.
Vi possono essere anche ERRORI DI DICHIARAZIONE del vettore.
ERRORE 1 - dichiara un vettore senza che il compilatore possa determinare in alcun modo
la dimensione
int Vett[];/* Per il dimensionamento automatico bisogna
indicare un valore */
ERRORE 2 - dichiara un vettore di 1 solo elemento, che serve a poco, dato che equivale ad
una variabile
int Vett[0];
ERRORE 3 - dichiara un vettore presumibilmente troppo ampio
long int Vett[5000000000];
ERRORE 4 - tenta di dimensionare il vettore con un valore proveniente da una variabile
anziché da una costante
int Max = 100;
long int Vett[Max];
E s e m p i o d i u t i l i z z o d e l v e t t o r e
Problema: inizializzare da programma un vettore di 10 componenti e stamparlo a video:
const int n=10;//costante n visibile globalmente
// da tutti i punti del programma anche da altre funzioni
// diverse dal main()
main(){
//dichiaro e inizializzo il vettore
int T[n]{3,7,11,15,7,2,4,6,12,8};
int i; //contatore
//stampa
for(i=0;i < n;i++)cout << " " << T[i];
}//fine main
Problema: caricare da tastiera un vettore di 5 componenti e stamparlo a video:
const int n=5;
main(){
int T[n]; //dichiaro il vettore
int i; //contatore
//caricamento
for(i=0;i < n;i++){
cout << "ins:";
cin >> T[i];
}//fine for
//stampa
for(i=0;i < n;i++)cout << " " << T[i];
}//fine main
Problema: caricare in modo random vettore di 5 componenti con numeri variabili da 0 a 9.
const int n=5;
main(){
int T[n]; //dichiaro il vettore
int i; //contatore
srand(time(0));//inizializzo il generatore di numeri random
for(i=0;i < n;i++)T[i]=rand%10;
}//fine main
V e t t o r i : e s e r c i z i r i s o l t i
E s e r c i z i o n o . 1 Dato un vettore di 8 numeri interi caricato da programma stampare in uscita la
somma e la media dei valori in esso contenuti.E s e r c i z i o n o . 2 Dato un vettore di 8 numeri interi caricato da programma individuare e stampare a
video il massimo e il minimo con la loro posizione nel vettore.E s e r c i z i o n o . 3 Caricare da tastiera in vettore di 10 numeri interi e stamparlo a video : disegnare lo
schema a blocchi.E s e r c i z i o n o . 4 Caricare in modo random un vettore di 10 numeri interi e stamparlo a video e scrivere l'algoritmo che individui quanti numeri pari e quanti numeri dispari ci sono
nel vettore.E s e r c i z i o n o . 5 Sviluppare un'algoritmo che permetta di riempire un vettore di 5 elementi interi con 5 numeri compresi fra 0 e 9 (inclusi) in modo casuale (random) e senza che vi siano ripetizioni. (questo algoritmo è molto importante i fini della simulazione di fenomeni come il gioco del lotto o l'estrazione di una carta da un mazzo: ossia estrazioni che non prevedono la possibilità della reimmissione dell'elemento scelto da un dato
insieme).E s e r c i z i o n o . 6 Eseguire l'ordinamento di un vettore di interi tramite l'algoritmo di
scambio.E s e r c i z i o n o . 7 Realizzare un programma per individuare il valore modale contenuto in un vettore di
interi.E s e r c i z i o n o . 8 Dato un vettore di 10 interi da 0 a 9, inserire in un secondo vettore le frequenze
assolute dei numeri che appaiono nel primo vettore.E s e r c i z i o n o . 9 Realizza un programma per convertire un numero binario nel corrispondente valore decimale. Matrici in C++ Nei precedenti articoli abbiamo descritto le matrici, evidenziando le loro caratteristiche. Adesso scopriamo come si gestiscono nel linguaggio C++. Nel linguaggio C++ una matrice viene dichiarata secondo la seguente sintassi: tipo nome_matrice [ const1 ] [const2 ]; Se per esempio volessimo dichiarare una matrice di nome MAT formata da un massimo di 20 righe e 30 colonne in grado di contenere numeri interi, scriveremmo: int MAT [ 20 ] [ 30 ]; oppure const int N1=20; const int N2=30; int MAT [ N1 ] [ N2 ]; Come per i vettori, la numerazione delle righe e delle colonne parte da zero. Per caricare una matrice è necessario utilizzare due cicli annidati: si può procedere per righe o per colonne. Nel C++ si possono utilizzare varie strutture iterative ( while,for ), l’importante è far scorrere gli elementi della matrice. E’ fondamentale ricordare che le operazioni di caricamento e visualizzazione degli elementi della matrice sono procedure nell’ambito della programmazione complessa, poiché non restituiscono nessun valore di ritorno bensì sono porzioni di codice autonome eseguite mediante chiamata per nome. Inoltre il C++ richiede di conoscere almeno il numero di colonne, altrimenti non la riconoscerà come matrice ( non basta inserire le doppie parentesi quadre ). Il passaggio dei parametri avviene per referenza, anche se non è necessario utilizzare il simbolo & in quanto le variabili strutturare in C++ sono viste come puntatori per cui vengono passate automaticamente per referenza. void CaricaMatrice ( int MAT [] [ NC ] ) { int i, j; for ( i=0; i<NR; i++ ) { for ( j=0; j<NC; j++ ) { cout<<“Inserisci elemento matrice”<<endl; cin>>MAT [ i ][ j ]; } } }
La ricerca Binaria in C++ Abbiamo descritto precedentemente la ricerca binaria( o dicotomica), ovvero un altro metodo di ricerca in grado di ridurre il numero medio dei tentativi purché il vettore su cui ricercare l’elemento sia ordinato.
L’algoritmo della ricerca dicotomica sotto forma di sottoprogramma è il seguente:
Proviamo a scrivere un programma in C++ in grado di risolvere il seguente problema: In un vettore sono caricate le sigle di alcune province italiane. Verifica se la sigla inserita in input
è presente nell’elenco. Utilizza l’algoritmo della ricerca dicotomica.
Innanzitutto dobbiamo scrivere i prototipi, i quali dovranno: 1) caricare il vettore; 2) ordinare il vettore ( condizione necessaria per utilizzare la ricerca dicotomica ); 3) ricercare l’elemento tramite la ricerca dicotomica. Per scrivere i tre prototipi dobbiamo capire quali sono le procedure e quali le funzioni, proviamo a rifletterci: il caricamento di un vettore avviene tramite un ciclo che scorre i campi, quindi possiamo
facilmente intuire che si tratta di una procedura, poiché non restituisce in uscita nessun valore di ritorno.
ordinamento del vettore avviene tramite due cicli, uno più esterno e uno più interno. Anche in questo caso utilizziamo una procedura, poiché si tratta di una porzione di codice autonomo che viene eseguito mediante chiamata per nome
in questo problema ci viene chiesto di verificare se una provincia è presente o meno in un elenco. Sappiamo che per verificare la presenza o meno di un certo dato si utilizza una variabile booleana, ovvero una variabile in grado di assumere soltanto due valori: “0” e “1“. In base a quanto detto sopra capiamo che si tratta di una funzione che restituirà al programma principale il valore assunto dalla variabile booleana.
Oltre ai prototipi bisogna non dimenticare di dichiarare una costante intera che riporti la dimensione massima che il vettore può assumere. const int N=5; void CaricaVettore ( string v[]); void OrdinaVettore ( string v[]); bool Dicotomica ( string v[]); Il codice per le due procedure l’abbiamo già analizzato nei precedenti articoli, proprio per questo possiamo andare a copiarlo e riportarlo in questo problema. Questo è uno dei
vantaggi della programmazione modulare! Analizziamo dunque la funzione “Dicotomica”. Bisogna dichiarare le variabili localiche si utilizzeranno, dopodiché seguiamo letteralmente i passi rappresentati dall’algoritmo a blocchi, ovvero: 1) inizializziamo le variabili; 2) inseriamo da input la sigla della provincia da ricercare; 3) facciamo partire un ciclo con controlla in testa che continua fin quando le due clausole sono vere; 4) diamo all’indice “i” un valore intero dettato dall’operazione (inizio+fine)/2; 5) codifichiamo i due costrutti condizionali a due uscite; 6) facciamo ritornare al programma il valore di ritorno tramite l’istruzione return, fondamentale quando si utilizzano le funzioni. Ricordiamo che il valore di ritorno deve essere dello stesso tipo del prototipo della funzione. bool Dicotomica(string v[]) { int i, inizio, fine; string provincia; bool trov=false; i=0, inizio=0, fine=N-1; cout<<“Inserisci nome provincia da ricercare: “; cin>>provincia; while ((trov==false) and (inizio<=fine)) { i=int((inizio+fine)/2); if (provincia==v[i]) trov=true; else { if (provincia<v[i]) fine=i-1; else inizio=i+1; } } return trov; }
ottoprogrammi in C++ Abbiamo analizzato nei precedenti articoli la complessità dei problemi e il metodo con cui affrontarli, facendo uso di funzioni e procedure, due tipologie di sottoprogrammi.
Nel linguaggio C++ esiste solo il concetto di funzione, cioè un sottoprogramma che restituisce un valore. Per creare delle procedure bisogna gestire in modo particolare le
funzioni, facendo in modo che non facciano ritornare nessun valore. Innanzitutto c’è bisogno di scrivere i prototipi, poiché secondo lo standard ANSI ( American National Standard Institute ) tutte le funzioni devono avere un corrispondente prototipo. La struttura di un prototipo è questa: tipo nomefunzione ( tipo var1, tipo var2 ); Dopo aver dichiarato i prototipi si scrive il programma principale, ovvero la funzione ( main ), con i vari richiami ai sottoprogrammi. E’ importante sapere che la funzione main è quella che verrà eseguita per prima. Dopo si procede all’implementazione dei prototipi di funzione. Una funzione ha la seguente struttura: tipo nomefunzione () { dichiarazioni di costanti; dichiarazioni di tipi; dichirazioni di variabili; istruzioni; } Tipo indica il il tipo ( int, float, bool, …. ) di risultato che la funzione restituisce. Tra le istruzioni deve essere presente: return valore; dove valore può essere una variabile o una costante ed è il valore che la funzione restituisce al programma chiamante; l’istruzione return blocca l’esecuzione del sottoprogramma e viene eseguita l’istruzione del programma chiamantesuccessiva all’istruzione di richiamo del sottoprogramma. E’ possibile creare delle procedure gestendo in particolar modo le funzioni, ovvero facendo in modo che non restituiscano nessun valore. Ciò è possibile inserendo la scritta “void” al posto del tipo della funzione. Nell’esempio seguente la funzione “esercizio” non restituisce nessun valore. void esercizio () { ……. ……. } Ricapitolando la struttura di un programma in C++ prevede: la dichiarazione dei prototipi. la scrittura del programma principale con i vari richiami ai sottoprogrammi. l’implementazione dei prototipi di funzione.
rdinamento in C++ Nel precedente articolo abbiamo illustrato i vari metodi per ordinare un vettore, illustrandovi i vari algoritmi e i passi da compiere. Adesso vediamo come codificarli in C++.
L’ordinamento per selezione
Ipotizziamo di dover ordinare un vettore contenente i nomi di un gruppo di soci. Il metodo è sempre quello, cambiano unicamente i nomi delle variabili come possiamo vedere:
Come possiamo vedere ci sono due indici, l’indice ” i ” serve per la gestione del ciclo esterno mentre l’indice ” j ” serve per la gestione del ciclo interno. La variabile di comodo è rappresentata da ” tmp ” di tipo string. Per la codifica completa cliccare qui: Sort.
Ordinamento per scambi successivi
In questo caso la variabile ” j ” non è utilizzata per identificare un campo del vettore, bensì è utilizzata come una variabile di lavoro poiché prende la dimensione del vettore e, ad ogni ciclo in cui la condizione ” i<=j ” non si verifica, riduce il suo valore di 1. Ciò potrebbe farlo anche la variabile N, ma se in futuro volessimo visualizzare tali valori non sarebbe più possibile poiché la sua dimensione risulterebbe modificata e probabilmente il programma andrà in loop ( ciclo infinito ) o genererà errori. Inoltre se la dimensione viene dichiarata come una costante, il suo valore non può in nessun modo essere alterato.
E’ importantissimo non confondere le variabili ” i ” e ” j “, inoltre bisogna prestare particolare attenzione ai due cicli i quali devono avere quei valori di inizio e fineonde evitare la perdita dei dati o un cattivo funzionamento del programma. Per la codifica completa cliccare qui: Bubblesort.
I vettori: alcuni tipici esercizi! I vettori sono strutture dati molto utilizzate nel campo della programmazione. Affrontiamo insieme alcuni esercizi. Calcolo della media Prima di eseguire l’elaborazione è necessario avere caricato i dati nel vettore,
successivamente, tramite un ciclo, gli elementi del vettore sono sommati in una variabile accumulatore; al termine del ciclo, per ottenere la media sarà sufficiente dividere la somma ottenuta per il numero degli elementi presenti nel vettore. E’ importante ricordare di usare sempre una variabile reale per il calcolo della media! Esempio in C++: Media
Individuare un massimo o un minimo Per ricercare il massimo o il minimo in una serie di valori contenuti in un vettore, si pone il primo elemento del vettore in una variabile d’appoggio. In seguito si scandisce il resto del vettore attraverso un ciclo confrontando ogni elemento con il contenuto della variabile d’appoggio. Se il valore dell’elemento considerato di volta in volta risulta maggiore ( o minore ) di tale contenuto, alla variabile d’appoggio viene assegnato il nuovo valore, altrimenti si procede nel ciclo. Al termine del ciclo si visualizza il valore della variabile d’appoggio, che coincide a questo punto con il massimo ( o minimo ) cercato. Esempio in C++: Max&Min
La ricerca di un elemento Quando non è nota la posizione ( l’indice ) dell’elemento a cui accedere bisogna cercarlo effettuando una ricerca parziale all’interno della struttura. Si scorre il vettore con
un ciclo che termina o perché si è trovato l’elemento cercato o perché sono finiti gli elementi. In questo modo si può anche sapere quante volte appare un dato elemento all’interno del vettore, basterà aggiungere una variabile contatore dopo la condizione. Siccome la ricerca può avere diverse clausole, preferiamo non mostrarvi un flowchartclassico perché non potrebbe andar bene per i vostri scopi. Se ci fossero dei problemi, contattateci tramite la nostra pagina “Contatti” o sulle nostre pagine social. Esempio in C++: Ricerca
I vettori in C++ In C++ un vettore viene definito nel seguente modo: tipo nomevettore[dimensione]; La dimensione indica il numero massimo di elementi presenti nel vettore, tipo indica il tipo elementare comune a tutte le componenti. Gli elementi sono numerati a partire da 0 fino a dimensione -1. Per esempio se vogliamo dichiarare un vettore contenente 5 numeri interi scriveremo: int vet[5]; ma gli elementi di vet sono numerati da 0 a 4. Per individuare un dato elemento del vettore si usa la seguente scrittura: vet[indice]; Indice può essere una variabile intera, una costante o un dato immediato. Per esempio “vet[2]” indica il terzo elemento di vet ( poiché sono numerati partendo da 0 ). In fase di definizione un array può essere anche inizializzato. I valori dei suoi elementi devono essere specificati tra parentesi graffe e separati l’un l’altro da un virgola, inoltre la dimensione dell’array, essendo determinata automaticamente, può essere omessa ( non però le parentesi quadre, che costituiscono l’operatore di dichirazione dell’array): int valor[] = { 31, 55, 60, 29, 3}; In questo caso la dimensione 5 è automaticamente calcolata. Per caricare un vettore bisogna inserire un singolo elemento per volta; questo si può realizzare attraverso l’uso di un ciclo. Ad esempio se si vuole caricare un vettore di 7 elementi il codice da scrivere sarà il seguente: int i, N=7, v[N]; for (i=0;i<N;i++) { cout<<”Inserisci il numero nella posizione “<<i<<” del vettore”<<endl; cin>>v[i]; } Ovviamente tutto questo può anche essere realizzato tramite l’utilizzo della struttura while, l’importante è usare una struttura iterativa. Alleghiamo qui due semplici programmi: –Dichiara e carica il vettore tramite la struttura for: VettoreFor ( aprire il file con estensione .cpp in un qualsiasi ambiente C++ e prestare particolare attenzione ai commenti). –Dichiara e carica il vettore tramite la struttura while :VettoreWhile Quando in fase di progettazione non è noto il numero di elementi da caricare, si assegna al vettore una dimensione piuttosto grande. In fase di esecuzione viene richiesto in input il numero di elementi che effettivamente si vogliono caricare. In questo caso è necessario controllare che il numero inserito sia minore o uguale della dimensione assegnata al vettore. In C++ il controllo viene fatto tramite la struttura iterativa postcondizionale,ovvero: do {
cout<<”Inserisci dimensione effettiva del vettore”<<endl; cin>>n; } while ((n<0)||(n>N)); La visualizzazione dei valori inseriti all’interno del vettore richiede un ciclo che punti singolarmente i campi e li mostri a video. Di seguito riportiamo il flowchart:
Un esempio di quanto spiegato lo ritroviamo in questo programma contenente dichiarazione, controllo sulla dimensione, caricamento e visualizzazione: Vettore
Le strutture iterative più complesse Nei precedenti articoli abbiamo parlato delle strutture iterative e abbiamo mostrato anche qualche semplice esercizio in linguaggio C++, ma l’utilizzo delle strutture iterative è veramente ampio e quindi abbiamo deciso di fornirvi ulteriori dettagli e precisazioni su questo argomento. Cicli con contatore e sommatorie Alcuni esercizi richiedono l’uso di variabili accumulatori e/o variabili contatori. Un accumulatore è una variabile al cui valore si somma una nuova quantità in ogni iterazione del ciclo. Al termine del ciclo l’accumulatore contiene la somma algebrica delle quantità considerate. Un contatore invece è incrementato di uno a ogni iterazione, in modo che a termine del ciclo indica quante interazioni sono state effettuate. A volte può essere necessario incrementare il contatore solo se si verifica una certa condizione: in tal caso al termine del ciclo il contatore indica quante volte si è verificata quella condizione. È fondamentale ricordarsi sempre di inizializzare a zero tanto gli accumulatori quanto i contatori prima dell’inizio del ciclo. Esempi: 1. Calcola quanto spendi in un negozio acquistando un certo numero di prodotti. Conosci il
numero di prodotto che hai acquistato e, per ogni prodotto, inserisci il costo. Download del programma in C++: Programma1( aprile il file con estensione .cppin un qualsiasi ambiente di sviluppo C++ e prestare particolare attenzione ai commenti ).
2. Inserisci in input una serie di numeri. La serie termina quando la somma dei numeri inseriti è maggiore o uguale a 100 oppure quando è stato raggiunto il numero di tentativi
stabiliti. In questo caso vi è un ciclo con uscita per condizione avverata. Download del programma in C++: Programma2
All’interno di un programma è possibile trovare più strutture iterative messe in modo sequenziale, inoltre all’interno di una struttura iterativa è possibile inserire istruzioni semplici come input/output, assegnazioni, ma anche istruzioni più complesse come quelle condizionali. Esempio: 1. Accetta in input N numeri ( N > 0 ). Al termine dell’inserimento segnala quanti numeri
sono positivi e quanti negativi. Visualizza inoltre la somma dei numeri positivi e la somma dei numeri negativi. Download del programma in C++: Programma3
In molti problemi è richiesto il calcolo della media e la ricerca del massimo in un insieme di elementi. Esempi: 1. Calcola la media dei voti di uno studente senza conoscere il numero delle
materie. Download del programma in C++: Programma4 2. Dati in input 10 numeri positivi, trova il valore massimo e visualizzalo. Download del
programma in C++.Programma5
Esempi di programmi sequenziali, condizionali e iterativi In questo articolo analizzeremo diversi programmi aventi diverse strutture algoritmiche.
Iniziamo con la struttura più semplice, quella sequenziale. Vi alleghiamo un semplice programma che prende in input due numeri dall’utente e assegna a 4 variabili le corrispondenti operazioni aritmetiche ( vi preghiamo di destare particolare attenzione ai commenti, con i quali spieghiamo le numerose peculiarità del programma) : Prog.Sequenziale1 ( per vedere il codice, basta aprire il file con estensione .cpp in un qualsiasi ambiente di sviluppo per C++ ). Ora analizziamo una struttura leggermente più complessa, quella condizionale. Vi alleghiamo un programma simile al precedente ma che si differenzia per la scelta, da parte dell’utente, dell’operazione da eseguire. Apparirà quindi un menù e in base al comando dell’utente il programma eseguirà l’operazione corrispondente ( anche qui particolare attenzione ai commenti ) : Prog.Condizionale1. Una struttura molto utilizzata nel campo della programmazione è sicuramente quella iterativa. Vi alleghiamo anche qui un semplice programma che, tramite l’utilizzo del ciclo for in due dei suoi numerosi aspetti, permette la visualizzazione di un particolare insieme di numeri, in questo caso da 1 fino a 10 e viceversa : Prog.Iterativo1.
truttura di un programma in C++ Nel precedente articolo abbiamo visto un semplice programma codificato in C++. Adesso analizziamo la struttura “standard” dei programmi in questo linguaggio. Innanzitutto bisogna includere una libreria, ovvero una collezione di classe e funzioni scritte nel linguaggio cuore e appartenenti allo standard ISO C++. Per includere una libreria basta far precedere al nome della libreria il comando “#include”, ad esempio per includere la libreria per la gestione dell’input/output il comando da scrivere sarà: #include <iostrem> L’utilizzo di più librerie può portare ad ambiguità se esistono nomi uguali in più librerie C++. E’ possibile quindi usare i namespace per limitare la visibilità dei nomi. Per ora è sufficiente utilizzare il namespace standard: using namespace std; Dopo aver dichiarato la libreria “iostream” è possibile utilizzare le istruzioni cin e coutche permettono rispettivamente di acquisire dati da tastiere e di visualizzare risultati su video. Ad esempio: cin>>nome_variabile indica che il valore inserito da tastiera verrà assegnato alla variabile nome_variabile. cout<<nome_variabile indica che il valore contenuto nella variabile nome_variabile verrà visualizzato a video. Gli operatori << e >> indicano il flusso dei dati “da” e “verso” il calcolatore. In C++ le variabili sono dichiarate facendo precedere il nome della variabile dal tipo. Istruzioni di assegnazione Le istruzioni di assegnazione sono necessarie per attribuire un valore a una variabile e sono contrassegnate dal simbolo di uguaglianza (=). Ad esempio: var1=8; In questo caso la var1 assume il valore immediato 8; è necessario, affinché non vi siano errori, che ciò che compare sulla destra del simbolo di un’istruzione di assegnazione sia dello stesso tipo della variabile che compare sulla sinistra e che i valori delle variabili sulla destra siano ben definiti. Una caratteristica tipica del C++ è la possibilità di utilizzare gli operatori aritmetici nella forma ridotta, come riportato dalla tabella:
Nome operatore Istruzione in forma estesa Istruzione in forma ridotta
Incremento unitario i=i+1; i++;
Decremento unitario i=i-1; i–;
Somma k=k+p k+=p;
Sottrazione k=k-p k-=p;
Prodotto k=k*p k*=p
Divisione k=k/p K/=p
Istruzioni condizionali L’istruzione condizionale può avere forme diverse:
if ( condizione ) istruzione; oppure if (condizione ) istruzione1; else istruzione2; dove condizione indica un’espressione logica, mentre istruzione indica una qualunque istruzione oppure un blocco di istruzioni ( cioè un insieme di istruzioni racchiuso tra i simboli { e } ). L’istruzione scritta dopo la condizione viene eseguita solo se la condizione è vera, mentre l’istruzione scritta dopo la clausola else viene eseguita solo se la condizione è falsa. Un altro utile costrutto C++ per realizzare strutture condizionali con più possibilità di scelta è quello della condizione multipla, realizzata tramite l’istruzione switch case: Switch (varl) { case val1: istr1; break; case val2: istr2; break; default: istr0; break; } Il valore assunto dalla variabile varl determina quale ramo della struttura deve essere eseguito. Se la variabile varl non assume nessuno dei valori specificati viene eseguita l’istruzione ( o le istruzioni ) istr0 specificata dopo la clausola default. È importante ricordare che il simbolo di uguaglianza viene rappresentato con == da non confondere con il simbolo = che indica assegnazione. Inoltre la disuguaglianza viene rappresentata con !=. Strutture iterative In C++ la struttura iterativa post-condizionale viene realizzata con la seguente sintassi: do { istruzione1; istruzione2; istruzioneN; } while (condizione); dove istruzione1, istruzione2 ed istruzioneN vengono eseguite mentre la condizione è vera. La struttura iterativa pre-condizionale viene realizzata con la seguente sintassi: while (condizione) istruzione; dove istruzione rappresenta una o anche più istruzioni dentro un blocco, eseguite se la condizione è vera. La ripetizione con contatore viene realizzata con l’istruzione for: for (i=0;i<N;i++) istruzione; L’istruzione o il blocco di istruzioni viene eseguito finché i è minore di N, ed a ogni ripetizione viene incrementato il valore di i di 1 (operatore ++).
Nel C++ il ciclo for può essere utilizzato in modo più generale rispetto ad altri linguaggi di programmazione facendo uso dell’operatore virgola.
Codificare un problema in C++ Abbiamo precedentemente illustrato vari linguaggi di programmazione. Abbiamo anche dato la definizione di algoritmo e vi abbiamo mostrato come si illustrano e come si progettano soluzioni a dei problemi. Ora manca l’ultimo tassello: codificare il problema in un linguaggio di programmazione. Per far ciò, bisogna conoscere le regole grammaticali e semantiche dei linguaggi di programmazione. Iniziamo con un problema semplice, illustrandovi la procedura per risolverlo in linguaggio C++.
Il programma chiede di inserire all’utente due numeri, che verranno memorizzati in due variabili: A e B. Poi li confronta ( rombo ) e se A>B allora il programma fa apparire in output la scritta “Il numero A è maggiore di B”, altrimenti, ovvero se B>A, fa la differenza tra B ed A. Inoltre, nella codifica che vi alleghiamo, facciamo anche apparire il risultato della differenza: Programma 1 Il file che contiene il codice ha estensione .cpp. Inoltre ogni istruzione è spiegata all’interno del programma attraverso i commenti, ovvero le scritte in grigio contrassegnate dal //.
In linguaggio C++
Il C++ è un linguaggio orientato agli oggetti. Nasce come estensione del linguaggio C del quale conserva tutte le caratteristiche: la sinteticità, la versatilità, la presenza di un vasto insieme di operatori e di strutture dati avanzate. Il C++ ( il nome indica le maggiori potenzialità di tale linguaggio rispetto al C ) oltre alle potenzialità del C, permette la definizione di dati astratti e l’implementazione dei concetti fondamentali della
programmazione orientata agli oggetti come l’ereditarietà e il polimorfismo. Essendo il C++ uno dei linguaggi più diffusi, esistono diversi ambienti di sviluppo disponibili sul mercato. Noi consigliamo uno fra il Dev C++ e il Code::Blocks poiché sono disponibili gratuitamente su Internet. Download Code::Blocks: http://www.codeblocks.org/downloads Download Dev C++: http://www.bloodshed.net/download.html Un file sorgente C++ ( con estensione .cpp) può comunque essere compilato in qualsiasi ambiente C++.
http://codeblog.altervista.org/category/storia-dei-linguaggi-di-programmazione/