• Forum
  • Programmazione Android
  • CORSI ONLINE
  • Web & Mobile Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

13. I Puntatori – Array, matrici e aritmetica dei puntatori – parte 2

By IgnazioC | on 6 maggio 2011 | 9 Comments
Corso completo di C

corso-completo-c-i-puntatori-parte-2-vettori-matrici-array-00 Ciao a tutti, in questa nuova lezione del nostro corso completo di programmazione in C approfondiremo lo studio dei puntatori iniziato nella precedente lezione.
Oggi esamineremo l’utilizzo dei puntatori come parametro per le funzioni e come base per la creazione di array e matrici.

 

Puntatori come parametri

Quando nella lezione 11 abbiamo parlato delle funzioni abbiamo specificato che il C utilizza il passaggio dati per valore, questo significa che quando il programma esegue una funzione e passa una variabile come parametro, questa viene copiata ed utilizzata all’interno della funzione. Quando la funzione arriva al termine la copia del dato viene semplicemente cancellata. Questo è il motivo per il quale effettuare una modifica al valore di una variabile all’interno di una funzione non cambia il valore che questa variabile ha all’esterno della funzione, perché tutte le modifiche vengono effettuate su una copia del dato.

Ma non avevamo ancora fatto i conti con i puntatori!

Infatti l’utilizzo dei puntatori permette di scavalcare in modo molto elegante questa limitazione.

Cosa succede se una funzione accetta come parametro un puntatore? prendiamo ad esempio la stessa funzione “incrementa” vista nella lezione 11 e modifichiamola per accettare come parametro un puntatore e non più un intero. Il codice interno della funzione deve essere modificato per tenere conto di questa differenza, otteniamo quindi questo codice:

void incrementa(int *value) {

        *value = *value + 10;

}

Quando questa funzione verrà richiamata dal programma principale, il parametro verrà copiato come sempre, ma questa volta viene effettuata una copia di un puntatore quindi il valore puntato è sempre lo stesso, non ci sono copie, quindi tutte le modifiche apportate al valore puntato all’interno della funzione saranno visibili anche fuori.

Se invece volessimo modificare il valore del puntatore (e non della cella puntata dal puntatore) ci troveremmo con il medesimo problema di prima, ovvero che la modifica effettuata verrebbe persa al termine della funzione, perché la funzione lavora su una copia del puntatore.

Possiamo quindi eseguire l’intero programma:

#include 

void incrementa(int *value) {

        *value = *value + 10;

}

int main (int argc, const char * argv[])

{

        int k = 5;

        int *p = &k;

        printf("Valore prima di eseguire incrementa %d\n",k);

        incrementa(p);

        printf("Valore dopo aver eseguito incrementa %d\n",k);

          return 0;

}

Questa volta notiamo che la fuzione ha effettivamente modificato il valore della variabile K.

Tutto chiaro? facciamo ancora una piccola modifica, come notate io ho scritto:

int *p = &k;

incrementa(p);

Possiamo sfruttare una sorta di “proprietà transitiva” e richiamare la funzione direttamente tramite:

incrementa(&k);

Questo perché il parametro che la funzione richiede è un int* che di fatto è un indirizzo, &k è anch’esso un indirizzo quindi il passaggio è lecito.

Concludo questa prima parte della lezione con delle domande:

  1. Modificare questa funzione utilizzando i puntatori piuttosto che gli interi:
    int max(int x, int y) {
    
            if (x <= y) {
    
       return x;
    
    }
    
    else {
    
            return y;
    
    }
    
    }
    
  2. la precedente funzione restituisce un intero, è utile/necessario che la sua versione che opera con i puntatori restituisca un puntatore?
  3. Quale tra questi è il modo corretto di richiamare la seguente funzione:
    int funzioneEsercizio(int a, int b, int *c, int *d)
    

    a)

    int a, b, c, d;
    
            funzioneEsercizio(a,b,c,d);
    

    b)

            int a, b, c, d;
    
            funzioneEsercizio(a, b, &c, &d);
    

    c)

            int a, b, *c, *d;
    
            funzioneEsercizio(*a, *b, &c, &d);
    

Array, matrici e aritmetica dei puntatori

In alcuni casi può essere utile all'interno del nostro codice raggruppare le variabili in "gruppi" ed accedere alle singole variabili tramite il nome del gruppo ed un indice numerico. Detta così può sembrare una cosa campata in area, ma poter accedere ad una serire di variabili utilizzando un indice numerico offre veramente infinite possibilità, che al contrario con le variabili "normali" non abbiamo. Qualche esempio? Possiamo accedere alle variabili in ordine dentro un ciclo for, semplicemente prendendo come indice l'indice dell'iterazione...vedremo più avanti qualche esempio di utilizzo.

Un array (detto anche vettore) è una struttura dati basilare, le strutture dati sono, appunto, strutture di variabili aggregate che in qualche modo ci aiutano a rappresentare meglio un problema all'interno del programma. Esistono strutture dati più o meno complesse, ma forse la più semplice di tutte è proprio l'array.

Un array è un insieme di variabili dello stesso tipo, ciascuna è accessibile tramite il nome dell'array e l'indice della sua posizione.

Per matrice invece si intende di norma una struttura bidimensionale, in cui le singole variabili sono raggiunte tramite una coppia di interi che rappresentano la coppia riga/colonna, avete presente battaglia navale? In C praticamente è pensata come un array...le cui singole variabili non siano interi o caratteri, ma a loro volta altri array.


corso-completo-c-i-puntatori-parte-2-vettori-matrici-array-01

Possiamo creare una matrice con un numero qualsiasi di dimensioni, certo fino ad una matrice a 3 dimensioni possiamo anche disegnarla, ma una a 4, 5, n dimensioni è costretta a stare solo nella nostra testa. Quello che cambia a livello teorico è che in una matrice ad n-dimensioni per accedere alla variabile mantenuta al suo interno abbiamo bisogno di n indici.

Ovviamente l'analogia con la matematica è forte, chi ha studiato un pò di analisi all'università sa che un elemento in uno spazio n-dimensionale è univocamente identificato da una n-upla ordinata di elementi, ma non addentriamoci troppo nel matematichese 🙂

Per dichiarare un array in C è sufficiente inserire il numero degli elementi che costituirà l'array tra parentesi quadra subito dopo il nome della variabile. Ad esempio se vogliamo dichiarare un array di dieci numeri interi basterà scrivere:

int mioArray[10];

Mentre per accedere alle singole variabili che costituiscono l'array basterà inserire l'indice corrispondente tra parentesi quadre.

mioArray[5] = 42; //assegna il valore 42 all'elemento in posizione 5 dell'array.

ATTENZIONE: Va sottolineato che in C gli array sono "zero based" il che significa che il primo elemento avrà come indice di posizione zero. Quindi gli elementi di mioArray sono accessibili da:

mioArray[0];

//fino a

mioArray[9];

Per dichiarare una variabile a due dimensioni si utilizza una sintassi molto simile, che si ripete poi identica per le matrici ad n dimensioni. Per una matrice bidimensionale costituita da 10 righe e 5 colone basta scrivere:

int miaMatrice[10][5]; //dichiara una matrice composta 10 righe e 5 colonne.

e per accedere all'elemento alla riga 3, colonna 4 basterà scrivere:

miaMatrice[3][4];

ATTENZIONE: nche in questo caso ricordiamo che la prima riga (e la prima colonna) hanno indice zero.

Per dichiarare una matrice a 3 dimensioni scriveremo:

int miaMatrice[10][10][10];

Vi assicuro che difficilmente vi troverete ad utilizzare matrici a più di tre dimensioni..

Notare che già la sintassi spiega che parlare di matrice bidimensionale equivale a parlare di un array di array, una matrice tridimensionale è un array di matrici bidimensionali e così via.

Facciamo un piccolo esempio sull'utilizzo degli array e delle matrici, scriviamo un programma che dichiari e inizializzi un array di 10 elementi ed una matrice 5x5 e poi ne visualizzi a video i valori.

#include 

int main(int argc, char **argv){

 int mioArray[10];

 int miaMatrice[5][5];

 int i,j; //una variabile temporanea, mi serviranno per i cicli for.                                                      

 /* inizializzo l'array, utilizzo un ciclo ed assegno ad ogni variabile                                                  

 *  un valore pari alla sua posizione + 100;                                                                              

 */

 for (i = 0; i < 10; i++ ){

   mioArray[i] = i + 100;

 }

 //visualizzo l'array a schermo                                                                                          

 for (i = 0; i < 10; i++ ){

   printf("%d ", mioArray[i]);

 }

 printf("\n");

 /* inizializzo la matrice utilizzando due cicli for annidati.                                                            

  * in ogni valore inserisco il prodotto riga * colonna                                                                  

  */

 for (i = 0; i < 5; i++) {

   for (j = 0; j < 5; j++) {

     miaMatrice[i][j] = i * j;

   }

 

 /* Visualizzo la matrice a schermo                                                                                        

  *                                                                                                                        

  */

 for (i = 0; i < 5; i++) {

   for (j = 0; j < 5; j++) {

     printf("%d ",miaMatrice[i][j]);

   }

   printf("\n");

 }

 return 0;

}

Avete notato com'è facile accedere quindi alle variabili tramite un indice? in questo modo possiamo scorrere tutto l'array o la matrice con dei semplici cicli for.

In questo esempio abbiamo inizializzato i valor dell'array tramite un ciclo for, esiste una sintassi alternativa che è la seguente

int mioArray[10] = {100,101,102,103,104,105,106, 107, 108, 109};

Questa sintassi è utilizzabile solo in fase di dichiarazione dell'array, non è possibile utilizzarla altrove.

Ovviamente gli array non servono solo ad essere ciclati dentro un for, ma sono una vera e propria struttura dati utile per rendere il nostro codice più semplice, facciamo un'altro piccolo esempio: supponiamo di voler scrivere una funzione che restituisca il numero dei giorni del mese passato come parametro.

Per semplificare diciamo che la nostra funzione accetterà come parametro un intero che rappresenta il numero del mese (gennaio = 1, febbraio = 2 ...etc etc).

Un approccio "ingenuo" potrebbe essere il seguente:

int giorniDelMese(int mese) {

        if (mese == 0){

                return 31;

}

        else if (mese == 2) {

                return 29;

}

else if (mese == 3) {

        return 31;

}

}

Oppure magari siete già un pò più esperti ed avete pensato a qualcosa come:

int giorniDelMese(int mese) {

        if (mese == 1 || mese == 3 || mese == 5 || mese == 7 || mese == 8 || mese == 10 || mese == 13) {

                return 31

}

        else if (mese == 4 ||  mese == 6 || mese == 9 || mese == 11 ) {

        return 30;

}

else {

        return 29;

}

}

..ma non sarebbe più bella da leggere se fosse scritta in questo modo: ?

int giorniDelMse (int mese) {

        int giorniDeiMesi[12] = {31,29,31,30,31,30,31,31,30,31,30,31}

        return giorniDeiMesi[mese - 1];

}

Semplice e chiara... notare che abbiamo digitato [mese -1] perché gli array sono "zero based" mentre come da specifiche abbiamo detto che gennaio =1, febbraio = 2 etc.

Vi state chiedendo perché io abbia deciso di inserire questo argomento nel capitolo dei puntatori? Bene, adesso allora è il momento (come diceva un autore di cui non ricordo il nome) di mettere al cervello gli scarponi da arrampicata, perché il percorso si fa un pò arduo (contenti? :P)

Un'informazione preliminare: gli array in C sono memorizzati in celle di memoria contigue, un array composto da 10 interi occoperà quindi 40 byte consecutivi all'interno della memoria del nostro calcolatore. Questa informazione ci tornerà utile tra un attimo, dopo che avremo parlato dell'aritmetica dei puntatori.

Con i puntatori è possibile svolgere delle operazioni aritmetiche un pò particolari, supponiamo di avere un puntatore ad intero che punti all'allocazione di memoria numero 100, se sommiamo a questo puntatore un intero, ad esempio 5, il risultato non sarà 105 come ci si potrebbe aspettare ma l'operazione effettuata sarà in realtà 100 + 5 * il numero di byte che costituiscono il tipo del puntatore (in questo caso 4byte perchè puntatore ad intero) quindi il risultato sarà 120. Quello che abbiamo ottenuto in pratica è di far puntare il puntatore 5 "passi" più avanti nella memoria.

La definizione di wikipedia è molto più formale:

L'operatore di somma di puntatore e intero richiede un operando di tipo puntatore e un operando di tipo intero. Il risultato della somma è l'indirizzo dato dal puntatore incrementato del risultato della moltiplicazione dell'intero specificato per la dimensione (sizeof) del tipo base del puntatore espressa in byte. Per esempio, se p è un puntatore al tipo intero int (int *p) di valore 1000 (p=1000), e se la dimensione di un int è quattro byte, p+1 vale 1004, p+2 vale 1008, e in generale p+n vale 1000+n*2.

Quindi, date queste due ultime informazioni, se conoscessimo l'indirzzo "di partenza" di un array potremmo accedere ai suoi valori non tramite la sintassi con gli indici, ma tramite operazioni dirette con i puntatori.

Infatti è proprio quello che avviene! La dichiarazione di un array con l'istruzione:

int mioArray[10];

Si occupa di riservare 10 * 4 byte = 40 byte di memoria consecutiva e memorizza nella variabile mioArray (senza parentesi quadre) l'indirizzo di partenza di questo blocco di celle.

Quindi mioArray, è a tutti gli effetti un puntatore ad intero, soltanto che grazie a questa sintassi, il compilatore ha riservato lo spazio per lui e per altri 9 come lui.

Come abbiamo detto precedentemente, sommando un intero a mioArray posso muovermi all'interno della memoria a step di un intero, se volessi infatti leggere l'intero memorizzato nella prima posizione dell'array (la posizione zero) basterebbe leggere il valore contenuto in

mioArray + 0

se volessi invece leggere il valore contenuto nella seconda posizione basterebbe leggere il valore contenuto in

mioArray + 1

Ricordiamoci però che per leggere il valore della memoria all'indirizzo puntato da un puntatore è necessario l'operatore di dereferenziazione " * " quindi per stampare il valore contenuto nella posizione n dell'array dovremo scrivere:

printf("Il valore è: %d", *(mioArray + n));

Le parentesi son d'obbligo perché l'operatore di dereferenziazione ha la stessa priorità del " + " ma l'associatività è a destra quindi se le omettessi il risultato sarebbe errato.

Vediamo in questo piccolo esempio come utilizzare indifferentemente gli array tramite indici e tramite aritmetica dei puntatori:

#include 

int main(int argc, char **argv) {

 int i; //variabile da usare nei cicli for                                                                                

 /* Dichiaro un array composto da 5 interi                                                                                

  * e lo inizializzo con la sintazzi "veloce"                                                                              

  */

 int mioArray[5] = {99,98,97,96,95};

 /* Accedo all'array per visualizzare i dati tramite gli indici                                                            

  */

 for (i = 0; i < 5; i++){

   printf("In posizione %d l'array contiene: %d\n", i, mioArray[i]);

 }

 /* Eseguo l'operazione identica utilizzando però                                                                          

  * l'artimetica dei puntatori                                                                                            

  */

 for (i = 0; i < 5; i++){

   printf("In posizione %d l'array contiene: %d\n", i, *(mioArray + i));

 }

}

Il passo finale

Adesso siete pronti per un piccolissimo sforzo finale, comprendere come strutture come array e matrici possono essere passate ad una funzione come parametro.

Tutto quello che bisogna sapere è già stato detto in questo capitolo, bisogna solo farci attenzione.

Abbiamo visto come si passa un puntatore ad una funzione e abbiamo scoperto che un array non è altro che un puntatore al quale è stato riservato dello spazio in più, bene, a questo punto, per scrivere una funzione che accetti come parametro un array, occorre semplicemente scrivere una funzione che accetti un puntatore come parametro.

Purtroppo c'è una limitazione, considerando quanto abbiamo visto fino ad ora la funzione non può sapere, soltanto esaminando il puntatore, quant'è grande l'array, quindi occorrerà utilizzare uno stratagemma. Per questo ci sono diverse scuole di pensiero: qualcuno inserisce un valore particolare alla fine dell'array per "accorgersi" quando l'array è finito, altri (come me) passano alla funzione anche la dimensione dell'array. Vediamo in questo esempio la creazione della funzione "printArray":

#include 

void printArray(int *array, int size){

 int i;

 for (i = 0; i < size; i++ ){

   printf("%d ", array[i]);

 }

 printf("\n");

}

int main(int argc, char **argv) {

 int i; //variabile da usare nei cicli for                                                                                

 /* Dichiaro un array composto da 5 interi                                                                                

  * e lo inizializzo con la sintazzi "veloce"                                                                              

  */

 int mioArray[5] = {99,98,97,96,95};

 printArray(mioArray, 5);

}

Per questa lezione vi risparmio gli esercizi, mi aspetto però che chi vuol imparare sul serio si metta a studiare per bene queste ultime lezioni e a provare il codice fino a prendere dimestichezza con i concetti presentati.

Letture consigliate:

C-Corso-Completo-di-Programmazione-kernighan-ritchie-devAPP Il linguaggio C. Principi di programmazione e manuale di riferimento (Accademica)
Brian W. Kernighan - Dennis M. Ritchie
Editore: Pearson | Lingua: Italiano | Brossura: 313 pagine
Prezzo Listino: EUR 27,00
Prezzo Promozione: EUR 22,95 con Spedizione gratuita

C-Corso-Completo-di-Programmazione-Deitel-Deitel-devAPP C. Corso completo di programmazione
Paul J. Deitel - Harvey M. Deitel
Editore: Apogeo | Lingua: Italiano | Brossura: 640 pagine
Prezzo Listino: EUR 39,00
Prezzo Promozione: EUR 33,15 con Spedizione gratuita



Share this story:
  • tweet

Tags: aritmetica dei puntatori CArray Ccorso completo di Ccorso di Cesempi codice programmazione Cesercizi programmazione CI puntatori in CIgnazio Calòmatrici Cvettori C

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 agosto 2018 - 0 Comment

Related Posts

  • iSketch: la lavagna magica di Ignazio Calò disponibile GRATUITAMENTE in App Store

    29 agosto 2011 - 2 Comments
  • 17. La gestione dei file in C

    22 agosto 2011 - 4 Comments
  • Uno sguardo a Unit test: impariamo a prevenire ore e ore di debug durante lo sviluppo di Applicazioni iOS

    8 agosto 2011 - 2 Comments

Author Description

9 Responses to “13. I Puntatori – Array, matrici e aritmetica dei puntatori – parte 2”

  1. 6 maggio 2011

    Spartan

    O mamma..sto rimanendo indietro D:

  2. 7 maggio 2011

    simone

    molto interessante, un solo dubbio: con gli array a più dimensioni come si usa la somma del puntatore?
    Nel senso se ho un array: int n[2][2]; *n punta verso n[0][0], mentre *(n+1) punta a n[1][0] o a n[0][1] ?

    grazie mille e complimenti!

  3. 7 maggio 2011

    Ignazioc

    brevemente… il C memorizza le matrici multimensionali secondo il metodo Row-major order (http://en.wikipedia.org/wiki/Row-major_order) quindi se incrementi vai al valore successivo nella stessa riga, appena sei arrivato all’ultima colonna passi alla riga successiva.
    Questo aspetto è molto importante, e bisognerebbe tenerne conto quando si esplorano le matrici (ad esempio quando si lavora con le immagini) perché accedere agli elementi lungo le righe è più rapido che esplorarli per colonne.

  4. 9 maggio 2011

    simone

    grazie mille, chiarissimo!

  5. 29 maggio 2011

    WEBSELF

    Bellissima questa lezione! Mi ha chiarito davvero molti dubbi e lacune.

    Una piccola “errata corrige”:

    Nel codice in cui riempi la matrice del prodotto dei suoi valori hai 2 Cicli FOR annidati ma manca una chiusura “}”.

    Grazie ancora!

    Alessio

  6. 19 giugno 2011

    Luca


    Spartan:

    O mamma..sto rimanendo indietro D:

    Un po di confusione 🙂

  7. 24 giugno 2011

    Luca

    Bene, sto capendo tutto 🙂

  8. 13 novembre 2011

    Andrea

    Sbaglio o qua ti sei dimenticato di chiudere un for?
    #include

    int main(int argc, char **argv){

    int mioArray[10];

    int miaMatrice[5][5];

    int i,j; //una variabile temporanea, mi serviranno per i cicli for.

    /* inizializzo l’array, utilizzo un ciclo ed assegno ad ogni variabile

    * un valore pari alla sua posizione + 100;

    */

    for (i = 0; i < 10; i++ ){

    mioArray[i] = i + 100;

    }

    //visualizzo l'array a schermo

    for (i = 0; i < 10; i++ ){

    printf("%d ", mioArray[i]);

    }

    printf("\n");

    /* inizializzo la matrice utilizzando due cicli for annidati.

    * in ogni valore inserisco il prodotto riga * colonna

    */

    for (i = 0; i < 5; i++) {

    for (j = 0; j < 5; j++) {

    miaMatrice[i][j] = i * j;

    }

    /* Visualizzo la matrice a schermo

    *

    */

    for (i = 0; i < 5; i++) {

    for (j = 0; j < 5; j++) {

    printf("%d ",miaMatrice[i][j]);

    }

    printf("\n");

    }

    return 0;

    }

    printf(“\n”);

    }}

    return 0;

    }

    Dovrebbe essere cosi sennò non lo compila

  9. 2 ottobre 2012

    Melus

    Scusa, ma inizializzazione un array a 10 significa che saranno disponibili 10 locazioni di memoria (da 0 a 9) o 11 (da 0 a 10)?
    Grazie.

Leave a Reply Cancel Reply

Your email address will not be published. Required fields are marked *


*
*

Corsi online per imparare a programmare

La tua pubblicità qui



La tua pubblicità qui

CREIAMO UN’APPLICAZIONE COMPLETA

Costruiamo insieme il nostro Client per Flickr
Logo Flickr Ecco come creare un semplice cient per Flickr, il noto Social Network per la gestione e la condivisione di foto, che ci permetterà di scaricare e visulizzare sui nostri iPhone le foto marcate come "interessanti". Questo elenco viene aggiornato quotidianamente, quindi ogni giorno l'applicazione fornirà foto diverse!

Creare un client Flickr - PARTE 1
Creare un client Flickr - PARTE 2
Creare un client Flickr - PARTE 3
Creare un client Flickr - PARTE 4

Scarica il progetto completo (FREE)

Download (836 KB)

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • Jooble
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.