Vous êtes sur la page 1sur 24

Fonctions et fichiers

Laurent SARRY
laurent.sarry@uca.fr
Quizz Wooclap #2
Introduction aux fonctions
• Une fonction est une séquence réutilisable de commandes
définie pour réaliser une tâche spécifique
• La fonction principale du programme est main
• La bibliothèque standard du C++ comporte de nombreuses fonctions
prédéfinies
• L’utilisateur peut définir ses propres fonctions
• Une fonction est appelée au sein d’une autre fonction dite
appelante : l’exécution est interrompue et les commandes
sont exécutées séquentiellement jusqu’au retour de la
fonction appelée
Introduction aux fonctions
• La déclaration d’une fonction est de la forme :

type-de-retour nomFonction()
{
// Corps de la fonction
}
Introduction aux fonctions
#include <iostream> // pour std::cout

// Définition de la fonction print()


void print() // fonction appelée Début main()
{ Dans print()
std::cout << "Dans print()\n";
} Fin main()

// Définition de la fonction main()


int main()
{
std::cout << "Début main()\n";
print(); // Interruption de main() par un appel à la fonction print()
// main() est la fonction appelante
std::cout << "Fin main()\n"; // exécuté à la fin de l'appel de print()
return 0;
}
Introduction aux fonctions
• Une fonction peut être appelée plusieurs fois de suite
• Une fonction peut être appelée dans une autre fonction, elle-
même appelée dans une autre fonction…
Type de retour
• Une fonction peut retourner une valeur ou pas
• Dans le premier cas, le type de retour doit être spécifié et une
commande de retour (return) donne la valeur retournée
correspondant au type spécifié
#include <iostream> int main()
{
int saisieValeur() // cette fonction int num { saisieValeur() }; // initialise num
{ // retourne un entier // avec la valeur retournée par saisieValeur
std::cout << "Entrer un entier: ";
int input{}; std::cout << "Le double de " << num << " est : "
std::cin >> input; << num * 2 << '\n';

return input; // retourne la valeur return 0;


} // à la fonction appelante }
Paramètres et arguments de fonction
• Pour passer de l’information à une fonction, il est possible
d’utiliser un ou plusieurs paramètres
• Un paramètre de fonction est une variable utilisée dans une
fonction, dont la valeur (argument) est initialisée par la
fonction appelante
• Il est possible de donner à des paramètres et des arguments
les mêmes identifiants
Paramètres et arguments de fonction
#include <iostream> int main()
{
int saisieValeur() int num { saisieValeur() };
{ afficheDouble(num);
std::cout << "Entrer un entier: "; return 0;
int input{}; }
std::cin >> input;
return input;
}

void afficheDouble(int valeur)


{
std::cout << "Le double de " << valeur << " est : " << valeur * 2 << '\n';
}
Question #1
Portée locale
• Les paramètres de fonction et les variables définies dans le
corps d’une fonction sont des variables locales
• Leur portée s’étend depuis leur définition, jusqu’à l’accolade
de fermeture du bloc dans lesquelles elles sont définies
• En pratique il est préférable de déclarer les variables au plus
près de leur utilisation
Portée locale
#include <iostream>

int somme(int x, int y) // x et y sont créés, début de portée


{
// x et y ne sont utilisables que dans la fonction
return x + y;
} // fin de la portée, x et y sont détruits

int main()
{
int a{ 5 }; // a créée, initialisée, début de portée
int b{ 6 }; // b créée, initialisée, début de portée

// a et b ne sont utilisables que dans la fonction


std::cout << somme(a, b) << '\n'; // appel de somme avec x=5 et y=6

return 0;
} // fin de la portée, a et b sont détruites
Question #2
Déclarations et définitions anticipées
• Une fonction ne peut pas être appelée si elle n’a pas été
définie ou au moins déclarée avant l’appel
• Une déclaration consiste à donner le prototype de la fonction (nom,
type de retour, type des paramètres). Elle doit ensuite être définie et
pourra l’être après l’appel de la fonction.
• Une définition consiste à implémenter le corps de la fonction. Une
fonction ne peut être définie qu’une seule fois
• dans un fichier source (sinon erreur de compilation)
• dans un programme constitué de plusieurs fichiers (sinon erreur de linker)
Déclarations et définitions anticipées
#include <iostream>

// les identificateurs de paramètres ne sont pas utiles : int somme(int, int);


int somme(int x, int y); // déclaration anticipée de somme

int main()
{
int a{ 5 };
int b{ 6 };

std::cout << somme(a, b) << '\n'; // la fonction somme est connue

return 0;
}

int somme(int x, int y)


{
return x + y;
}
Question #3
Programme avec plusieurs fichiers source
• Suivre les instructions sur l’ENT pour utiliser Visual Studio
Code sur les ordinateurs de l’EUPI
• Créer un projet comportant deux fichiers sources main.cpp et
somme.cpp pour implémenter le programme précédent
• Mettre la définition de la fonction somme dans le fichier source
somme.cpp
• Faire la même chose sous Google Colab avec g++
Collision de nommage et namespace
• Si deux fonctions sont définies avec le même prototype dans le
même fichier ou dans des fichiers séparés, il y aura un conflit
détecté lors de l’édition de lien
• On peut définir des espaces de nommage (namespace) distincts
pour éviter les conflits
• L’espace de nommage std est associé à la librairie standard du
C++. Pour accéder aux identificateurs, il est possible d’utiliser:
• le qualificateur d’alias :: d’espace de nommage (préfixe std::)
• la directive using namespace (pas de préfixe, déconseillé)
• Exemple : std::cout vs. cout
Préprocesseur
• Avant d’être compilé, le code source passe par une étape de
traduction par le préprocesseur
• Le préprocesseur cherche dans le code des directives, c’est-à-
dire des instructions qui commencent par # et terminent par
un saut de ligne (pas de ;)
• Directive d’inclusion #include : le préprocesseur remplace la
directive par le code source du fichier inclus (exemple #include
<iostream>)
• Directive de macro #define : permet de remplacer du texte par un
autre (#define type unsigned char)
Préprocesseur
• Compilation conditionnelle : une macro ne comporte pas
forcément de texte de remplacement. Dans ce cas, elle
permet l’utilisation des directives de compilation
conditionnelle #ifdef, #ifndef et #endif.
• La directive #ifdef (#if defined()) teste si un identificateur a été
préalablement défini par #define : si c’est le cas le code compris
entre #ifdef et #endif est compilé
• La directive #ifndef (#if !defined()) teste si un identificateur n’a
pas été préalablement défini par #define : si c’est le cas le code
compris entre #ifndef et #endif est compilé
Préprocesseur
• Compilation conditionnelle :
#include <iostream>

#define PRINT_CAS_1

int main()
{
#ifdef PRINT_CAS_1
std::cout << "Cas 1 \n"; // compilé car PRINT_CAS_1 défini
#endif

#ifndef PRINT_CAS_2
std::cout << "Cas 2\n"; // compilé car PRINT_CAS_2 non défini
#endif

return 0;
}
Fichiers d’en-tête
• Les fichiers d’en-tête (header .h, .hpp ou sans extension)
permettent de mutualiser toutes les déclarations anticipées de
fonctions et de variables
• Exemple : le fichier d’en-tête iostream de la STL comporte les
déclarations anticipées de variables comme std::cout
• Modifier le projet C++ pour inclure un fichier d’en-tête
somme.h
Problème d’inclusion multiple
• L’inclusion multiple d’un fichier d’en-tête conduit à la
redéfinition des fonctions ou variables qu’il contient
• Il peut être inclus dans plusieurs fichiers sources
• Il peut être inclus dans un ou plusieurs autres fichiers d’en-tête
• Pour éviter cela, on doit encadrer les déclarations du fichier
d’en-tête dans un garde-fou (header guard ou include guard)
#ifndef NOM_UNIQUE
#define NOM_UNIQUE
// déclarations (ou définitions par l’utilisateur de certains
types)
#endif
Problème d’inclusion multiple
• La plupart des compilateurs récents comporte la directive
#pragma once comme alternative aux garde-fous standards
• Pour assurer la portabilité du code, il est cependant
préférable de ne pas l’utiliser

Vous aimerez peut-être aussi