Menu

Les fonctions en C : déclarer, appeler et renvoyer des valeurs

Comment définir et appeler des fonctions en C - le type de retour, les paramètres, les valeurs de retour, les fonctions void, pourquoi la décomposition compte, et un exemple complet construit à partir de petits morceaux nommés.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Pourquoi des fonctions

Tout ce qui précède vivait dans main. Cela fonctionne jusqu'à ce qu'un programme fasse plus d'une chose, et trois problèmes arrivent alors d'un coup : le même bloc de code est recopié à trois endroits, main dépasse ce que quiconque peut garder en tête, et il n'y a aucun moyen de nommer ce à quoi sert un morceau de code.

Une fonction règle les trois. Elle donne à un bloc de code un nom, ses propres variables locales, une liste d'entrées, et une seule valeur à rendre.

square est écrite une fois et appelée trois fois, dont une sur son propre résultat. Vous appeliez déjà des fonctions - printf en est une, et main aussi.

L'anatomie d'une fonction

typeRetour nom(listeParametres) {
    // corps
    return valeur;
}

Quatre parties :

  • Le type de retour - le type de la valeur que la fonction rend. int, double, char, un type pointeur, ou void pour « rien ».
  • Le nom - il suit les mêmes règles qu'un nom de variable, et devrait dire ce que la fonction fait ou produit.
  • La liste de paramètres - les entrées, chacune avec son type, séparées par des virgules. (void) signifie qu'elle n'en prend aucune.
  • Le corps - les instructions, entre accolades, se terminant par un return sauf si le type de retour est void.

Notez que pi() a toujours besoin des parenthèses vides au point d'appel - c'est ce qui en fait un appel plutôt qu'une mention du nom. Et printBanner(20); est une instruction à part entière, car elle ne produit aucune valeur à utiliser.

Écrivez (void), pas (), pour une fonction sans paramètre. En C, ces deux formes signifient des choses différentes, et () est la forme laxiste - nous y venons.

Appeler et retourner

Un appel transfère le contrôle dans la fonction, exécute son corps, et revient avec une valeur qui remplace l'appel dans l'expression environnante.

return fait deux choses à la fois : il fournit la valeur et il termine la fonction sur place. Tout ce qui suit un return sur le même chemin ne s'exécute jamais, c'est ce qui fait fonctionner le style de retour anticipé de larger - aucun else n'est nécessaire, car atteindre la dernière ligne signifie déjà que a > b était faux.

Une fonction peut avoir plusieurs instructions return. Elle ne peut en exécuter qu'une par appel.

Les fonctions void

Une fonction qui existe pour son effet plutôt que pour sa valeur a le type de retour void : afficher, mettre à jour quelque chose à travers un pointeur, dessiner.

Dans une fonction void, return; sans valeur sort par anticipation. return uneValeur; y est une erreur de compilation, tout comme utiliser l'appel dans une expression - int x = printTable(7, 5); ne compilera pas, ce qui est le système de types faisant son travail.

La décomposition : le vrai enjeu

Les fonctions ne servent pas principalement à éviter la répétition. Elles servent à transformer une longue procédure en quelques étapes nommées, pour que main se lise comme une description du programme plutôt que comme son implémentation.

main fait maintenant quatre lignes d'affichage et un calcul. Chaque fonction auxiliaire fait une chose, a un nom qui dit laquelle, et peut être testée isolément. Remarquez que la longueur du tableau voyage en second paramètre : une fois un tableau passé à une fonction, l'astuce sizeof n'y fonctionne plus, car le tableau se dégrade en pointeur.

Trois règles empiriques pour découper le travail en fonctions :

  • Un travail par fonction. Si le nom a besoin d'un « et », c'est probablement deux fonctions.
  • Prenez ce dont vous avez besoin, renvoyez ce que vous produisez. Une fonction qui lit un état global est plus difficile à comprendre et impossible à tester isolément.
  • Assez courte pour être vue d'un coup. Il n'y a pas de nombre de lignes magique, mais une fonction qui ne tient pas sur un écran cache généralement une fonction plus petite en elle.

Des fonctions qui appellent des fonctions

Une fonction peut appeler toute fonction déjà déclarée, y compris celles que vous avez écrites :

main appelle report, qui appelle sumEvens, qui appelle isEven dans une boucle. Chaque appel obtient son propre jeu neuf de variables locales, empilé au-dessus de celui de l'appelant, et elles sont jetées au retour.

Les définitions ci-dessus sont dans l'ordre des dépendances - isEven avant sumEvens avant report - car le C exige qu'une fonction soit déclarée avant d'être appelée. Appelez-en une qui apparaît plus bas dans le fichier et le compilateur se plaint d'une déclaration implicite. La solution générale est un prototype près du début du fichier, qui vous laisse ordonner les définitions comme vous voulez. Une fonction qui s'appelle elle-même est légale et utile aussi ; c'est la récursion.

main est une fonction

main est une fonction ordinaire que l'environnement d'exécution appelle pour vous. Son type de retour int est le code de sortie du programme : 0 signifie succès, non nul signifie échec, et les scripts shell et outils de construction le lisent.

int main(void) {
    /* ... */
    return 0;
}

Depuis C99, tomber au bout de main sans return renvoie implicitement 0 - une règle spéciale qui ne s'applique qu'à main. Toute autre fonction non-void qui se termine sans renvoyer de valeur produit un comportement indéfini si l'appelant utilise le résultat.

L'autre forme standard est int main(int argc, char *argv[]), qui reçoit les arguments de ligne de commande. Les deux sont correctes ; void main() ne l'est pas, quoi qu'en disent de vieux tutoriels.

Erreurs courantes

  • Oublier return dans une fonction non void. L'appelant lit alors une valeur aberrante. gcc -Wall avertit.
  • Déclarer dans le corps une variable du même nom qu'un paramètre. Elle masque le paramètre, et l'affectation que vous vouliez faire à l'entrée ne va nulle part.
  • Attendre d'une fonction qu'elle modifie ses arguments. Le C passe tout par valeur, une fonction reçoit donc des copies. void reset(int x) { x = 0; } ne change rien au point d'appel - le sujet de la page suivante.
  • Omettre les parenthèses dans un appel. printBanner; est une expression légale qui évalue l'adresse de la fonction et la jette. Cela compile, ne fait rien, et -Wall le signale.
  • Écrire () au lieu de (void). Légal, mais cela désactive la vérification des arguments dans les anciens dialectes du C.

Questions fréquentes

Comment déclare-t-on une fonction en C ?

Écrivez le type de retour, le nom et une liste de paramètres entre parenthèses, puis le corps entre accolades : int add(int a, int b) { return a + b; }. Une fonction qui ne prend rien utilise (void), et une qui ne renvoie rien a le type de retour void.

Que fait return dans une fonction C ?

Il termine la fonction immédiatement et rend une valeur à l'appelant. Le type de la valeur doit correspondre au type de retour déclaré de la fonction. Dans une fonction void, vous pouvez écrire un simple return; pour sortir tôt, ou l'omettre et laisser la fonction se terminer à l'accolade fermante.

Pourquoi main renvoie-t-il un int en C ?

La valeur de retour de main est le code de sortie du programme, que le système d'exploitation et les scripts shell peuvent lire. return 0; signifie succès et toute valeur non nulle signifie échec. Depuis C99, atteindre la fin de main sans instruction return renvoie implicitement 0.

Une fonction C peut-elle renvoyer plus d'une valeur ?

Pas directement - return produit exactement une valeur. Les contournements habituels sont de renvoyer une structure contenant plusieurs champs, ou de passer des pointeurs en paramètres pour que la fonction y écrive ses résultats supplémentaires.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER