les
fonctions sont les briques avec lesquelles on construit un programme en langage
C. La vie d'un programmeur C se résume donc à créer puis utiliser des fonctions
Nous allons donc maintenant voir de plus près ce que c'est qu'une fonction.
En mathématiques, on définit une fonction comme suit :
f(x) = x2 - 3
Cela signifie que f est une fonction qui reçoit en argument
un réel x et qui retourne un réel: x2-3.
Ecrivons une fonction C que nous appellerons f, qui reçoit en argument un
entier x et qui retourne également un entier : x2 - 3.
|
Voyons
maintenant un exemple d'utilisation de cette fonction.
|
La ligne : |
est
ce qu'on appelle une déclaration (ou le prototype de la fonction f).
Elle dit : f est une fonction qui nécessite en argument un int et qui retourne un int. Le %d dans la
fonction printf est ce qu'on appelle un spécificateur
de format. Elle renseigne sur la manière dont nous souhaitons afficher le
texte. Ici, on veut afficher les nombres 4 et 13 (f(4)). Nous disons donc à printf d'utiliser le format « nombre entier » (%d) pour les
afficher. Le premier %d correspond au format que nous voulons utiliser pour
afficher x et le deuxième pour f(x).
Sachez également que la variable x dans la fonction main n'a absolument rien à
voir avec la variable x en paramètre de la fonction f. Chaque fonction peut
avoir ses propres variables et ignore complètement ce qui se passe chez les
autres fonctions.
L'idée
maitresse est qu'en C le passage des arguments des
fonctions se fait toujours par valeur. Après avoir fait les
conversions opportunes la valeur de chaque argument effectif est affectée à
l'argument formel correspondant. Si l'argument effectif est d'un type simple
(nombre, pointeur) ou d'un type struct ou union, sa
valeur est recopiée dans l'argument formel correspondant, quelle que soit sa
taille, c'est-à-dire quel que soit le nombre d'octets qu'il faut recopier.
Apparaissant
dans la partie exécutable d'un programme, le nom d'un tableau, et plus
généralement toute expression déclarée « tableau de T », est considérée comme
ayant pour type « adresse d'un T » et pour valeur l'adresse du premier élément
du tableau. Cela ne fait pas intervenir la taille effective du tableau. Ainsi
lorsqu'un argument effectif est un tableau, l'objet effectivement passé à la
fonction est uniquement l'adresse du premier élément et il suffit que l'espace
réservé dans la fonction pour un tel argument ait la taille, fixe, d'un
pointeur.
D'autre
part, en C il n'est jamais vérifié que les indices des tableaux appartiennent
bien à l'intervalle 0:::N ¡ 1 déterminé par le nombre d'éléments indiqué dans
la déclaration. Il en découle la propriété suivante :
Dans
la déclaration d'un argument formel t de type « tableau de T » :
Exemple.
L'en-tête « vague » de la fonction suivante
|
peut
indifféremment être concrétisée de l'une des trois manières suivantes :
|
Dans
les trois cas, si t est un tableau de char et p l'adresse d'un char, les trois
appels suivants sont corrects :
|
A
retenir : parce que les arguments effectifs sont passés par valeur, les
tableaux sont passés par adresse. Passage par valeur des tableaux.
|
Les
arguments qui sont d'un type simple (char, int,
pointeur...) ou bien des struc ou des union sont
toujours passés par valeur. Lorsque l'argument effectif est une variable, ce
mécanisme empêche qu'elle puisse être modifiée par la fonction appelée. Or une
telle modification est parfois souhaitable ; elle requiert que la fonction
puisse accéder non seulement à la valeur de la variable, mais aussi à son
adresse.
C'est
ce qui s'appelle le passage par adresse des arguments. Alors que certains
langages le prennent en charge, C ne fournit aucun mécanisme spécifique : le
passage de l'adresse de l'argument doit être programmé explicitement en
utilisant comme argument un pointeur vers la variable.
Par
exemple, supposons avoir à écrire une fonction
|
censée
renvoyer le quotient de la division euclidienne de l'entier a par l'entier b et
devant en plus remplacer a par le reste de cette division. On devra la définir
:
|
Ci-dessus,
b représente un entier ; a l'adresse d'un entier. La notation *a fait référence
à une variable (ou, plus généralement, une lvalue)
appartenant à la procédure appelante.
L'argument
effectif correspondant à un tel argument formel doit être une adresse. Souvent,
cela est obtenu par l'utilisation de l'opérateur &. Par exemple, si x, y et
z sont des variables entières, un appel correct de la fonction précédente sera
|
Bien
entendu, l'opérateur & ne doit pas être utilisé si l'argument effectif est
déjà une adresse. Le schéma suivant résume les principales situations
possibles. Soient les fonctions
|
A
l'intérieur de la fonction h, des appels corrects de f et g sont :
|
cette
dernière expression correspondant, bien s^ur, à
g(&*d).