Académique Documents
Professionnel Documents
Culture Documents
Nous y trouvons tout d’abord un programme principal formé d’un bloc (limité ici à une seule
instruction). Mais cette fois à sa suite apparaît la définition d’une fonction.
Celle-ci comporte :
• un “en-tête“ qui précise le nom de la fonction (ici optimiste). Bien que cette fonction ne
comporte aucun argument, les parenthèses qui suivent son nom sont obligatoires (comme elles
l’étaient pour le nom main). Notez bien qu’un point-virgule n’apparaît pas à la fin de l’en-tête.
• un “corps” matérialisé par un bloc dans Lequel se trouvent les instruction que l’on souhaite voir
exécutées par la fonction lorsqu’elle sera “appelée”
Ici, nous avons placé la fonction à la suite du programme principal, mais nous aurions pu la placer
avant.
D’autre part, il faut noter que les fonctions sont toujours indépendantes les unes des autres. Aucune
fonction ne peut être contenue dans une autre.
Au sein du programme principal, nous trouvons l’appel de la fonction. Il se fait par son nom suivi
d’une paire de parenthèses obligatoires (même lorsque la fonction ne comporte aucun argument).
Vous voyez que le programme principal apparaît finalement comme une fonction de nom (main)
imposé. La seule différence est que, par convention, l’exécution d’un programme commence
toujours par cette fonction nommée main. Cette dernière n’est donc exécutée qu’une seule fois,
alors que les autres fonctions peuvent l’être un nombre quelconque de fois.
VI.3. LES ARGUMENTS D’UNE FONCTION
Soit l’exemple suivant :
void main ()
{
int a =10, b=20;
ecris(a) ; valeur: 10
ecris(b) ; valeur: 20
ecris(a+b) ; valeur: .30
}
void ecris(int n)
{
printf (“valeur :%d \n“, n) ;
}
Dans la définition de la fonction ecris, nous trouvons l’en-tête : ecris(int n)
Cette fois, en plus du nom de la fonction, nous y trouvons une indication signifiant que cette fonction
possède un argument de type int nommé n. Cela signifie que lorsque cette fonction sera appelée,
on lui transmettra une valeur de type int. Quant à l’usage que ecris doit faire de cette valeur, celui-
ci lui est spécifié au sein des instructions du corps de la fonction, sachant que c’est l’identificateur n
qui désigne la valeur en question.
Le symbole n n’a de signification qu’au sein de la fonction ecris et il n’a aucun rapport avec
d’éventuelles variables de même nom qui pourraient être définis en dehors. On dit souvent que n est
un argument muet ou encore un argument formel.
En ce qui concerne l’utilisation de notre fonction ecris dans le programme principal, vous constatez
que nous faisons suivre son nom d’une expression de type int, placée entre parenthèses. Cette
expression porte le nom d’argument effectif. C’est sa valeur qui sera effectivement transmise à la
fonction lors de l’appel.
En C, un argument effectif porte la forme de n’importe quelle expression. Au contraire un argument
muet ne peut être qu’un identificateur de variable
D’une façon générale, une fonction peut avoir plusieurs arguments séparés par des virgules. Voici un
exemple incomplet de définition et d’utilisation d’une fonction nommée fct possédant trois
arguments.
void main
{
int n, p ;
double x,y;
…
fct (n, p,x) ;
…
fct (n+p, 5 *n,x-y);
…
}
fct (int i, int j, double v)
{…}
Dans ce cas, la correspondance entre argument effectifs et argument formels se fait en fonction de leur
ordre d’apparition dans la liste.
Remarque : Dans le C tel qu’il est défini par K&R, la déclaration de notre fonction ecris se présente de la
façon suivante :
ecris (n)
int n ;
{
…
}
L’entête est découpée en deux parties :
• une première ligne précisant le nom de la fonction et la liste des arguments formels.
• une suite de déclarations précisant les types des arguments.
VI.4. LES FONCTIONS FOURNISSANT UN RESULTAT
VI.4.a. Exemple de fonction calculant la somme de deux valeurs
soit cet exemple :
void main() {
int a, b, c, d, x, y;
int som() ;
a=1; b=2; c=S; d=4;
x = som(a, b) + 5 ; x=8
printf( “r = %d \n”, x); y = 21
y = 3*som(c, d) ;
printf( “y =%d \n“, y) ;
}
int som (int u, int v)
{
int s;
s = u+v
return(s)
}
L’entête de la fonction précise toujours la liste des arguments formels avec leurs types ; mais de plus,
ici, on y trouve (au début) l’indication du type du résultat que fournira la fonction.
Par ailleurs, dans le corps de la fonction on trouve une instruction : return (s);
qui spécifie le résultat qui sera fournit par la fonction lors de son appel.
L’utilisation de la fonction som au sein du programme se fait toujours en faisant suivre son nom d’une
liste d’arguments effectifs (ici deux arguments), comme par exemple : som (a, b)
Comme la fonction fournit un résultat, une telle notation désigne effectivement une expression qu’il
est possible de l’utiliser à son tour dans une expression plus complète, comme nous l’avons fait dans
l’expression : x= som (a, b) + 5;
De plus vous constatez la présence dans le programme principal d’une déclaration supplémentaire :
int som();
Cette déclaration précise au compilateur que som est une fonction et qu’elle fournit un résultat de
type int. Nous reviendrons en détail sur la justification de cette déclaration dans la suite de cette
leçon.
VI.4.b. L’instruction return
D’une manière générale :
• L’instruction return peut mentionner n’importe quelle expression. Ainsi nous aurions pu écrire la
fonction précédente d’une manière plus simple : int som (int x, int y)
{
return (u+v);
}
• L’instruction return peut apparaître à plusieurs reprises dans une fonction, comme dans cet
exemple :
double absom(double u, double v) // va leur absolue de la somme
{
double s;
s = a +b;
if (s>= 0)
return (s),
else
return (-s);
}
il faut noter que non seulement l’instruction return définit la valeur du résultat, mais en même temps
elle interrompt l’exécution de la fonction en revenant à la fonction (y compris la fonction principale
main)qui l’a appelée. En l’absence de l’instruction return ce retour est mis en place
automatiquement par le compilateur à la fin de la fonction.
VI.4.c. Quand on ne déclare pas le type du résultat
Dans notre exemple du paragraphe “fonctions fournissant un résultat”, nous avons placé, dans le
programme principal, une déclaration précisant le type de la fonction som. On se demande pourquoi,
puisque la définition de cette même fonction som figure dans le même ficher source. En fait, il faut
savoir que le compilateur travaille d’une manière relativement séquentielle. Plus précisément, vous ne
pouvez pas faire référence à “quelque chose qui n’est définie que plus tard dans le code source. C’est
pourquoi la déclaration en question est indispensable ici.
Par contre, si, comme l’autorise C, vous aviez placé la définition de la fonction avant le programme
principal, vous auriez pu omettre cette déclaration; en effet le compilateur aura disposé de
l’information de type de la fonction som.
Dune manière générale lorsque le compilateur rencontre l’appel d’une fonction, deux cas peuvent se
présenter :
• Il connaît le type de la fonction, soit parce qu’il la déjà compilé au sein du même code source, soit
qu’il existe une déclaration appropriée. Aucun problème particulier ne se pose alors.
• Il ne connaît pas le type de la fonction. Dans ce cas il ne fournit pas de message d’erreur ; se
contente de lui attribuer, par défaut, le type int.
Cette dernière particularité peut être gênante lorsque la fonction se trouve fournie ultérieurement
avec un type différent de int. En effet le compilateur découvrira une incohérence entre le type qu’il
avait attribué par défaut et le type effectif déclaré dans l’en-tête de la fonction.
int n ;
float x;
fct1(….)
{ … }
fct2(….)
{ … }
Les variables n et x sont accessibles aux fonctions fct1 et fct2, mais pas au programme principal.
Classe d’allocation
D’une manière générale, les variables globales existent pendant toute l’exécution du programme
dans lequel elles apparaissent. Leurs emplacement mémoires sont complètement définis lors de
l’édition des liens. On dit quelles font partie de la “classe d’allocation statique“
De plus, Ces variables sont initialisées à zéro avant le début de l’exécution du programme, sauf bien
sûr, si vous les initialisez explicitement lors de leur déclaration.
VI.10. LES CLASSES D’ALLOCATION DES VARIABLES - LEUR PORTEE ET LEUR INITIALISATION
VI.10.a. La portée des variables
On peut classer les variables en quatre catégories en fonction de leur portée (ou espace de validité).
i- Les variables globales
Elles sont accessibles depuis n’importe quel endroit du fichier source faisant suite à l’emplacement où
elles ont été définies. Elles sont également accessible depuis un autre fichier source ou depuis une autre
partie du fichier source qui précède l’endroit où elles sont définies, par l’intermédiaire de la déclaration
extern.
ii- Les variables globales cachées
Ce sont des variables globales faisant l’objet d’une déclaration static. Elles ne sont accessible que
depuis le fichier source où elles ont été définies.
iii- Les variables locales à une fonction
N’oubliez pas que les variables locales au programme principal (main) entrent dans cette catégorie.
iv- Les variables locales à un bloc
Le langage C permet de déclarer des variables au début d’un bloc de la même façon qu’au début
d’une fonction. Dans ce cas la portée de telles variables est limitée au bloc en question.
VI.10.b. Les classes d’allocation des variables
Il est également possible de classer les variables en trois catégories en fonction de leur classe
d’allocation
i- La classe static
On trouve dans cette catégorie les variables globales et les variables locales faisant l’objet d’une
déclaration static. Les emplacements mémoire correspondants sont alloués une fois pour toutes au
moment de l’édition de lien.
ii- La classe automatique
Par défaut les variables locales entrent dans cette catégorie. Les emplacements mémoire
correspondants sont alloués à chaque entrée dans la fonction et ils sont libérés à chaque sortie.
iii- La classe register
Toute variable entrant à priori dans la classe automatique peut être déclarée explicitement par le qualificatif
register. Celui-ci demande au compilateur d’utiliser dans la mesure du possible, un registre pour y ranger la
variable ceci peut amener quelques gains de temps d’exécution.
Cette possibilité ne peut s’appliquer qu’aux variables scalaires.
VI.10.c. Le cas des fonctions
La fonction est considérée par le langage C comme un “objet global”. c’est ce qui permet d’ailleurs à l’éditeur
de lien d’effectuer correctement son travail. Il n’est pas nécessaire d’utiliser une déclaration extern pour les
fonction définies dans un fichier source différent de celui où elles sont appelées.
VI.10.d. Initialisation des variables
Les variables de classe statique
Ces variables sont permanentes. Elles sont initialisées une seule fois avant le début de l’exécution du programme.
Elles peuvent être initialisées explicitement lors de leur déclaration. Les valeurs servant à cette initialisation ne
peuvent être que des constantes. En l’absence d’initialisation explicite, Ces variables seront initialisées à ’’zéro ’’.
Les variables de classe automatique
Ces variables ne sont pas initialisées par défaut. Par contre, comme les variables de classe statique, elles peuvent
être initialisées explicitement lors de leur déclaration.