Cours Programmation en Langage C - Les Fonctions

Ce TP présente la programmation modulaire en langage C à travers l'utilisation des fonctions. Il explique comment structurer un programme en sous-programmes pour améliorer la lisibilité, la réutilisation et la maintenance du code.

D'après le document Cours Programmation en Langage C - Les Fonctions

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Cours Programmation en Langage C - Les Fonctions

Document source

Cours Programmation en Langage C - Les Fonctions

Computer Science · PDF · 12 pages · 2011

Afficher l'aperçu du document

Consulter le document original →

Ce TP présente la programmation modulaire en langage C à travers l'utilisation des fonctions. Il explique comment structurer un programme en sous-programmes pour améliorer la lisibilité, la réutilisation et la maintenance du code. Vous apprendrez à déclarer, définir et appeler des fonctions, à gérer la portée des variables, ainsi qu'à passer des paramètres par valeur et par adresse, y compris pour les tableaux. Ce TP nécessite un compilateur C et des connaissances de base en programmation C.

Objectifs

  • Comprendre la modularisation d'un programme en C par les fonctions.
  • Savoir déclarer, définir et appeler des fonctions avec ou sans paramètres.
  • Maîtriser la portée des variables locales et globales.
  • Appliquer le passage de paramètres par valeur et par adresse.
  • Manipuler des tableaux en passant leur adresse aux fonctions.

Prérequis et installation

  • Un environnement de développement C avec un compilateur compatible (gcc, clang, etc.).
  • Connaissances de base en langage C : variables, boucles, conditions, entrées/sorties.
  • Éditeur de texte pour écrire les programmes.

Modularisation de programmes avec les fonctions

La structuration d'un programme en sous-programmes (fonctions) permet d'éviter les longues listes d'instructions peu lisibles et répétitives. La modularité offre plusieurs avantages :

  • Meilleure lisibilité du code
  • Diminution du risque d'erreurs
  • Possibilité de tests sélectifs
  • Dissimulation des méthodes
  • Réutilisation de modules existants
  • Simplicité de maintenance
  • Facilitation du travail en équipe
  • Hiérarchisation des modules

Exemple : calcul de la factorielle d'un entier n saisi au clavier.

#include <stdio.h>
void factorielle(int n)
{
  int i, f = 1;
  for (i = 2; i <= n; i++)
    f *= i;
  printf("la factorielle de %d est %d\n", n, f);
}
void main()
{
  int n;
  printf("saisir un entier    ");
  scanf("%d", &n);
  factorielle(n);
  printf("fin du travail");
}

Cette version illustre la séparation du calcul dans une fonction dédiée, améliorant la clarté du programme.

Blocs d'instructions et portée des variables

Les fonctions sont définies par des blocs d'instructions délimités par des accolades :

{
  <déclarations locales>
  <instructions>
}

Les variables déclarées dans un bloc sont locales à ce bloc et invisibles à l'extérieur. Par exemple :

if (n > 0) {
  int i;
  for (i = 0; i < n; i++) ...
}

La variable i n'existe que dans ce bloc. Une variable locale masque toute variable du même nom dans un bloc englobant.

Exemple de variables locales et globales :

#include <stdio.h>
int y = 3;  // variable globale

void test()
{
  printf("la valeur de y dans test est %d\n", y);
}

void main()
{
  printf("la valeur de y, dans main, avant l'appel de test est %d \n", y);
  test();
  printf("la valeur de y, dans main, apres l'appel de test est %d \n", y);
}

Les variables globales sont accessibles dans toutes les fonctions, contrairement aux variables locales.

Déclaration, définition et appel des fonctions

Une fonction est généralement mentionnée à trois endroits :

  1. Déclaration (prototype)
  2. Définition (corps de la fonction)
  3. Appel (utilisation dans le programme)

Exemple d'un programme utilisant deux fonctions : saisir() pour lire un entier, maximum() pour calculer le maximum de deux entiers.

#include <stdio.h>
int saisir()
{
  int a;
  printf("saisir un entier ");
  scanf("%d", &a);
  return a;
}

int maximum(int a, int b)
{
  int max;
  if (a > b) max = a;
  else max = b;
  return max;
}

void main()
{
  int x, y, max;
  x = saisir();
  y = saisir();
  max = maximum(x, y);
  printf("\n le maximum est %d", max);
}

La définition d'une fonction suit la syntaxe :

<TypeRésultat> <NomFonction>(<TypeParam1> <NomParam1>, <TypeParam2> <NomParam2>, ...)
{
  <déclarations locales>
  <instructions>
}

Le type void indique qu'une fonction ne retourne pas de valeur. Le type par défaut est int si non spécifié. Les fonctions ne peuvent pas retourner directement des tableaux ou chaînes, mais peuvent retourner des pointeurs.

Fonctions appelant d'autres fonctions

Une fonction peut appeler une autre fonction. Exemple :

#include <stdio.h>
int saisir()
{
  int x;
  printf("saisir un entier    ");
  scanf("%d", &x);
  return x;
}

int somme(int x, int y, int z)
{
  return (x + y + z);
}

float moyenne(int x, int y, int z)
{
  int s;
  s = somme(x, y, z);
  return (float)(s / 3);
}

void main()
{
  int a, b, c;
  float m;
  a = saisir();
  b = saisir();
  c = saisir();
  m = moyenne(a, b, c);
  printf("la moyenne est %f", m);
}

Exemple pratique : affichage d'un rectangle d'étoiles

Version simple :

#include <stdio.h>
void main()
{
  int l, c, i, j;
  printf("saisir nombre de lignes et nombre de colonnes");
  scanf("%d%d", &l, &c);
  for (i = 0; i < l; i++)
  {
    for (j = 0; j < c; j++)
      printf("*");
    printf("\n");
  }
}

Version modulaire avec fonctions :

#include <stdio.h>
void ligne(int c)
{
  int j;
  for (j = 0; j < c; j++)
    printf("*");
}

void rectangle(int l, int c)
{
  int i;
  for (i = 0; i < l; i++)
  {
    ligne(c);
    printf("\n");
  }
}

void main()
{
  int l, c;
  printf("saisir nbre de lignes et nbre colonnes");
  scanf("%d%d", &l, &c);
  rectangle(l, c);
}

Retourner un résultat avec return

La commande return <expression>; évalue l'expression, convertit son résultat au type de la fonction, renvoie ce résultat et termine la fonction.

Passage des paramètres

Les paramètres sont des variables locales initialisées par les valeurs passées lors de l'appel. Le nombre et l'ordre des paramètres doivent correspondre à la déclaration.

Passage par valeur

Exemple de permutation de deux entiers dans main :

#include <stdio.h>
void main()
{
  int x, y, aide;
  printf(" saisir deux entiers   ");
  scanf("%d%d", &x, &y);
  printf("les valeurs avant permutation: x: %d et y:%d", x, y);
  aide = x;
  x = y;
  y = aide;
  printf("\n les valeurs après permutation: x: %d et y:%d", x, y);
}

Version avec fonction permuter (la permutation ne fonctionne pas car passage par valeur) :

#include <stdio.h>
void permuter(int x, int y)
{
  int aide;
  aide = x;
  x = y;
  y = aide;
}

void main()
{
  int x, y;
  printf(" saisir deux entiers   ");
  scanf("%d%d", &x, &y);
  printf("les valeurs avant permutation: x: %d et y:%d", x, y);
  permuter(x, y);
  printf("\n les valeurs après permutation: x: %d et y:%d", x, y);
}

Résultat : les valeurs ne changent pas après appel de permuter.

Passage par adresse (pointeurs)

Pour modifier les variables d'origine, il faut passer leur adresse et utiliser des pointeurs :

#include <stdio.h>
void permuter(int *a, int *b)
{
  int aide;
  aide = *a;
  *a = *b;
  *b = aide;
}

void main()
{
  int x, y;
  printf(" saisir deux entiers   ");
  scanf("%d%d", &x, &y);
  printf("les valeurs avant permutation: x: %d et y:%d", x, y);
  permuter(&x, &y);
  printf("\n les valeurs après permutation: x: %d et y:%d", x, y);
}

Cette version réalise correctement la permutation.

Passage de l'adresse d'un tableau à une dimension

Impossible de passer un tableau entier par valeur, on passe l'adresse du premier élément (le nom du tableau).

Exemple : remplir et afficher un tableau de n éléments.

#include <stdio.h>
void saisir2(int *a)
{
  printf("saisir nbre d'éléments");
  scanf("%d", a);
}

void remplir(int *p, int n)
{
  int i;
  for (i = 0; i < n; i++)
    scanf("%d", p + i);
}

void afficher(int *p, int n)
{
  int i;
  for (i = 0; i < n; i++)
    printf("%d \t", *(p + i));
}

void main()
{
  int tab[50], n;
  saisir2(&n);
  remplir(tab, n);
  afficher(tab, n);
}

Autres versions utilisant des pointeurs explicites ou la notation tableau :

// Version avec pointeurs et itérateur h
void remplir(int *p, int n)
{
  int *h;
  for (h = p; h < p + n; h++)
    scanf("%d", h);
}

void afficher(int *p, int n)
{
  int *h;
  for (h = p; h < p + n; h++)
    printf("%d \t", *h);
}
// Version avec notation tableau
void remplir(int tab[], int n)
{
  int i;
  for (i = 0; i < n; i++)
    scanf("%d", tab + i);
}

void afficher(int tab[], int n)
{
  int i;
  for (i = 0; i < n; i++)
    printf("%d \t", *(tab + i));
}

Passage de l'adresse d'un tableau à deux dimensions

Pour un tableau 2D, on passe l'adresse du premier élément en convertissant en pointeur int*.

#include <stdio.h>
void remplir(int *tab, int l, int c)
{
  int i, j;
  for (i = 0; i < l; i++)
    for (j = 0; j < c; j++)
      scanf("%d", tab + i * 50 + j);
}

void afficher(int *tab, int l, int c)
{
  int i, j;
  for (i = 0; i < l; i++)
  {
    for (j = 0; j < c; j++)
      printf("%d", *(tab + i * 50 + j));
    printf("\n");
  }
}

void main()
{
  int mat[50][50];
  int l, c;
  printf("saisir nbre d'élémnets");
  scanf("%d%d", &l, &c);
  remplir((int *)mat, l, c);
  afficher((int *)mat, l, c);
}

Résultats attendus

  • Les programmes doivent compiler sans erreur et produire les sorties attendues.
  • La fonction factorielle affiche la bonne valeur pour l'entier saisi.
  • Les fonctions saisir() et maximum() fonctionnent correctement pour retourner des valeurs.
  • Le rectangle d'étoiles s'affiche avec le nombre de lignes et colonnes demandé.
  • La permutation des entiers fonctionne uniquement avec passage par adresse.
  • Les tableaux sont correctement remplis et affichés selon les versions proposées.
  • Le tableau 2D est rempli et affiché correctement avec les indices calculés.

Pièges courants

  • Ne pas déclarer une fonction avant son premier appel peut entraîner des erreurs de compilation.
  • Confondre variables locales et globales : une variable locale masque une variable globale du même nom.
  • Passage de paramètres par valeur ne modifie pas la variable originale ; utiliser des pointeurs pour modifier.
  • Oublier d'utiliser l'opérateur & pour passer l'adresse d'une variable lors d'un appel.
  • Dans les tableaux 2D, mal calculer l'offset (i * largeur + j) lors de l'accès aux éléments.
  • Ne pas mettre de point-virgule après la définition d'une fonction (il n'y en a pas).
  • Attention à la conversion des types lors du retour d'une fonction, notamment pour les flottants.
  • Dans la fonction moyenne, écrire correctement la conversion en float pour éviter les divisions entières.

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions