Lezione 1 - I terribili puntatori

Un programma in C++ non ha bisogno di essere a oggetti ma deve contenere la funzione main

Programma 1:Scrivete il programma "Ciao a tutti" in C++ .S

    file Ciao.cc

# include  <iostream>

     int main() {
         cout << "Ciao a Tutti!" << endl;
         return 0;
     }
Per scrivere programmi C++ usate un editore di testi .Copiate il programma e salvatelo in un file di nome Ciao.cc L'istruzione
g++ -o Ciao Ciao.cc
compilera' il programma' creando un file: Ciao Finalmente l'istruzione
Ciao
fara' eseguire il programma che scrivera' la frase Ciao a Tutti.

Cominciamo con un piccolo vocabolario Java/C++:
JavaC++
classclass
proprieta'data members
metodimember functions
thisthis
supernon esiste:usare l'operatore :: di namespace dando il nome della classe madre oppure inherited
. . o ->
extends Superclass: public Superclass
implements Interface:public Interface (C++ permette l'ereditarieta' multipla)

A differenza di Java, non tutte le funzioni devono essere definite in una classe;in C++ abbiamo oltre alle member functions anche le free functions funzioni al di fuori di ogni classe.Anzi,ogni programma C++ deve includere almeno una free function, quella chiamata main che viene richiamata dal sistema operativo quando parte il programma.Questo spiega la particolare struttura del programma "Ciao a tutti" in C++.

Compilare sotto Windows: Le istruzioni riportate in queste lezioni sono valide se lavorate in una finestra terminale sotto Linux. Sotto Windows i programmi sono stati provati col compilatore Borland di C++Builder 5 .Dovete salvare i programmi in files col suffisso .cpp . Da una finestra MsDos posizionatevi nella cartella dove avete salvato i files(ad es. Calcola.cpp e Counter.cpp) e date le istruzioni:
bcc32 Calcola.cpp Counter.cpp
Calcola 
Qualche programma richiede qualche piccola modifica come:
#include <iostream.h>
invece di
#include <iostream>

Borland offre il C++ Builder Compiler 5 scaricabile liberamente.

Sotto Windows potete anche usare Bloodshed DEV-C++ un ambiente per lo sviluppo di programmi nei linguaggi c e c++ in ambiente windows. L'ambiente integra editor, compilatore, linker e debugger.
Il programma e` scaricabile gratuitamente all'indirizzo http://www.bloodshed.net/dev/devcpp.htm. Sul sito sono disponibili materiali di documentazione.




Programma 2:Scrivete un programma che legge 2 numeri e ne fa la somma .S

    file Somma.cc

# include  <iostream>

     int main() {
         float a,b,c;
         cout << "Scrivi 2 numeri" << endl;
         cin >> a >> b;
         c = a+ b;
         cout << a <<" + "<< b << " = " << c  << endl;
         return 0;
     }
A livello del codice all'interno di una funzione, il C++ e' molto simile a Java: Una tra le caratteristiche che rendono C++ cosi' complicato e soggetto a errori e' il fatto che la condizione logica ha come valore un valore numerico e viceversa ogni espressione numerica e' accettata anche come espressione logica. In questo caso, il suo valore viene calcolato e se e' 0 allora da' falso, se e' !=0 da true. Percio' l'istruzione if(x=y)faiqualcosa e' corretta in C++ ma non in Java.Se il programmatore intendeva scrivere if(x==y) ecco un errore che scoprira' difficilmente.
Debugging con DDD:DDD e' un programma (in effetti solo un'interfaccia grafica a gdb) che permette di debuggare i programmi C++. Per usarlo, dovete prima compilare il programma con l'opzione -g. Ad esempio:
g++ -o Somma -g Somma.cc
e poi dare il comando :
ddd Somma
. Si apre una finestra grafica contenente il codice del programma e vari tasti e menu'. La prima cosa da fare e' di creare uno o piu' breakpoint (istruzioni dove il programma si fermera' in attesa). Questo lo si fa cliccando col tasto destro nello spazio vuoto prima dell'istruzione e poi selezionando "set breakpoint". Ora potete cliccare sul tasto Run. Quando il programma si ferma al primo breakpoint potete usare il mouse per vedere il valore delle variabili vicine al breakpoint (basta passare il mouse sopra attendendo qualche istante). Per procedere di un'istruzione alla volta basta cliccare su "Until".

Quando il programma e' in esecuzione, potete in ogni momento cliccare su "Interrupt" , esaminare il valore delle variabili o inserire nuovi breakpoint e poi cliccare su "Cont" per riprendere l'esecuzione.

Da notare che l'esame di programmi compilati con ottimizzazione (opzione -O) puo' dare sorprendenti risultati:ad es:variabili non definite, esecuzione non sequenziale di istruzioni.




Programma 3:Scrivete un programma che legge una serie di numeri e ne calcola la media .S

    file Media.cc

# include  <iostream>

     int main() {

    int i;
    float  x[10];
    double sum;

    sum = 0;
    i = 0;
    while(cin >> x[i]){
        sum += x[i];
        i = i + 1;
    }
  cout << sum/i << endl;
  return 0;
     }   

Commenti: Padroneggiare i puntatori e' una specie di rito di iniziazione per i programmatori C/C++ . Essi vengono usati per gestire stringhe di caratteri,interi,strutture,classi e persino funzioni. Essi sono dietro la potenza e la flessibilita' del linguaggio. Infatti il C fu creato per poter scrivere un sistema operativo (cosa che prima richiedeva l'assembler) con un linguaggio ad alto livello.
Purtroppo i puntatori sono anche i responsabili della complessita' e della maggior parte degli errori dei programmi C/C++.
I puntatori sono strettamente collegati alle funzioni e percio' cominceremo col parlare di queste.


Programma 4:Scrivete un programma che legge una serie di numeri e ne calcola la media ma questa volta la media e' calcolata da una funzione a parte. .S

    file Media.cc

#include <iostream.h>

double media() {
    int i,n;
    cin >> n;
    float*  x = new float[n];
    double sum;

    sum = 0;
    for ( i = 0; i < n; i++) {
        cin >> x[i] ;
        sum += x[i];
    }
    return sum/n;
    delete [] x;
}


int main() {
  cout << media() << endl;
  return 0;
}                                
Commenti:


Programma 5:Come il precedente ma ora la definizione della funzione media e' fuori dal main in un file header .S

    file Media.h

double media();      

    file Calcola.cc

#include <iostream>
#include "Media.h"


int main() {
  cout << media() << endl;
  return 0;
}                   

    file Media.cc

#include <iostream>

double media() {
    int i,n;
    cin >> n;
    float*  x = new float[n];
    double sum;

    sum = 0;
    for ( i = 0; i < n; i++) {
        cin >> x[i] ;
        sum += x[i];
    }
    return sum/n;
    delete [] x;
}          

Infine il tutto va compilato con
g++ -o Calcola Media.cc Calcola.cc 
Commenti:


Programma 6: Scrivi un programma che usa la libreria di funzioni matematiche. S

    file Calcola.cc

#include <iostream>
#include <math.h>

int main() {
  float x;
 while (cin >> x) {
   cout << x << " " << sqrt(x) << endl;
   }
 return 0;
}
       
Per ogni libreria standard bisogna includere il file delle dichiarazioni(header). Quindi l'istruzione include che potrebbe sembrare superficialmente molto simile all'import di Java ha un significato completamente diverso.Il compilatore C++ non fa che copiare il contenuto delle dichiarazioni all'inizio del programma e compilare il tutto. Invece e' solo all'atto del link che dobbiamo indicargli il file contenente la libreria compilata.Un #include "header" indica che il file va cercato a partire dalla cartella dove ci troviamo , #include <header> nella cartella contenente tutti gli include files.In quest'ultimo caso la regola e' di non indicare il ".h" a meno che non ci si riferisca alle vecchie librerie C. Queste vengono talvolta indicate anche con un nome che comincia con c e senza il .h (Ad esempio #include <cstdio> invece di #include <stdio.h>. Quindi se volete usare le funzioni C di trattamento di stringhe scrivete:#include <string.h> oppure #include <cstring>, se invece volete usare le strighe C++ scrivete : #include <string>
Per listare i file delle librerie C dare:
ls /usr/include  (per avere la lista dei file)
more nomefile   (per avere il contenuto di un singolo header file)
Invece i gli header files del C++ si possono trovare in /usr/local/include/g++
using namespace std: Anche se nei programmi che seguono non viene quasi mai usata, questa istruzione la troverete spessissimo nei programmi C++. Che significa e perche si inserisce sempre all'inizio di un programma dopo gli include?L'istruzione using e' legata al fatto che in C++ avete anche dichiarazioni fuori da ogni classe. Questi nomi si possono anch'essi suddividere e qualificare con namespaces.Il namespace std e' quello di tutte le variabili e funzioni delle librerie standard di C++.Ma voi potreste definire un vostro namespace pippo per evitare possibili confusioni e poi usarlo con using namespace pippo.
Oppure usare i nomi completi che si indicano anteponendo il nome del namespece seguito da ::
Cosi' se avete definito una vostra funzione sqrt, avreste std::sqrt e pippo::sqrt. Ovviamente std e' il namespace predefinito ed e' per questo che l'istruzione using manca nei programmi di queste lezioni.




Programma 7: Scrivete una funzione che faccia lo scambio di 2 valori passati come argomenti S

    file Scambia.cc

#include <iostream.h>

void  scambio(int &x, int &y  ) {
    int temp;
    temp = x; x = y; y =temp;
}


int main() {
  int a,b;
  a = 1; b=2;
  cout << a  << b << endl;
  scambio(a,b);
  cout << a  << b << endl;
  return 0;
}           
Commenti:




Programma 8: Il primo programma che usa i puntatori! S

    file Puntatori.cc

#include <iostream.h>

int main() {
  int *p;
  int j;
  p = &j;
  cout << *p  << endl;
  *p = 5;
  cout << *p  << ' ' << j  << endl;
  if(p != 0)
    cout << "Il puntatore p punta a " << *p << " che quindi si  trova all\'indir
izzo "<< p << endl;
  return 0;
}                  
In Java abbiamo 2 tipi di variabili. Le variabili che indicano tipi primitivi si riferiscono al valore degli stessi, per cui:
int i = 1;
     int j = 2;
     j = i; 
ha come risultato che j alla fine contiene il valore 1. Invece le variabili che si riferiscono ad oggetti contengono solo il puntatore all'oggetto,per cui:
 String a = "Torino"; 
 String b = "Milano";
 b = a;
ha come effetto di distruggere l'oggetto "Milano" in quanto dopo l'assegnazione b=a b punta a "Torino".
In C++ le normali dichiarazioni (cioe quelle senza l'asterisco) producono sempre delle variabili che si riferiscono al valore dell'oggetto o del dato primitivo. Invece e' possibile definire esplicitamente una variabile in modo che si comporti come un puntatore di oggetto Java usando la scritta <T>* dove <T> puo' essere un qualsiasi tipo di dati primitivi,aggregati o oggetti.
Quindi int* p indica che p e' un puntatore a un dato intero.

puntatore a un intero


Vediamo ora vari modi di assegnare il valore al puntatore:
p = &varp punta a var. L'operatore & ritorna l'indirizzo di una variabile.
int *p = new int;un intero viene allocato e il suo indirizzo ritornato in p; alla fine ricordarsi di inserire delete p
int *p = new int[10]un vettore di interi viene allocato e il suo indirizzo tornato in p; alla fine ricordarsi di inserire delete p[]
p = NULL oppure p=0 puntatore vuoto
p = 5 p punta all'indirizzo di memoria 5
i = *p permette di ottenere il valore puntato da p . L'asterisco indica in questo caso un operatore detto in gergo di dereferenziazione o indirezione.
*p = 5permette di assegnare il valore 5 all'intero puntato da p.




Programma 9: Riscrivete la funzione che scambia i valori di 2 variabili intere in modo da usare i puntatori S

    file Scambia.cc

#include <iostream.h>

void  scambio(int *x, int *y  ) {
    int temp;
    temp = *x; *x = *y; *y =temp;
}


int main() {
  int a,b;
  a = 1; b=2;
  cout << a  << b << endl;
  scambio(&a,&b);
  cout << a  << b << endl;
  return 0;
}        




Programma 10: Riscrivete il programma che calcola la media in modo da usare i puntatori S

    file Media.cc

#include <iostream.h>

double media() {
    int i,n;
    cin >> n;
    float*  x = new float[n];
    float *y = x;//y punta a x[0]
    double sum;

    sum = 0;
    for ( i = 0; i < n; i++) {
        cin >> x[i] ;
        sum += *y++;
    }
    return sum/n;
    delete [] x;
}


int main() {
  cout << media() << endl;
  return 0;
}                    




Programma 11: Riscrivete il programma precedente in modo da leggere i valori nel main e passare il vettore alla funzione S

    file Media.cc

#include <iostream.h>

double media(float *y, int n) {
    int i;
    double sum;

    sum = 0;
    for ( i = 0; i < n; i++)
        sum += *y++;
    return sum/n;
}

int main() {
    int i,n;
    cin >> n;
    float*  x = new float[n];
    for ( i = 0; i < n; i++)
        cin >> x[i] ;
    cout << media(x,n) << endl;
    delete [] x;
  return 0;
}                     
vettore di float

In C/C++ quando passate il nome di un vettore, il passaggio degli argomenti avviene per riferimento. In effetti il nome di un vettore non e' altro che il puntatore al primo elemento del vettore. Cioe' se x e' un vettore:

Per dimostrare queste caratteristiche dei vettori ecco una leggera variante del programma precedente che funziona allo stesso modo:

    file Media1.cc

#include 

double media(float y[], int n) {
    int i;  
    double sum;

    sum = 0;
    for ( i = 0; i < n; i++) 
        sum += y[i];
    
    return sum/n;
}


int main() {
    int i,n;
    cin >> n;
    float*  x = new float[n];
    for ( i = 0; i < n; i++) 
        cin >> x[i] ;
    cout << media(x,n) <<  endl;
    delete [] x;
  return 0;
}




Programma 12: Scrivete un programma che riversa una stringa di caratteri. S

    file Riversa.cc

#include < iostream.h>
#include < string.h>

void riversa(char *s, int n) {
  
 char *primo = &s[0];
 char *ultimo =&s[n-1] ;
 while(primo < ultimo){
  char temp = *primo;
  *primo++ = *ultimo;
  *ultimo-- = temp;
  }
}


int main() {
  int n;
  char str[] = "ROMA";
  cout << str ;
  n = strlen(str);
  riversa(str,n);
  cout << str << endl;
  return 0;
}
Commenti: Da notare che ora il C++ ha un oggetto string che rende obsolete le stringhe C viste in questo esempio.

Per concludere una importante osservazione su puntatori ,vettori e stringhe:
C++ non fa nessun controllo sulla dimensione del vettore. Se ,magari usando i puntatori, avete un valore sbagliato di un indice, C++ continuera' ad eseguire tranquillamente anche se sta sporcando aree contigue in memoria.

In definitiva C++ come Java ha 2 tipi di variabili:variabili che si comportano come contenitori e variabili che si comportano come puntatori.La differenza e' che in C++ la cosa e' possibile sia per i tipi primitivi che per le classi. Il primo tipo di variabile viene allocato da una zona di memoria chiamata stack e la gestione della memoria e' fatta automaticamente. Per il secondo tipo di variabili invece (se si usa l'operatore new), l'allocazione viene fatta dal cosiddetto heap ed allora deve essere lo stesso programmatore a liberare la memoria (con delete) quando i dati o gli oggetti non servono piu'. Uno degli errori piu' comuni in C++ e' quello del memory leak cioe' scordarsi di cancellare dati non piu' necessari con conseguente ingombro sempre maggiore di memoria.
I dati e gli oggetti allocati con new(quelli dell'heap) non sono automaticamente disallocati quando non esiste piu' nessun puntatore ad essi!Sta a voi cancellare questi dati e questi oggetti con delete! E questo va fatto alla fine della funzione dove il dato e' allocato o nel distruttore dell'oggetto che contiene questi dati diventati garbage(immondizia).


INDIETRO a Imparate C++ in 3 giorni
INDIETRO a Da Java al C++
Maintained by : Giuseppe.Zito@cern.ch
Ultimo aggiornamento: