Salta al contenuto
Note per Studenti Funzioni in C

Funzioni in C

In questa pagina 7
In questa pagina 7

Definizione e prototipo

c
#include <stdio.h>

double media(int a, int b);          /* prototipo (dichiarazione) */

int main(void) {
    printf("%.1f\n", media(3, 4));   /* 3.5 */
    return 0;
}

double media(int a, int b) {         /* definizione */
    return (a + b) / 2.0;
}

Definizione (funzione C, tipo di ritorno e prototipo)

Esempio. media(3, 4) restituisce 3.5, cioè (3+4)/2,0(3+4)/2{,}0; media(3.9, 4) converte 3.9 in 3 e restituisce lo stesso valore.

Passaggio per valore

Definizione (passaggio per valore in C). In C gli argomenti sono sempre copiati nei parametri: modificare un parametro non modifica la variabile del chiamante.

Esempio. Con void incrementa(int n) { n++; }, dopo incrementa(x) la variabile x = 5 resta 5; con void incrementa(int *p) { (*p)++; } e incrementa(&x) diventa 6.

c
void incrementa(int n) {
    n++;                 /* modifica la copia locale */
}

int main(void) {
    int x = 5;
    incrementa(x);
    printf("%d\n", x);   /* 5 */
}

Per modificare una variabile del chiamante si passa il suo indirizzo:

c
void incrementa(int *p) {
    (*p)++;              /* modifica la variabile puntata */
}
/* chiamata: incrementa(&x); */

void scambia(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

Gli array sono un caso particolare: passando un array si passa l'indirizzo del primo elemento, quindi la funzione può modificarlo e non ne conosce la lunghezza, che va passata a parte (vedi Array e stringhe in CArray di dimensione fissa in memoria contigua, inizializzazione, nessun controllo sugli indici, passaggio a funzioni con la lunghezza, matrici; stringhe come array di char terminati da '\0' e funzioni di string.h.Array e stringhe in C →).

Confronto con Python, dove si passano riferimenti a oggetti: Funzioni in Pythondef e return, parametri posizionali, con nome e con valore predefinito, *args e **kwargs; passaggio per riferimento a oggetto; ambito delle variabili LEGB; docstring, type hint, lambda.Funzioni in Python →.

Ambito e durata delle variabili

Definizione (ambito e durata delle variabili in C)

Tipo di variabile Dove si dichiara Visibilità Durata
locale (automatica) dentro un blocco { } dal punto di dichiarazione alla fine del blocco dall'ingresso all'uscita del blocco (sullo stack)
parametro intestazione corpo della funzione come le locali
globale fuori da ogni funzione tutto il file (dal punto di dichiarazione) intero programma
locale static dentro una funzione con static la funzione intero programma: conserva il valore tra le chiamate

Esempio. Con static int n = 0; return ++n;, tre chiamate successive di conta_chiamate() restituiscono 1, 2, 3.

c
int conta_chiamate(void) {
    static int n = 0;    /* inizializzata una sola volta */
    return ++n;
}

Non restituire mai l'indirizzo di una variabile locale: alla fine della funzione quella memoria non esiste più (vedi Gestione della memoria in CSegmenti di memoria di un processo (codice, dati statici, stack, heap); durata delle variabili; allocazione dinamica con malloc, calloc, realloc e free; errori classici: memory leak, dangling pointer, double free, buffer overflow; strumenti di controllo.Gestione della memoria in C →).

Compilazione separata e header

Programmi più grandi si dividono in più file. Il file header .h contiene i prototipi (l'interfaccia); il .c le definizioni.

geometria.hc
#ifndef GEOMETRIA_H
#define GEOMETRIA_H

double area_cerchio(double r);

#endif
geometria.cc
#include "geometria.h"

double area_cerchio(double r) {
    return 3.141592653589793 * r * r;
}
main.cc
#include <stdio.h>
#include "geometria.h"

int main(void) {
    printf("%f\n", area_cerchio(2.0));
    return 0;
}
> gcc -Wall -o prog main.c geometria.c

Librerie standard utili

<stdio.h> I/O, <stdlib.h> (malloc, free, abs, rand, exit), <math.h> (sqrt, pow, fabs; su Linux compilare con -lm), <string.h> stringhe, <ctype.h> (isdigit, toupper), <stdbool.h>, <limits.h>.

Ricorsione

Identica a Python (RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →): ogni chiamata ha il suo record di attivazione sullo stack.

c
long long fattoriale(int n) {
    return n <= 1 ? 1 : n * fattoriale(n - 1);
}

Errori tipici

  • Chiamare una funzione prima di dichiararla.
  • Aspettarsi che una funzione modifichi un int passato per valore.
  • Funzione non void che in qualche ramo non esegue return: il valore restituito è indefinito.
  • Prototipo e definizione con tipi diversi.

Versione ripasso

Definizione e prototipo

Passaggio per valore

In C gli argomenti sono sempre copiati: modificarli non modifica la variabile del chiamante. Per modificarla si passa il suo indirizzo (es. incrementa(&x) usando int *p). Gli array passano l'indirizzo del primo elemento e possono essere modificati (vedi Array e stringhe in CArray di dimensione fissa in memoria contigua, inizializzazione, nessun controllo sugli indici, passaggio a funzioni con la lunghezza, matrici; stringhe come array di char terminati da '\0' e funzioni di string.h.Array e stringhe in C →).

Ambito e durata delle variabili

Tipo di variabile Visibilità Durata
locale / parametro blocco / corpo funzione dall'ingresso all'uscita del blocco (sullo stack)
globale tutto il file intero programma
locale static la funzione intero programma: conserva il valore tra le chiamate

Non restituire mai l'indirizzo di una variabile locale (vedi Gestione della memoria in CSegmenti di memoria di un processo (codice, dati statici, stack, heap); durata delle variabili; allocazione dinamica con malloc, calloc, realloc e free; errori classici: memory leak, dangling pointer, double free, buffer overflow; strumenti di controllo.Gestione della memoria in C →).

Compilazione separata e header

  • Il file header .h contiene i prototipi; il .c le definizioni.
  • #ifndef / #define / #endif (include guard) evita inclusioni multiple.
  • Compilazione: gcc -Wall -o prog main.c geometria.c.

Librerie standard utili

<stdio.h>, <stdlib.h>, <math.h> (compilare con -lm), <string.h>, <ctype.h>, <stdbool.h>, <limits.h>.

Ricorsione

Identica a Python (RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →), con record di attivazione sullo stack.

Errori tipici

  • Chiamare una funzione prima di dichiararla.
  • Aspettarsi che una funzione modifichi un int passato per valore.
  • Funzione non void senza return in qualche ramo.
  • Prototipo e definizione con tipi diversi.

Teoria collegata