{"id":193,"date":"2012-06-22T18:53:47","date_gmt":"2012-06-22T16:53:47","guid":{"rendered":"http:\/\/localhost\/blog\/?p=193"},"modified":"2013-03-07T11:55:01","modified_gmt":"2013-03-07T10:55:01","slug":"tutorial-i-puntatori","status":"publish","type":"post","link":"https:\/\/www.ilbytecidio.it\/?p=193","title":{"rendered":"Tutorial: i puntatori"},"content":{"rendered":"<p>I puntatori sono una delle cose pi\u00f9 temute dai programmatori nuovi dei linguaggi C e C++, ma prima o poi bisogna averci a che fare. Ho pensato di creare questo piccolo tutorial. Vi garantisco che se capirete bene tutto fino in fondo e sarete capaci di riprodurre quello che vi illustrer\u00f2, non avrete alcun problema in futuro con i puntatori.<!--more--><\/p>\n<p><strong>Cos&#8217;\u00e8 un puntatore?<\/strong><br \/>\nUn puntatore non \u00e8 altro che una particolare variabile che contiene un indirizzo di memoria, spesso, ma non necessariamente, quello di un&#8217;altra variabile.<br \/>\nCosa ce ne facciamo di una roba del genere? Posso garantirvi che \u00e8 molto pi\u00f9 utile di quanto sembri, tanto \u00e8 vero che in alcuni linguaggi usate implicitamente i puntatori, come il Java, in cui ogni variabile corrispondente a un oggetto \u00e8 in realt\u00e0 un puntatore! In C e C++ la gestione dei puntatori \u00e8 esplicita e richiede molta attenzione perch\u00e9 facilita la creazione di bug fastidiosi e difficili da individuare.<br \/>\nGli esempi qui riportati sono tutti in C, ma vanno bene anche per C++.<br \/>\nIl prerequisito per capire questo tutorial \u00e8 di conoscere i rudimenti del C o del C++, possibilmente con un po&#8217; di pratica. Se siete tipi svegli, dovreste capirlo anche se siete capaci di programmare con un altro linguaggio.<\/p>\n<p><strong>Il primo esempio:<\/strong><\/p>\n<pre lang=\"C\" line=\"1\">#include <stdio.h>\r\n\r\nvoid scambia(int *a, int *b){\r\n  int tmp;\r\n \r\n  tmp = *a;\r\n  *a = *b;\r\n  *b = tmp;\r\n}\r\n\r\nint main(){\r\n  int a = 9, b = 5; \/* Variabili *\/\r\n  int *pa, *pb; \/* Puntatori *\/\r\n  pa = &a; \/* Assegnamo a pa l'indirizzo di a *\/\r\n  pb = &b; \/* Assegnamo a pb l'indirizzo di b *\/\r\n\r\n  \/* Scriviamo i valori di a e b attraverso i puntatori*\/\r\n  printf(\"a: %d, b: %d\\n\", *pa, *pb); \/* Scrive \"a: 9, b: 5\" *\/\r\n  scambia(pa, pb);\r\n  printf(\"a: %d, b: %d\\n\", *pa, *pb); \/* Scrive \"a: 5, b: 9\" *\/\r\n\r\n  pb = pa; \/* Ora sia pa che pb puntano ad a *\/\r\n  printf(\"*pa: %d, *pb: %d\\n\", *pa, *pb); \/* Scrive \"*pa: 5, *pb: 5\" *\/\r\n\r\n  *pb = 12;\r\n  printf(\"*pa: %d, a: %d, b: %d\\n\", *pa, a, b); \/* Scrive \"*pa: 12, a: 12, b: 9\". Se hai capito perch\u00e9, hai capito i puntatori! *\/\r\n\r\n  return 0;\r\n}<\/pre>\n<p>Questo \u00e8 un esempio di base sui puntatori.<br \/>\nTre sono le cose fondamentali da sapere:<\/p>\n<ol>\n<li>Se scriviamo<strong><br \/>\nint *p;<\/strong><br \/>\nstiamo dichiarando un puntatore a una variabile di tipo intero. Ovviamente possiamo dichiarare puntatori a qualsiasi tipo di dato.<\/li>\n<li>Scrivendo<strong><br \/>\np = &amp;a;<\/strong><br \/>\nassegnamo al puntatore p l&#8217;indirizzo della variabile a<\/li>\n<li>Se scriviamo<strong><br \/>\n*p = 12;<\/strong><br \/>\nstiamo assegnando 12 alla variabile puntata da p. Se consideriamo l&#8217;assegnamento che abbiamo fatto nel punto 2, ora la variabile a varr\u00e0 12.<\/li>\n<\/ol>\n<p>Nel nostro esempio, dichiariamo due variabili e ne scambiamo i valori attraverso una procedura, poi ci giochiamo un po&#8217;, tanto per fissare i concetti!.<br \/>\nPerch\u00e9 alla procedura di scambio abbiamo passato dei puntatori invece di passare direttamente le variabili? Perch\u00e9 quando passiamo una variabile a una qualsiasi funzione in C, in realt\u00e0 passiamo una copia del suo valore. Se ci interessa modificare la variabile all&#8217;interno della procedura, dobbiamo passare come parametro un puntatore alla variabile. Questo si fa a volte anche per risparmiare spazio in memoria e migliorare le prestazioni: se una procedura deve lavorare con una struttura molto grande, il passaggio di dati per copia pu\u00f2 essere molto costoso, mentre passare un puntatore \u00e8 come passare un semplice intero!<br \/>\nChiaro fin qui?<\/p>\n<p><strong>Allocazione dinamica della memoria<\/strong><\/p>\n<pre lang=\"C\" line=\"1\">#include <stdio.h>\r\n#include <malloc.h>\r\n\r\nstruct coppia{\r\n  int val1;\r\n  int val2;\r\n};\r\n\r\nint main(){\r\n  \/* Allocazione statica di una struttura coppia *\/\r\n  struct coppia c1;\r\n  \/* Modifica dei campi *\/\r\n  c1.val1 = 1;\r\n  c1.val2 = 10;\r\n\r\n  \/* Dichiariamo un puntatore a struttura coppia e assegnamo l'indirizzo di c1 *\/\r\n  struct coppia *pc = &c1;\r\n  \/* Per riferirci a un campo della struttura puntata possiamo usare l'operatore -> sul puntatore, che \u00e8 come scrivere (*puntatore).campo! *\/\r\n  printf(\"val1: %d, val2: %d\\n\", pc->val1, (*pc).val2); \/* Scrive \"val1: 1, val2: 10\" *\/\r\n\r\n  \/* Allocazione dinamica di una struttura *\/\r\n  pc = (struct coppia*)malloc(sizeof(struct coppia));\r\n  pc->val1 = 13;\r\n  pc->val2 = 23;\r\n  printf(\"val1: %d, val2: %d\\n\", pc->val1, (*pc).val2); \/* Scrive \"val1: 13, val2: 23\" *\/\r\n\r\n  \/* Deallocazione della struttura allocata dinamicamente. *\/\r\n  free(pc);\r\n}\r\n<\/pre>\n<p>Ecco qui un altro uso dei puntatori. In C e C++ abbiamo due modi per allocare qualsiasi variabile: allocazione statica o dinamica. Allocare una variabile vuol dire semplicemente riservare la memoria per memorizzarla. Con l&#8217;allocazione statica la memoria \u00e8 riservata quando si dichiara la variabile e resta l\u00ec finch\u00e9 non si esce dal programma. Con l&#8217;allocazione dinamica la memoria pu\u00f2 essere riservata in qualsiasi momento durante l&#8217;esecuzione (a <em>run time<\/em>) ed eventualmente liberata affinch\u00e9 lo spazio sia disponibile per altre allocazioni. Entrambi i modi servono, a seconda dei vari usi.<br \/>\nNell&#8217;esempio abbiamo dichiarato una struttura che contiene una coppia di valori e l&#8217;abbiamo allocata nei due modi possibili. Per l&#8217;allocazione dinamica ci serve obbligatoriamente un puntatore per avere modo di riferirci alla memoria appena riservata. L&#8217;indirizzo di memoria da associare al puntatore ci viene restituito da malloc, funzione con cui possiamo allocare dinamicamente quantit\u00e0 arbitrarie di memoria. Nell&#8217;esempio appena visto non era necessario liberare la memoria con la funzione free, dato che quando il programma termina, il sistema operativo rilascia tutta la memoria da lui occupata.<\/p>\n<p><strong>Mettiamo tutto in pratica, complicandoci un po&#8217; la vita<\/strong><br \/>\nL&#8217;esempio che vedrete ora \u00e8 pi\u00f9 complesso, ma non troppo.<br \/>\nLink al codice sorgente: <a href=\"http:\/\/www.ilbytecidio.it\/altri_file\/pila.c\">http:\/\/www.ilbytecidio.it\/altri_file\/pila.c<\/a><br \/>\nCon questo esempio impareremo a costruire una pila (o stack). Una pila \u00e8 una struttura dati gestita con politica LIFO (Last In First Out). Questo significa che possiamo inserire e togliere dati solo in testa, come se i nostri valori fossero una pila di piatti: non li possiamo sfilare dal centro! Nel nostro caso si tratta di una pila di caratteri.<br \/>\nPartiamo dall&#8217;inizio. Per creare la nostra pila usiamo una struttura &#8220;nodo&#8221; che contiene un carattere e un puntatore all&#8217;elemento successivo della pila. In parole povere, la implementeremo usando una lista concatenata.<br \/>\nCi sono modi pi\u00f9 semplici, ma noi vorremmo gestire la pila in modo astratto, cio\u00e8 nascondendo all&#8217;utente pi\u00f9 dettagli possibile circa la sua implementazione. Perch\u00e9 questo sia possibile, ci serve una serie di funzioni che agiscano direttamente sulla pila.<br \/>\nSulla riga 10 vediamo che pila e nodo sono due tipi praticamente uguali, che indicano la struttura nodo. Avremmo potuto scrivere typedef *nodo pila per nascondere qualche dettaglio in pi\u00f9, ma volevo che l&#8217;uso dei puntatori fosse pi\u00f9 esplicito possibile, per chiarire l&#8217;esempio.<br \/>\nNel main, possiamo vedere che la pila \u00e8 dichiarata come puntatore. Per la precisione sar\u00e0 un puntatore al primo nodo.<br \/>\nVediamo le funzioni che usiamo per la gestione. La prima (riga 13) serve a inizializzare la pila. Questo viene fatto mettendo a NULL il puntatore alla pila. NULL \u00e8 un particolare valore che indica che il puntatore non sta puntando ad alcun indirizzo. Quando il puntatore alla pila \u00e8 NULL, significa che la pila \u00e8 vuota. Notate qualcosa di strano? Forse che il parametro \u00e8 un puntatore a un puntatore! Se avete prestato attenzione al paragrafo precedente, dovrebbe essere chiaro che se vogliamo modificare una qualsiasi variabile all&#8217;interno di una procedura dobbiamo passarle un puntatore. Questo vale anche se la nostra variabile \u00e8 a sua volta un puntatore!<br \/>\nLa seconda funzione \u00e8 la push, con cui inseriamo i valori in testa alla pila. Per prima cosa si alloca un nuovo nodo, quindi gli si assegna il valore e si fa puntare il suo campo <em>prossimo<\/em> al nodo che fin&#8217;ora era in testa. E il puntatore alla testa della pila punter\u00e0 al nuovo nodo!<br \/>\nLa terza funzione \u00e8 la pop, con cui togliamo il valore in testa alla pila. L&#8217;aspetto saliente \u00e8 che salviamo temporaneamente l&#8217;indirizzo del nodo che stiamo eliminando per poter liberare la memoria da esso occupata, dopo aver aggiustato il puntatore della pila.<br \/>\nLe ultime due funzioni non necessitano di un puntatore a puntatore perch\u00e9 non fanno modifiche. La penultima restituisce il valore in testa alla pila e l&#8217;ultima ci permette di controllare se la pila \u00e8 vuota.<br \/>\nIl resto \u00e8 decisamente banale: si chiede all&#8217;utente di inserire una stringa, i caratteri digitati sono inseriti uno ad uno nella pila e poi questa \u00e8 svuotata, scrivendo i caratteri man mano che li si estrae. Come possiamo vedere, alle funzioni a cui serve il puntatore al puntatore della pila, passiamo l&#8217;indirizzo di mia_pila che a sua volta \u00e8 il puntatore alla pila.<br \/>\nCapito questo esempio siete a posto per sempre coi puntatori. Come esercizio di comprensione vi lascio il compito di creare un&#8217;altra struttura dati astratta: la coda, in cui, al contrario della pila i valori sono inseriti in fondo ed estratti in testa (politica FIFO, First In First Out).<\/p>\n<p><strong>\u00c8 tutto?<\/strong><br \/>\nQuasi. Quello che sto per dirvi era definito dalla mia insegnante di programmazione all&#8217;universit\u00e0 come &#8220;il Vangelo&#8221;, guai dimenticarlo!<br \/>\n<em>In C e C++ il nome di un array \u00e8 un puntatore al suo primo elemento.<\/em><br \/>\nQuindi queste istruzioni sono perfettamente lecite:<\/p>\n<pre lang=\"LANGUAGE\" line=\"1\">char prova[5] = \"Ciao\\0\";\r\nputchar(prova[2]); \/* Stampa \"a\" *\/\r\nputchar(*(prova + 2)); \/* Stampa \"a\" *\/<\/pre>\n<p>Come avrete sentito dire, C e C++ sono linguaggi di livello relativamente basso, ci\u00f2 significa che mancano molte delle astrazioni presenti in altri linguaggi. Da un lato significa avere molta libert\u00e0 e prestazioni migliori, dall&#8217;altro vuol dire avere poco aiuto sia dal compilatore che a run time, quindi per evitare errori dobbiamo acquisire molta padronanza e prestare attenzione a ci\u00f2 che facciamo. Per esempio, se in Java non \u00e8 possibile uscire dai confini di un array, in C lo \u00e8! Se puntiamo per sbaglio a un inesistente sesto elemento di un array di cinque o facciamo qualsiasi altro errore nell&#8217;assegnamento di un puntatore, il compilatore NON ci avviser\u00e0 e non \u00e8 nemmeno detto che il programma inizi a funzionare male da subito.. potremmo accorgercene in un momento a caso, giusto per rendere le cose pi\u00f9 divertenti!<\/p>\n<p>Ora che vi ho spaventato, \u00e8 davvero tutto! Buon divertimento!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I puntatori sono una delle cose pi\u00f9 temute dai programmatori nuovi dei linguaggi C e C++, ma prima o poi bisogna averci a che fare. Ho pensato di creare questo piccolo tutorial. Vi garantisco che se capirete bene tutto fino &hellip; <a href=\"https:\/\/www.ilbytecidio.it\/?p=193\">Continua a leggere<span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,17],"tags":[],"class_list":["post-193","post","type-post","status-publish","format-standard","hentry","category-programmazione-c","category-programmazione-cpp"],"views":152,"_links":{"self":[{"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=\/wp\/v2\/posts\/193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=193"}],"version-history":[{"count":10,"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=\/wp\/v2\/posts\/193\/revisions"}],"predecessor-version":[{"id":199,"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=\/wp\/v2\/posts\/193\/revisions\/199"}],"wp:attachment":[{"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ilbytecidio.it\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}