Remove ads
unità di organizzazione del codice Da Wikipedia, l'enciclopedia libera
In informatica e in particolare nella programmazione, una funzione è un'unità di organizzazione del codice che permette di raggruppare una sequenza di istruzioni in un unico blocco, caratterizzato da un nome, dei parametri in ingresso (detti argomenti) e uno o più dati restituiti in uscita. Si tratta effettivamente di un'evoluzione del concetto di procedura.[1]
L'importanza, la praticità e i vantaggi di una funzione stanno nel fatto che può essere "chiamata" ("richiamata", "invocata" o "attivata") in diversi punti del programma di cui fa parte ogni volta in cui si ha la necessità di farlo come se fosse una singola istruzione senza la necessità di doverne riscrivere ogni volta il relativo codice implementando dunque il cosiddetto riuso di codice, cui si aggiunge una più facile manutenibilità del codice all'interno del programma e una più facile progettazione del software secondo la classica filosofia del divide et impera.
Quasi tutti i linguaggi di programmazione supportano le funzioni, fornendo una propria sintassi per definire una funzione, ovvero scriverne il codice, e una per richiederne l'esecuzione (invocazione o chiamata della funzione). In generale la sintassi comune della definizione di una funzione prevede:
void
) la funzione avrà svolto altre elaborazioni agendo ad esempio sullo stato delle variabili del programma o visualizzando a video qualcosa senza restituire nulla. In linguaggi non tipizzati o a tipizzazione debole invece non è presente il tipo di ritorno.Una funzione è una porzione di codice che può essere invocata da qualsiasi punto di un programma. Per l'invocazione occorre tipicamente richiamarne almeno il nome passando i valori degli eventuali parametri, cui si aggiungono eventualmente altri dettagli dipendenti dal particolare linguaggio di programmazione in uso.
Generalmente i parametri di una funzione sono definiti all'atto della definizione della stessa. La loro numerosità e il loro ordine è fissato. Alcuni linguaggi forniscono sistemi più o meno eleganti per realizzare funzioni con un numero variabile di argomenti. In tal caso la funzione si dice "variadica".[2]
Nei linguaggi tipizzati, anche il tipo di dato dei parametri formali deve essere definito, e il type checking deve essere svolto anche per verificare che i parametri effettivi siano di tipo compatibile con quello dei corrispondenti parametri formali. Il tipo di un parametro può essere anche una complessa struttura dati.
Le variabili della funzione che fanno riferimento ai parametri ricevuti sono detti parametri formali. Quando una funzione viene invocata, gli identificatori associati internamente ai valori che le vengono passati sono detti parametri effettivi o parametri attuali.
Ciascuna istanza di una funzione che è in esecuzione in un certo momento possiede la propria copia dei parametri effettivi (e delle variabili locali).
Il comportamento di una funzione può essere dipendente dai dati che le sono passati come parametri all'atto dell'invocazione; inoltre una funzione può restituire un dato all'uscita.
Esistono diverse modalità di passaggio dei parametri, o di accesso alle strutture definite all'interno del programma stesso: ogni linguaggio, infatti, gestisce il proprio ambiente dipendentemente dalle regole di scoping che implementa, e di cui è necessario tenere conto.
Con questo meccanismo il valore del parametro effettivo viene copiato nella variabile della funzione chiamata che rappresenta il parametro formale. Se la funzione chiamata lo modifica, la funzione chiamante non potrà vedere questa modifica. Si tratta quindi di un passaggio unidirezionale.
Con questo meccanismo la funzione invocata riceve come parametro un puntatore o riferimento al parametro effettivo. Se modifica il parametro passato per indirizzo, la modifica sarà visibile anche alla funzione chiamante. Il passaggio è quindi potenzialmente bidirezionale.
Una funzione può restituire un valore (tipizzato) al chiamante. Questa modalità di passaggio di dati è unidirezionale, dal chiamato al chiamante.
Una chiamata di funzione è quindi un'espressione, che viene valutata per ottenere un valore. La valutazione di un'espressione che contenga una chiamata di funzione comporta l'esecuzione della funzione stessa.
In alcuni linguaggi, il termine procedura indica una funzione senza valore di ritorno, in altri si usa un tipo di dato apposito, detto void
, per il valore restituito, a significare che la funzione non restituisce alcun valore.
Nei linguaggi funzionali il tipo di dato restituito può essere una funzione, che il chiamante potrà invocare.
Una funzione può definire variabili, dette locali, che sono visibili solo durante l'esecuzione di una particolare istanza della funzione ovvero sono definite e allocate all'interno del blocco o corpo della funzione e sono deallocate al termine dell'esecuzione del blocco stesso o funzione stessa. Se più istanze di una funzione sono in esecuzione contemporaneamente, ciascuna avrà la sua copia di ciascuna variabile locale.
Il concetto di funzione è utilizzabile in tutti i linguaggi di programmazione che implementano il paradigma procedurale. La maggioranza dei linguaggi di programmazione ad alto livello supporta esplicitamente le funzioni, prevedendo costrutti sintattici dedicati o che in alternativa accorpano diverse varianti del concetto:
Alcuni linguaggi tendono a utilizzare terminologie diverse:
Nella programmazione le funzioni sono uno strumento talmente importante e diffuso da richiedere una gestione della loro esecuzione estremamente efficiente allo scopo di mantenere bassi i tempi di chiamata della funzione e di ritorno del controllo al programma chiamante. Per questo motivo la gestione dell'allocazione delle variabili locali e del passaggio dei parametri vengono normalmente supportate direttamente dall'hardware. L'esistenza dello stack nelle architetture hardware è appunto riconducibile alla necessità di supportare efficientemente le funzioni. Infatti, quando viene invocata una funzione il punto del codice in cui è stata invocata viene salvato sullo stack (indirizzo di ritorno), e anche i parametri e le variabili locali di una funzione vengono salvati sullo stack.
L'insieme di questi dati sullo stack è detto record di attivazione, e rappresenta una funzione in fase di esecuzione, che può essere sospesa in attesa del completamento di un'altra funzione che a sua volta ha invocato.
Il record di attivazione in cima allo stack è quello della funzione attualmente in esecuzione, sotto c'è quello della funzione che l'ha chiamata, e così via.
Lo stack può essere usato anche in altri modi, ad esempio per memorizzare temporaneamente valori intermedi nella valutazione delle espressioni aritmetiche.
In assembly esistono istruzioni dedicate al supporto delle funzioni e dello stack, con un corrispondente diretto nel linguaggio macchina:
PUSH
: metti un valore sullo stackPOP
: leggi e togli dallo stack un valoreJSR
: jump to subroutine, ovvero salta a una subroutine (salvando l'indirizzo di ritorno sullo stack con PUSH
)RET
: ritorno da una subroutine al chiamante (identificato eseguendo una POP
dell'indirizzo di ritorno dallo stack)Naturalmente, ciascuna funzione o pezzo di codice che usa lo stack ha la responsabilità di togliere tutto quello che ha messo sullo stack prima di terminare (e nulla più di quanto ha messo), altrimenti il valore di un parametro o di una variabile locale verrà impiegato come indirizzo di ritorno, con conseguenze impredicibili. La difficoltà di garantire manualmente questa correttezza è uno dei motivi che giustificano l'uso di linguaggi di alto livello, che tra l'altro gestiscono automaticamente la coerenza dello stack.
A causa di queste operazioni, l'invocazione di una funzione comporta un costo, seppur normalmente modesto, in termini di prestazioni.
Una funzione dovrebbe eseguire una determinata operazione o risolvere un determinato problema (o al limite tutto il problema) all'interno dell'algoritmo risolutivo, contribuendo così alla fattorizzazione del software. Ad esempio, una subroutine progettata per disporre in ordine crescente un insieme di numeri interi può essere richiamata in tutti i contesti in cui questa operazione sia utile o necessaria, e supplisce alla mancanza di una vera e propria "istruzione" dedicata allo scopo, consentendo al contempo di descrivere il corrispondente algoritmo di ordinamento in un unico punto del programma.
Le subroutine che implementano funzionalità di base spesso richiamate nel codice sorgente dal programmatore sono raccolte all'interno delle cosiddette librerie.
Il meccanismo di implementazione delle funzioni, insieme all'aritmetica dei puntatori nel linguaggio C, viene sfruttato per costruire attacchi di tipo stack overflow o buffer overflow, che permettono di prendere il controllo di un programma in esecuzione fornendogli dati accuratamente artefatti.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.