CPGE Oujda                                                                                                                       Sup

Les fonctions

 

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.

int f(int x)
{
    return x*x - 3;
}

Voyons maintenant un exemple d'utilisation de cette fonction.

#include <stdio.h>
int f(int); /* declaration de la fonction f ou prototype*/
int main()
{
    int x = 4;
    printf("f(%d) = %d\n", x, f(x));
    return 0;
}
int f(int x)
{
    return x*x - 3;
}
La ligne : int f(int);

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.

 Remarque :

Arguments des fonctions

Passage des arguments

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.

 

Arguments de type tableau

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

int strlen(chaine de caractères s) {
               int i = 0;
               while (s[i] != 0)
                               i++;
               return i;
}

peut indifféremment être concrétisée de l'une des trois manières suivantes :

int strlen(char s[80])
int strlen(char s[])
int strlen(char *s)   

Dans les trois cas, si t est un tableau de char et p l'adresse d'un char, les trois appels suivants sont corrects :

l1 = strlen(t);
l2 = strlen(p);
l3 = strlen("Bonjour");

A retenir : parce que les arguments effectifs sont passés par valeur, les tableaux sont passés par adresse. Passage par valeur des tableaux.

 

 

Arguments par adresse

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

int quotient(a, b)

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 :

 
int quotient(int *a, int b) {
               int r = *a / b;
               *a = *a % b;
               return r;
}

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

z = quotient(&x, y);

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

f(int a) {
               ...
}
g(int *b) {
               ...
}
h(int c, int *d) {
               ...
}

A l'intérieur de la fonction h, des appels corrects de f et g sont :

f(c);
f(*d);
g(&c);
g(d);

cette dernière expression correspondant, bien s^ur, à g(&*d).