Nous avons déjà utilisé des fonctions (printf, scanf, cos, sin, srand,…) définies dans des bibliothèques (stdio.h,math.h,stdlib.h). Si l'on ouvre le fichier de la bibliothèque math.h par exemple, on retrouve les définitions ou prototypes de ces fonctions. La définition d'une fonction permet de connaitre le nombre de paramètres et leur type ainsi que le type de la variable renvoyée. Ainsi pour la fonction cosinus définie par double cos(double x), (double x) est l'argument ou le paramètre de la fonction et double (devant cos) est le type de la variable qui sera renvoyée. Le code de la fonction cos est définie dans la librairie des fonctions fournies par le compilateur et seule la définition de cos apparait dans math.h.
Dans l'exemple ci-dessous, que nous avons déjà vu, il est nécessaire d'inclure les définitions des fonctions utilisées dans le main (ici un programme qui fait 5 tirages aléatoires de valeurs comprises entre 1 et 10 et calcule le cosinus de 2π/n) :
printf est défini dans stdio.h
time est défini dans time.h
srand est défini dans stdlib.h
cos est défini dans math.h
_USE_MATH_DEFINES est utilisé pour pouvoir utiliser la macro (ou constante) M_PI
#include<stdio.h>
#include<time.h>
#define_USE_MATH_DEFINES
#include<math.h>
#include<stdlib.h>
int main(){
srand(time(NULL));
for (int i = 0; i < 5; i++) {
int n = rand() % 10+1;
double valeur = cos(2 * M_PI / n);
printf("cos(2*PI/%d)=%.2lf\n", n, valeur);
}
return 0;
}
Le fichier main.c est compilé en intégrant les définitions des bibliothèques puis est assemblé avec le code déjà compilé des fonctions de la librairie C pour générer l'exécutable. L'assemblage des différents codes est fait par le linker. Nous reviendrons en détail sur ces concepts dans un futur chapitre.
3. Premiers exemples de fonction
Prenons un premier exemple de fonction permettant de calculer la somme de 2 entiers. Cette fonction va prendre 2 paramètres (ou arguments) de type int et renvoyer le résultat de type int.
Nous avons jusqu'à présent utilisé des fonctions de la bibliothèque C dans la fonction main (code du programme), c'est à dire que nous avons vu par exemple le prototype ou la définition de la fonction cosinus et nous avons appelé cette fonction dans le main. Nous allons ici, en plus de déclarer la fonction somme et de l'appeler dans le main, écrire son code .
Nous avons donc dans notre fichier main.c 3 éléments distincts :
la déclaration de la fonction somme (ou le prototype de la fonction somme en jaune)
le code du main avec 3 appels de cette fonction qui renvoie dans la variable n (de la fonction main) la somme des 2 paramètres (en vert)
le code de la fonction somme avec le mot clé return qui renvoie la somme des 2 variables passées en arguments (en bleu)
La déclaration des fonctions (comme dans le code ci-dessous) sera placée avant la fonction main et le code des fonctions aprés la fonction main.
int somme(int, int);// déclaration ou prototype de la fonction
int main() {
int n;
n = somme(2, 3); // appel de la fonction, n prend la valeur renvoyée par la fonction
n = somme(n, 4);
n = somme(n, n);
printf("n=%d", n);
return 0;
}
int somme(intia, intib) // code de la fonction
{
return (ia + ib);
}
Prenons un deuxième exemple : une fonction qui affiche une valeur mais qui ne renvoie aucune valeur vers le main. C'est un deuxième type de fonction moins courant mais qui peut exister (en général une fonction renvoie toujours une valeur).
On peut voir un nouveau mot clé void qui veut dire rien, donc la fonction aff_ligne ne renvoie aucune valeur à la fonction main, et donc l'appel de la fonction aff_ligne se fait sans valeur de retour.
voidaff_ligne(int);
int main() {
aff_ligne(3);
printf("\n");
aff_ligne(2);
printf("\n");
aff_ligne(1);
}
voidaff_ligne(intn) {
for (inti = 0; i < n; i++)
printf("*");
}
Quizz
4. Les règles d'écritures de fonctions
Une fonction dans la grande majorité des cas doit renvoyer une valeur. Nous avons vu qu'il existait le mot clé void lorsque la fonction ne renvoie rien à la fonction principale, mais nous n'utiliserons que trés peu cette solution. Dans l'exemple ci-dessous, dans la colonne de gauche, le programme carre calcule et affiche dans la fonction le résultat du calcul alors que le programme de la colonne de droite appelle la fonction carre qui calcule le carre et c'est dans le main que l'affichage est effectué.
Code à proscrire (affichage dans la fonction)
Code correct (affichage dans le main)
void carre(int);
int main() {
int n;
carre(2);
return 0;
}
void carre(intn) {
int valeur;
valeur = n * n;
printf("n=%d\n", valeur);// interdit
}
int carre(int );
int main() {
int n;
n = carre(2);
printf("n=%d\n", n);
return 0;
}
int carre(intn) {
return (n * n); // une fonction doit renvoyer une valeur
}
Quizz
5. Portée d'une variable
En C depuis la mise à jour C99 (1999) il est possible de déclarer une variable n'importe ou dans le code. Dans l'exemple de la colonne de gauche, la variable i est définie au moment de l'exécution de la boucle for. Cette solution est possible, mais attention, les variables ne sont visibles qu'a l'intérieur des accolades c'est à dire ici à l'intérieur de la boucle for, l'affichage de la valeur finale de i est donc impossible (erreur de compilation). Pour la colonne de droite, la variable i est définie en amont de la boucle for et donc la variable i est disponible pour l'affichage à la sortie de la boucle for.
Erreur de compilation i n'est visible qu'à l'intérieur de la boucle for
i est définie à l'extérieur de la boucle for : code correct
for (int i = 0; i < 10; i++) {
printf("%d ", i);
}
printf("Valeur de i=%d", i);//erreur de compilation
int i;
for (i = 0; i < 10; i++) {
printf("%d ", i);
}
printf("Valeur de i=%d", i);
Pour les fonctions c'est le même principe, si l'on crée 2 variables avec le même nom dans la fonction main et dans la fonction divise, ces valeurs étant visible qu'à l'intérieur des accolades (donc des fonctions) elles sont donc différentes, même si elles ont le même nom.
Pour les fonctions, on parle de variables locales à la fonction (paramètres de la fonction et variables définies dans la fonction). Et donc si une variable est définie à l'extérieur des fonctions, on parle de variables globales, puisque ces variables globales sont à l'extérieurs des accolades, elles sont donc visibles par toutes les fonctions. On utilisera ces variables globales seulement pour des cas particuliers, l'utilisation des variables locales étant la norme.
Variables locales
Variables globales
Quizz
6. Code de la fonction avant le main ?
Il est possible de placer le code des fonctions avant le main, dans ce cas, il n'est pas nécessaire de placer le prototype de la fonction puisque le code de la fonction est suffisant pour compiler le code. Rappelons en effet que le processus de compilation se fait du début du fichier jusqu'à la fin du fichier et donc lors de l'appel de la fonction, le compilateur a besoin de connaitre au minimum la définition de la fonction (si le code est aprés la fonction main), mais si le code est placé avant la fonction main alors le compilateur n'a plus besoin de la définition.