martedì 30 Aprile 2024
Home / Corsi / Corso C / Lezione 6 – I puntatori (parte seconda)
Corso C

Lezione 6 – I puntatori (parte seconda)

Eccoci di nuovo qua!

Proseguiamo ad esplorare il mondo dei puntatori.

Sommario

  1. Soluzione esercizio Lezione 5
  2. Funzioni: variabili passate per valore e per riferimento
  3. scanf
  4. Array e funzioni
  5. Parametri di ingresso del main argc ed *argv[]
  6. Riassunto
  7. Esercizi

1 Soluzione esercizio Lezione 5

2 Funzioni: variabili passate per valore e per riferimento

Ebbene, vediamo tramite un esempio in cosa consiste il passaggio per valore.

  • la variabile passata alla funzione ritorna nel main uguale a se stessa
  • la subroutine ha infatti lavorato su di copia
  • notazione nella funzione valida ma utile principalmente nel caso dei puntatori

A livello di memoria abbiamo quanto rappresentato in Fig1:

Fig1 – Passaggio di una variabile per valore

le variabili sono totalmente separate.

Le cose sono differenti nel caso del passaggio per riferimento:

Questa volta a livello di memoria cosa è successo?

Fig2 – Passaggio di una variabile per riferimento

Dalla Fig2 si vede che il puntatore x (notazione alternativa usata nel codice) coincide con l’indirizzo di a e di conseguenza *x sarà uguale ad a

In altre parole, come dimostra anche l’esempio, una funzione può anche restituire uscite, oltre che tramite l’istruzione return, anche tramite i parametri di ingresso, a patto di usare dei puntatori, per il meccanismo appena illustrato.

3 scanf

Arrivati a questo punto allora non stupirà vedere come funziona l’istruzione scanf (e tutte le sue analoghe) nella lettura di parametri di input.

Fino ad ora abbiamo solo stampato stringhe e grandezze numeriche, adesso è arrivato il momento di leggere i nostri input.

  • Come per il passaggio di variabili per riferimento, la scanf necessita l’indirizzo della variabile in input
  • Come per la printf bisogna specificare il formato di lettura.
  • Si noti come nel caso della stringa non serve & perché il nome della stringa stessa è un puntatore (linea 14)
  • Come esiste la sprintf, analogamente c’è la sscanf che agisce sulle stringhe
  • Le stringhe in ingresso alla scanf non devono avere spazi: la lettura infatti si arresta quando ne incontra uno,  (vedremo come superare questo inconveniente con un’altra funzione)
  • Con %*formato è possibile saltare la lettura di un elemento presente.

4 Array e funzioni

Una condizione molto particolare si può generare quando si usano array nelle funzioni.

Come prima configurazione consideriamo un array allocato nel main e passato ad una subroutine

Quindi quello che viene allocato nel main può tranquillamente essere trattato nella funzione a patto di passare il puntatore al vettore.

A livello di memoria la situazione è quella di Fig3

Fig3 – Array passato a funzione

in cui i due puntatori si portano a coincidere.

Avevamo detto che era possibile restituire grandezze tramite i parametri di ingresso alle funzioni: vediamo allora il caso il cui l’array è allocato nella subroutine

Prima di analizzare il codice per capire come funziona, chiariamo cosa è un puntatore doppio.

Fig4 – Puntatore doppio

In Fig4 è rappresentata la situazione.

Se dichiariamo int **y, a livello di memoria, l’indirizzo sarà y che avrà come cella di memoria *y che in effetti è un altro indirizzo che punta a *yy.

Adesso esaminiamo le varie fasi in cui possiamo suddividere il nostro codice:

  1. Inizialmente, nel main, il puntatore x non è stato inizializzato quindi l’unica grandezza rilevante è il suo indirizzo &x linea 13
  2. Nella linea 15, in cui passiamo l’indirizzo di x, di fatti stiamo facendo qualcosa di simile alla Fig4, stiamo usando un puntatore doppio:
    • &x ≡ y
    • x ≡ *y
    • *x ≡ **y
  3. Per il punto 2 allora, nella chiamata alla funzione effettuiamo la seguente uguaglianza: &x=y (tramite la notazione alternativa tale uguaglianza appare evidente)
  4. Nella funzione vi è anche il puntatore tmp che si alloca come fatto precedentemente e si valorizzano le varie posizioni.
  5. Ora tmp che è un puntatore singolo, devo uguagliarlo ad un puntatore singolo che consenta di ritornare nel main e questo è *y (linea 39) e si noti che in questo modo l’indirizzo di x è stato preservato (&x).
  6. Nel main, y=&x, *y=tmp e quindi x[0] = tmp[0], x[1] = tmp[1], etc

Tutto questo non è obbligatorio, come dimostra l’esempio6_6.c:

5 Parametri di ingresso del main argc ed *argv[]

Sino ad ora la funzione main l’abbiamo scritta sempre senza parametri di ingresso.

Esistono però due ingressi per la funzione principale che consentono di passare degli input al nostro programma in fase di esecuzione.

La più generica chiamata della funzione principale è: int main(int argc, char *argv[]):

  • argc indica il numero di parametri che sono stati passati eseguendo il comando
  • argv[] è un vettore di stringhe che contiene tutti i parametri passati, ricordando che argv[0] rappresenta il nome dell’eseguibile

Vediamo un esempio:

Appare evidente allora come valutando il valore della variabile argc, è possibile tentare una prima verifica dei parametri di ingresso del codice.

Supponendo di voler valutare il valore dell’ordinata di una retta, di cui si conosca il coefficiente angolare (m) ed il termine noto (q), passate per il punto xP (l’equazione della retta è y=m*x + q  e quindi yP = m*xP + q):

Questo tipo di test protegge dagli errori più grossolani.

Per verificare eventuali errori nei parametri di ingresso, un’analisi molto più approfondita sarebbe necessaria, ma in questa sede si voleva solo proporre un possibile uso ragionevole di argc ed argv.

Terminiamo qui questa carrellata sui puntatori.

Alcuni aspetti non sono stati riportati (come per esempio l’aritmetica dei puntatori) ma si spera che le principali informazioni siano presenti. Come già detto nella prima lezione, lo scopo di questo corso non è fare una trattazione a livello universitario delle caratteristiche di un linguaggio di programmazione, ma fornire i principali strumenti per poter affrontare la scrittura di un codice. L’esperienza e tutti i casi che incontrerete faranno il resto e ricordate: Per quanto originali voi siate, sicuramente il vostro problema sarà già capitato a qualcun altro. Usando le keyword appropriate allora cercate in rete. Troverete sicuramente la soluzione, ma per comprenderla, tutto quello che avrete visto in queste pagine sarà indispensabile.

6 Riassunto

  • Una variabile, passata ad una funzione per valore, non sarà modificata, la funzione ne fa una copia che usa al suo interno, lasciando inalterato l’ingresso
  • Una variabile invece passata per riferimento (indirizzo) sarà modificata. L’indirizzo sarà preservato e la funzione, tramite un puntatore agirà sulla cella di memoria della variabile in ingresso, modificandola. Questo è il principio su cui si basa, in generale, la restituzione di output tramite i parametri di ingresso.
  • Sulla base di questi principi funziona la scanf (e sua variante che lavora sulle stringhe sscanf) che consente di leggere i parametri immessi tramite la tastiera
  • Un array passato ad una funzione è un particolare caso di passaggio per riferimento, infatti il contenuto del vettore potrà essere modificato dalla funzione stessa
  • Caso particolare è quello di allorare un vettore in una subroutine e restituirlo tramite i parametri di ingresso. Nel caso di un vettore di n dimensioni, sarà necessario ricorrere ad un puntatore di tipo n+1 (ad esempio vettore monodimensionale, puntatore doppio, vettore bidimensionale, puntatore triplo, etc..)
  • In ogni caso questo procedere non è un obbligo, si può sempre restituire l’array tramite l’istruzione return
  • La funzione main, prevede l’uso, non obbligatorio, di due parametri di ingresso argc ed *argv[]
  • Il primo conta i parametri di ingresso considerando come parametro anche il nome dell’eseguibile
  • il secondo è un vettore di stringhe in cui in posizione 0 c’è il nome dell’eseguibile e nelle posizioni seguenti gli eventuali parametri passati da riga di comando
  • Analizzando queste due variabili è possibile preservare da eventuali errori commessi in fase di lancio del programma

7 Esercizi

  • Provate a scrivere un programma con due funzioni a cui passate variabili per valore e riferimento e verificatene il funzionamento
  • Provate a leggere e stampare dati immessi tramite tastiera
  • Modificate array allocati nel main in subroutine (anche multidimensionali)
  • Seguendo la falsa riga dell’esempio6_5, allocate e restituite tramite parametri di ingresso un array bidimensionale (la soluzione sarà fornita la prossima lezione)
  • Giocate con argc ed argv, rendendo più professionali i vostri programmi.

Ed anche questa settimana è tutto.

Qui il forum di supporto al corso.

Se vuoi restare aggiornato, seguici anche sui nostri social: Facebook, Twitter, Youtube

Se vuoi anche trovare prodotti e accessori Raspberry Pi in offerta, seguici anche su Telegram !!

 

 

 

A proposito di arkkimede

Vedi Anche

MagPi139 doppiapagina

MagPi in Italiano! Soluzione dei Problemi: La Guida

Estratto, tradotto in italiano, di The MagPi N°139 la rivista ufficiale della Fondazione Raspberry Pi.

Powered by themekiller.com