Comprensione delle funzioni C: parametri, rendimenti e ambito

3

La maggior parte dei linguaggi di programmazione consente di suddividere un programma disordinato e di lunga durata in blocchi ordinati e denominati. Questi pezzi sono chiamati funzioni. Ti consentono di riutilizzare il codice invece di riscrivere la stessa logica più e più volte. Una funzione accetta input, noti come parametri, e genera un risultato. In C puoi passare tutti i parametri di cui hai bisogno. Il compilatore non si preoccupa dell’ordine delle funzioni nel file sorgente, purché conosca il nome della funzione prima di tentare di eseguirla.

Ne abbiamo parlato brevemente con la funzione rand. È la funzione più semplice possibile. Non richiede nulla e restituisce un numero intero.

La riga int rand() dice al compilatore che rand è una funzione. Non accetta parametri e restituisce un numero intero. Questa versione specifica non ha variabili locali. Se così fosse, si troverebbero proprio sotto la parentesi graffa di apertura “{“. C ti consente di dichiarare variabili ovunque dopo un {. Esistono finché non viene raggiunto il } corrispondente. Poi svaniscono. Mentre sono attive, le variabili locali risiedono nello stack di sistema.

Nota la mancanza del punto e virgola dopo rand(). Se ne inserisci uno accidentalmente, riceverai un’enorme cascata di messaggi di errore dal compilatore che non hanno senso. Anche senza parametri è necessario utilizzare le parentesi. Senza di essi, non stai dichiarando una funzione. Stai semplicemente dichiarando un numero intero.

La dichiarazione di ritorno e i punti di uscita multipli

L’istruzione return è fondamentale per qualsiasi funzione che fornisca un risultato. Imposta il valore da restituire ed esce immediatamente dalla funzione. È possibile inserire più istruzioni return in una singola funzione per creare più punti di uscita.

Se si dimentica l’istruzione return in una funzione che dovrebbe restituire un valore, la funzione termina quando raggiunge la parentesi graffa di chiusura. Restituisce un valore casuale e spazzatura. La maggior parte dei compilatori moderni ti avviserà se non riesci a restituire un valore specifico. C supporta la restituzione di qualsiasi tipo: int, float, char, struct e altro.

Esistono diversi modi corretti per chiamare la funzione rand. Il più comune è l’assegnazione:

Qui, la variabile “x” ottiene il valore restituito da “rand”. È necessario includere le parentesi nella chiamata, anche se non sono presenti parametri. Senza di essi, “x” riceve l’indirizzo di memoria della funzione “rand”. Raramente è quello che volevi.

Puoi anche chiamarlo all’interno di una condizione:

O semplicemente ignora il risultato:

Nell’ultimo caso, la funzione viene eseguita ma il valore restituito viene scartato. Potresti non volerlo fare con rand, ma molte funzioni usano il valore restituito come codice di errore. Se sai che un errore è impossibile, scartare il codice va bene.

Gestione dei resi annullati

A volte non vuoi restituire nulla. Per questo utilizzi un tipo di ritorno “void”.

Questa funzione restituisce testo ma non restituisce alcun valore. Lo chiami così:

Ancora una volta, includi le parentesi. Se li ometti, la funzione non viene chiamata. Si compilerà correttamente su molti sistemi, ma non farà nulla.

Parametri e codice C vecchio stile

Le funzioni C possono accettare parametri di qualsiasi tipo. Ecco una funzione che calcola il fattoriale di un numero:

Il parametro “i” viene passato come numero intero. Per passare più parametri, separarli con virgole.

C si è evoluto. Occasionalmente vedrai codice più vecchio scritto nel “vecchio stile”. Sembra questo:

Devi essere in grado di leggere questa vecchia notazione. Funziona esattamente allo stesso modo. È solo una sintassi diversa. Dovresti utilizzare il “nuovo stile”, noto come ANSI C, in cui i tipi sono dichiarati nell’elenco dei parametri. Utilizza il vecchio stile solo se sai che stai spedendo il codice a qualcuno con un compilatore non ANSI.

Perché questo è importante per gli utenti quotidiani

Potresti non scrivere funzioni C quotidianamente. Ma capire come funzionano cambia il modo in cui pensi al software. Ogni volta che si fa clic su un pulsante in un’app Web, è probabile che una funzione elabori quel clic, controlli i parametri e restituisca uno stato. Se hai mai riscontrato un “errore di runtime” o uno “stack overflow”, hai riscontrato i limiti di questi meccanismi.

La distinzione tra passare un valore e passare un riferimento (l’indirizzo di memoria) è una fonte comune di bug. Nei linguaggi di alto livello, questo è spesso nascosto. In C è esplicito. Sapere che “rand” senza parentesi restituisce un indirizzo, non un numero, fa risparmiare ore di debug.

Il dibattito tra vecchio stile e nuovo stile è in gran parte risolto per i nuovi progetti. ANSI C è lo standard. Ma esistono ancora basi di codice legacy. Saper leggere int add(i,j) int i; int j; ti impedisce di rimanere bloccato su codice che sembra sbagliato ma funziona perfettamente. È una questione di alfabetizzazione.

Lo stack è davvero importante per te? Se il tuo programma si blocca inaspettatamente, l’analisi dello stack è il tuo unico indizio. La scomparsa delle variabili locali quando toccano la parentesi graffa di chiusura non è un bug. È una caratteristica. Mantiene prevedibile l’utilizzo della memoria.

Non esiste un modo perfetto per scrivere il codice. Esiste solo il modo in cui viene compilato e il modo in cui non lo fa. Mantieni le parentesi. Guarda i tuoi rendimenti. E non dare per scontato che il compilatore ti salverà dai tuoi errori di sintassi. Non lo farà.