TP N°1

Langage C - Généralités

 

Exercice 1 : L’opérateur sizeof sur types

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

 

#include     <stdio.h> /* bibliothèque d'entrées-sorties standard */

#include  <stdlib.h>

typedef  struct 

{

     int jour, mois, annee;

} Date;

typedef struct

{

    char nom[20] ;

    char  prenom[10] ;

    long matricule ;

    Date dateNaiss;

} Etudiant ;

 

int main()

{         

          printf("Taille d’un entier  short:%d\n", sizeof(short));

          printf("Taille d’un entier  simple:%d\n", sizeof(int));

         printf("Taille d’un unsigned  int:%d\n", sizeof(unsigned int));

          printf("Taille d’un entier Long:%d\n" , sizeof(long));

          printf("Taille d’un caractere:%d\n", sizeof(char));       

          printf("Taille d’un reel  float:%d\n", sizeof(float));     

          printf("Taille d’un reel  double:%d\n", sizeof(double));

          printf("Taille de struct Date: %d\n", sizeof(Date));

          printf("Taille de struct Etudiant: %d\n", sizeof(Etudiant));

          printf("Taille de pointeur vers struct Etudiant: %d\n", sizeof(Etudiant *));

          system("pause") ;

          return 0 ;

}

 

Exercice 2 : L’opérateur sizeof sur variables

Modifier l’exercice precedent en utilisant sizeof() avec des variables maintenant.

Compilez et exécutez le programme,  constatez ce qui est affiché.

 

#include     <stdio.h> /* bibliothèque d'entrées-sorties standard */

#include    <stdlib.h>

typedef  struct 

{

     int jour, mois, annee;

} Date;

typedef struct

{

    char nom[20] ;

    char  prenom[10] ;

    long matricule ;

    Date dateNaiss;

} Etudiant ;

 

int main()

{         

          short s ;

          int i ;

          unsigned int ui ;

          long l ;

         char c ;

         float f ;

         double d ;

         Date  dat ;

         Etudiant etd, *p ;

 

          printf("Taille d’un entier  short:%d\n", sizeof(s));

          printf("Taille d’un entier  simple:%d\n", sizeof(i));

          printf("Taille d’un unsigned  int:%d\n", sizeof(ui));

          printf("Taille d’un entier Long:%d\n" , sizeof(l));

          printf("Taille d’un caractere:%d\n", sizeof(c));

          printf("Taille d’un reel  float:%d\n", sizeof(f));           

          printf("Taille d’un reel  double:%d\n", sizeof(d));

          printf("Taille de struct Date: %d\n", sizeof(dat));

          printf("Taille de struct Etudiant: %d\n", sizeof(etd));

          printf("Taille de pointeur vers struct Etudiant: %d\n", sizeof(p));

          system("pause") ;

          return 0 ;

}

 

Exercice 3 : printf

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

    int a;

    float b;

    a = 1625;

    b = 13.1452;

    printf ("A format Entier Positif = %8d ****\n", a);

    printf ("A format Entier Negatif = %-8d ****\n", a);

    printf ("B format Reel Positif = %8.3f ****\n", b);

    printf ("B format Reel Negatif = %-8.3f ****\n", b);

    a = 19 ;

    printf ("A format Hexadecimale Entier = %x ****\n", a);

 

    system("pause") ;

    return 0;

}

 

Exercice 4 : Les Opérateurs Arithmétiques

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

    int a, b;

    float f ;

 

    a = 4 ;

    b = 3 ;

    a = a + 2 ;

    b = b – 2 ;

 

    printf("a = %d et  b = %d  \n", a, b) ;

  

    a = 2 ;

    b = 5 ;

    a = a * 3 ;

    b = b - 2 ;

    printf("a = %d et  b = %d  \n", a, b) ;

   

    a = 23 ;

    b = 5 ;

    a = a % b ;

    printf("a = %d et  b = %d  \n", a, b) ;

 

   f = 3 / 2 ;

   printf("f = %4.2f   \n", f) ;

 

   f = 3.0 / 2 ;

   printf("f = %4.2f   \n", f) ;

 

   system("pause") ;

   return 0;

}

 

Exercice 5: Les Opérateurs de Comparaison et les Opérateurs Logiques

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

    int a, b;

 

    a = 4 ;

    b = 3 ;

    if ((a >= 4) && (b == 3))

    {

         printf("a est >= 4 ET b est egale a 3 \n") ;

    }

 

    if ((a < 12) || (b != 3))

    {

         printf("a est < 12 OU  b est different de 3 \n") ;

    }

 

     if  ( !(a <= 2))

    {

         printf("a N’EST PAS <= 2 \n") ;

    }

 

   system("pause") ;

    return 0;

}

 

Exercice 6 : pré/post incrémentation

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

    int a, b;

 

    a = 4 ;

    b = ++a ;

    printf("a = %d et b = %d \n", a, b) ;

 

    a = 4 ;

    b = a++ ;

    printf("a = %d et b = %d \n", a, b) ;

   system("pause") ;

    return 0;

}

 

Exercice 7: Les Opérateurs sur les Bits

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

 

 

  

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

    int a, b, c;

 

    a = 4 ;

    b = 3 ;

    c = a & b;  // a AND b

    printf("a = %x ,  b = %x et (a AND b) = %x  \n", a, b, c) ;

  

    c = a | b;  // a OR b

    printf("a = %x ,  b = %x et (a OR b) = %x  \n", a, b, c) ;

 

    c = a ^ b;  // a XOR b

    printf("a = %x ,  b = %x et (a XOR b) = %x  \n", a, b, c) ;

 

    c = ~a ;  // complement a 1 de a

    printf("a = %x et (complement a 1 de a) = %x  \n", a, c) ;

 

    c = a << 2;  // decalage a gauche de a de 2 positions

    printf("a = %x et (a << 2) = %x  \n", a, c) ;

 

   a = 49 ;

   c = a >> 2;  // decalage a droite de a de 2 positions

    printf("a = %x et (a >> 2) = %x  \n", a, c) ;

 

   system("pause") ;

   return 0;

 

}

 

Priorité des Opérateurs

 

Priorité 1 (la plus forte):

()

      -->

Priorité 2:

! ++ --

       <--

Priorité 3:

* / %

       -->

Priorité 4:

+ -

       -->

Priorité 5:

< <= > >=

       -->

Priorité 6:

==     !=

       -->

Priorité 7:

&&

       -->

Priorité 8:

||

       -->

Priorité 9 (la plus faible):

= += -= *= /= %=

       <--

 

Dans table c-dessus :

 <--   : Priorite de droite a gauche 

 -->  : Priorite de gauche a droite

 

Exercice 8: Boucles for imbriquées

Compilez et exécutez le programmes suivant,  constatez ce qui est affiché.

 

Ecrire un programme qui affiche les tables de multiplication  de 1 à 9 pour toutes les valeurs de 1 à 9.

 

#include <stdio.h>

#include <stdlib.h>

 

int     main()

{

   int i, j;

   

   for (i = 1; i < 10; i++)

   {

              for (j = 1; j < 10; j++)

              {

                        printf("%2d * %2d = %2d       ", j, i, j * i);

              }

              printf("\n");

   }

  

   system("pause")  ;

   return 0 ;

 

}

 

 

Exercice 9: break dans while/for/switch

Compilez et exécutez le programmes suivant,  constatez ce qui est affiché.

 

Ecrire un programme qui lit 2 entiers x et y et qui lit un entier choix et qui fait : (x + y) si choix = 1, (x – y) si choix = 2 ou choix = 3 , (x * y) si choix = 4 ou choix = 5,   (x / y) si y #0 si choix = 6, (x %  y) si choix = 9, s’arrete si choix = 0 et affiche erreur pour toute autre valeur.

 

Remarques:

- un break permet de sortir d'un for, d'un while ou d'un switch.

- while(1) ou for( ; ; ) expriment une boucle infinie mais grâce au break on peut sortir de cette boucle.

 

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

   int a, b, result;

   int choix;

   while (1)

   /* for( ; ; ) */

   {

     printf(" Faites Entrer 2 entiers a and b ...\n");

     scanf("%d %d", &a, &b);

     printf("Faites votre choix ...\n");

    

 

     printf("Entrer 1 pour faire a + b \n");

     printf("Entrer 2 ou 3 pour faire a - b \n");

     printf("Entrer 4 ou 5 pour faire a * b \n");

     printf("Entrer 6 pour faire a / b ou b non nul \n");

     printf("Entrer 7 pour faire a %% b \n");

     printf("Entrer 0 pour Quitter le programme  \n");

 

     scanf("%d", &choix);

     if (choix == 0)

        break;

 

     switch(choix)

     {

       case 1:

        result = a + b;

        printf("%d + %d = %d \n", a, b, result);

       break;

       case 2:

       case 3:

         result = a - b;

         printf("%d - %d = %d \n", a, b, result);

       break;

       case 4:

       case 5:

         result = a * b;

         printf("%d * %d = %d \n", a, b, result);

       break;

       case 6:

         if (b != 0)

         {

            result = a / b;

            printf("%d / %d = %d \n", a, b, result);

         }

         else

            printf("Erreur: b est 0 dans la division par b \n");

        break;

        case 7:

            result = a % b;

            printf("%d %% %d = %d \n", a, b, result);

         break;

         default:

           printf("Erreur dans le choix de operation ..\n");

         break;

       }

    }

 

   system("pause");

      return 0;

}

 

Exercice 10: Opérateur ?

L’opérateur conditionnel ? est un opérateur ternaire. Sa syntaxe est la suivante :

condition ? expression-1 : expression-2

Cette expression est égale à expression-1 si condition est satisfaite, et à expression-2 sinon.

 

Compilez et exécutez le programme suivant,  constatez ce qui est affiché.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

int main()

{

    int a, b, max ;

    printf("Donner 2 Entiers a est b\n") ;

    scanf("%d %d", &a, &b) ;

    max = ((a > b) ? a : b);

    printf(“”Le Maximum de %d et %d est %d\n”, a, b, max);

    system("pause") ;

    return 0;

}

     

Exercice 11: Passation par Valeur vs Passation par Adresse de Paramètres à une Fonction

Compilez et exécutez les 2 programmes suivant,  constatez ce qui est affiché et comparer.

 

Programme 1 :

 

#include <stdio.h>

#include <stdlib.h>

 

void     permuter (int a, int b)

{

    int t;

    printf("debut fonction permuter :\n a = %d \t b = %d\n",a,b);

    t = a;

   a = b;

   b = t;

   printf("fin fonction permuter :\n a = %d \t b = %d\n",a,b);

   return;

}

int     main()

{

    int a = 2, b = 5;

    printf("debut programme principal :\n a = %d \t b = %d\n",a,b);

    permuter(a,b);

    printf("fin programme principal :\n a = %d \t b = %d\n",a,b);

 

   system("pause") ;

   return 0;

 

}

 

Programme 2:

 

#include <stdio.h>

#include <stdlib.h>

 

void    permuter (int *a, int *b)

{

    int t;

    t = *a;

    *a = *b;

    *b = t;

 

   return;

}

int      main()

{

    int a = 2, b = 5;

    printf("debut programme principal :\n a = %d \t b = %d\n",a,b);

    permuter(&a,&b);

    printf("fin programme principal :\n a = %d \t b = %d\n",a,b);

 

   system("pause") ;

   return 0;

 

}

 

REMARQUE : En C un tableau est toujours passé par adresse comme paramètre à une fonction.

 

#include <stdio.h>

#include <stdlib.h>

 

void     init(int tab[], int n)

{

   int i;

   for (i = 0; i < n; i++)

       tab[i] = i;

   return;

}

void     afficherVect(int V[], int n)

{

   int i;

  

   printf("Elements du Vecteur V:\n"); 

   for (i = 0; i < n; i++)

       printf("V[%d] = %d\n", i, V[i]);

   return;

}

 

int     main()

{

     int I;

     int V[5];

 

     init(V,5);

     afficherVect(V, 5);

 

     system("pause") ;

     return 0;

}

 

REMARQUES:

Une structure, comme une variable de type simple, peut être passée par valeur comme elle peut être passée par adresse à une fonction.

Une fonction peut retourner une structure comme elle peut retourner un pointeur vers une structure.

Contrairement aux tableaux, on peut affecter une variable de type structure à une autre variable du même type structure.

 

Exercice 12: Encore Passation par Valeur vs Passation par Adresse - Notion de Pointeur de Pointeur 

Compilez et exécutez le programme suivant,  constatez ce qui est affiché et comparer.

 

#include <stdio.h>

#include <stdlib.h>

 

void     setIntPassVal(int v, int val)

{

    v = val;

}

 

void      setIntPassAdrs(int *v, int val)

{

    *v = val;

}

 

void      setPtrPassVal(int *p, int *pAdrs)

{

    p = pAdrs;

}

 

void     setPtrPassAdrs(int **p, int *pAdrs)

{

    *p = pAdrs;

}

 

int    main()

{

    int v, *ptr;

 

    printf("Initialement V = %d et Ptr = %x\n", v, ptr);

 

    setIntPassVal(v, 5);

    setPtrPassVal(ptr, &v);

    printf("Passation Par Valeur V = %d et Ptr = %x\n", v, ptr);

 

    setIntPassAdrs(&v, 5);

    setPtrPassAdrs(&ptr, &v);

    printf("Passation par Adresse V = %d et Ptr = %x\n", v, ptr);

 

    return 0;

}

 

 

 

Exercice 13: Les chaines de caractères

Compilez et exécutez le programmes suivant,  constatez ce qui est affiché.

Etant donné une chaine de caractères, le programme indique si c’est un palindrome ou non. Lorsqu’on rentre le mot "fin" le programme s’arrête. Un palindrome est un mot qui se lit de gauche à droite et de droite à gauche (c'est-à-dire, le mot se lit dans les deux sens). 

 

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

int    palindrome(char st[])

{

    int i, j, len;

 

    len = strlen(st);

    i = 0;

    j = len - 1;

 

    while (i < j)

    {

        if (st[i] != st[j])

            return 0;

 

        i++;

        j--;

    }

 

    return 1;

}

 

int     main()

{

    char mot[100];

    while (1)

    {

        printf("Veuillez Donner un mot\n");

        scanf("%s", mot);

        if (strcmp(mot, "fin") == 0)

            break;

 

        if (palindrome(mot))

            printf("'%s' est un Palindrome\n", mot);

        else

            printf("'%s' N'est Pas un Palindrome\n", mot);

    }

    return 0;

}

 

Exercice 14: Allocation Dynamique de la Mémoire

 

Rappel

Les Fonctions suivates (faisant partie de stdlib.h) sont utilisees dans l'allocation dynamique de memoire:

void   *malloc(int nb);

Cette fonction alloue une zone de nb octets en mémoire et retourne l'adresse de cette zone s'il y'a  nb octets contigues libres à allouer (les octets alloues ne sont pas initialisée).. S'il n'y a pas nb octets contigues libres à allouer, cette fonction retourne NULL.

void   *calloc(int nb, int size);

Cette fonction alloue une zone de nb éléments où chaque élément a size octets en mémoire, retourne l'adresse de cette zone et initialise tous les octets à 0 s'il y'a  (nb * size) octets contigues libres à allouer.. S'il n'y a pas  (nb * size) octets contigues libres à allouer, cette fonction retourne NULL.

void  free(void *ptr);

Cette fonction libère l'espace de la zone mémoire dont l'adresse est contenu dans ptr.

void  *realloc(void *ptr, int nb);

Cette fonction réalloue la zone mémoire dont l'adresse est dans ptr avec un espace mémoire de taille nb octets s'il y'a suffisamment d'espace mémoire libre et retourne dans ce cas l'adresse mémoire de la nouvelle zone mémoire allouée. S'il n'a pas suffisamment d'espace libre à allouer cette fonction retourne NULL.

 

Compilez et exécutez le programmes suivant,  constatez ce qui est affiché.

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

int    main() {

 

   char nom[100];

   char *detail;

 

   strcpy(nom, "Omar Belkadi");

 

   detail = malloc( 30 * sizeof(char) );

 

   if( detail == NULL )

   {

      printf("Erreur - Memoire Insuffisante a Allouer\n");

      return EXIT_FAILURE;

   }

 

    strcpy( detail, "L2ACAD");

    printf("%s %s\n", nom, detail);

 

   /* supposons qu'on veut plus de details */

   detail = realloc( detail, 100 * sizeof(char) );

 

   if( detail == NULL )

   {

      printf("Erreur - Impossible de Reallouer plus d'Espace\n");

      return EXIT_FAILURE;

   }

 

   strcat( detail, " a une moyenne de 14 sur 20 en Algo");

 

   printf("%s %s\n", nom, detail);

 

   free(detail);

 

   return EXIT_SUCCESS;

}

 

 

 

Exercice 15: Passation de Paramètres à main()

Lors du lancement de l’execution d’un programme C on peut lui passer un certain nombre d’arguments.

La fonction principale main s’ecrit : int main(int argc, char *argv[])

argc est de type int et contient le nombre d’arguments et argv est un tableau de chaines de caractères qui contiennent chacun l’argument correspondant. argv[0] contient le nom de l’executable, argv[1] contient le paramètre 1, …

 

Remarque pour passer des arguments à l’executable :

Sur CodeBlocks aller a Project-Set program’s arguments et taper les arguments

Sur Dev-C++ aller à Execute-Parameters et taper les arguments

Sur une fenêtre commande il suffit d’ecrire le nom de l’executable suivi des arguments.

 

Compilez et exécutez le programmes suivant qui fait la somme de 2 entiers.

Remarque l’utilisation de la fonction atoi() qui convertit une chaine de caractere en un entier.

 

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[])

{

      int a, b, i;

 

      for (i = 0; i<argc;i++)

            printf("arg[%d] = %s\n", i, argv[i]);

 

      if (argc != 3)

     {

             printf("\nErreur : nombre invalide d’arguments");

             printf("\nUsage: %s int int\n",argv[0]);

             return 1;

     }

 

     a = atoi(argv[1]);

     b = atoi(argv[2]);

     printf("\nLa Somme de %d par %d vaut : %d\n", a, b, a + b);

     return 0;

 

}

 

 

 

Exercice 16: Pointeur sur une fonction

Il est parfois utile de passer une fonction comme paramètre à une autre fonction. Cela permet en particulier d’utiliser une même fonction pour differents usages. On utilise pour cela un mécanisme de pointeur. Un pointeur sur une fonction correspond à l’adresse du début du code de la fonction.

 

Du point de vue syntaxique, un pointeur sur une fonction ayant pour prototype

type fonction(type 1,...,type n);

s’ecrit:

type (*)(type 1,...,type n);

 

Par exemple l’ecriture:

int (*pF) (int a, int b);

 

declare pF comme un pointeur vers une function qui retourne un int et qui a 2 paramètres qui sont tous les 2 des int.

 

Les pointeurs sur les fonctions sont notamment utilisés dans la fonction de tri des éléments d’un tableau qsort et dans la recherche d’un élément dans un tableau bsearch. Ces deux fonctions sont définies dans la libriarie standard (stdlib.h).

 

Le prototype de la fonction de tri (algorithme quicksort) est

void qsort(void *tableau, size_t nb_elements, size_t taille_elements, int(*comp)(const void *, const void *));

Elle permet de trier les nb éléments premiers éléments du tableau tableau. Le paramètre taille éléments donne la taille des éléments du tableau. Le type size t utilise ici est un type prédefini dans stddef.h. Il correspond au type du résultat de l’evaluation de sizeof. Il s’agit du plus grand type entier non signé. La fonction qsort est paramètree par la fonction de comparaison utilisée de prototype :

int comp(void *a, void *b);

Les deux paramètres a et b de la fonction comp sont des pointeurs génériques de type void *. Ils correspondent à des adresses d’objets dont le type n’est pas determiné. Cette fonction de comparaison retourne un entier qui vaut 0 si les deux objets pointés par a et b sont égaux et qui prend une valeur strictement négative (resp. positive) si l’objet pointé par a est strictement inférieur (resp. superieur) à celui pointé par b.

 

La librairie standard dispose également d’une fonction de recherche d’un élément dans un tableau trié, ayant le prototype suivant :

void *bsearch((const void *clef, const void *tab, size_t nb_elements, size_t taille_elements, int(*comp)(const void *, const void *)));

Cette fonction recherche dans le tableau trié tab un élément qui soit égal à l'élément d’adresse clef. Les autres paramètres sont identiques à ceux de la fonction qsort. S’il existe dans le tableau tab un élément égal à celui pointé par clef, la fonction bsearch retourne son adresse (de type void *). Sinon, elle retourne le pointeur NULL.

 

 

 

Compilez et exécutez le programmes suivant qui appelle la fonction qsort ensuite la fonction bsearch en passant a chaque fois un pointeur de fonction.

 

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

 

#define NB_ELTS 20

void affixher_tab(int[], int);

int comp_int(int *, int *);

 

void afficher_tab(int tab[], int nb)

{

   int i;

   printf("\n");

   for (i = 0; i < nb; i++)

       printf("%d \t",tab[i]);

   printf("\n");

   return;

}

 

int comp_int(int *a, int *b)

{

   return(*a - *b);

}

 

int main()

{

   int tab[NB_ELTS]={ 15, -2, 18, 99, 6, 26, -27, 6, 11, -9, 256, 27, 10, -76, 22, 26, 17, 71, 27, -3};

 

   int i, v;

 

   afficher_tab(tab, NB_ELTS);

   qsort(tab, NB_ELTS, sizeof(int), comp_int);

   afficher_tab(tab, NB_ELTS);

 

   printf("Donner un entier a rechercher …\n");

   scanf("%d", &v);

   if ((bsearch(&v,tab,NB_ELTS,sizeof(int),comp_int)) == NULL)

       printf("\nLe tableau ne contient pas l'element %d\n",v);

   else

       printf("\nLe tableau contient l'element %d\n",v);

 

   system("pause");

   return 0;

}

 

 

 

Unr autre utilisation des pointeurs de fonction est de remplacer un switch/case statement par un appel directe à une fonction en utilisant un tableau de pointeurs de fonction comme illustré dans l’exemple ici bas. Ceci permet d’avoir des programmes plus rapides. Remarquer dans le programme ici bas que comme pour un tableau, le nom d'une fonction represente son adresse.

 

Compilez et exécutez le programmes suivant.

 

#include <stdlib.h>

#include <stdio.h>

 

int somme(int, int);

int difference(int, int);

int produit(int, int);

int quotient(int, int);

 

int main()

{

    int a, b, choix;

    int  (*tabF[4])(int, int);

 

    tabF[0] = somme;

    tabF[1] = difference;

    tabF[2] = produit;

    tabF[3] = quotient;

 

    while(1)

    {

        printf("Entrer votre choix\n");

        printf("0: somme, 1:difference, 2:produit, 3. qutient, 99:quitter le programme ...\n");

        scanf("%d", &choix);

        if (choix == 99)

            break;

        if ((choix >= 0) && (choix <= 3))

        {

            printf("Entrer 2 Entiers ...\n");

            scanf("%d %d", &a, &b);

            tabF[choix](a, b);

        }

    }

 

    return 0;

}

 

int somme(int a, int b)

{

    printf("%d + %d = %d\n", a, b, a+b);

}

int difference(int  a, int  b)

{

    printf("%d - %d = %d\n", a, b, a-b);

}

int produit(int a, int  b)

{

    printf("%d * %d = %d\n", a, b, a*b);

}

int quotient(int a, int b)

{

    if (b != 0)

      printf("%d / %d = %d\n", a, b, a/b);

    else

      printf("Erreur: b = 0 ...\n");

 

}

 

 

Exercice 17: Fonction avec nombre variable de paramètres

Il se peut qu'on aie besoin parfois de définir une fonction qui a un un nombre variable de paramètres. Le langage C permet de faire celà. Les fonctions scanf() et printf() qui font partie de stdio.h sont des exemples de telles  fonctions. 

 

Choses à faire pour dèfinir une telle fonction:

  • Inclure stdarg.h car on a besoin d'utiliser des macros definies dans stdarg.h.
  • Le dernier paramètre formel dans la définition de la fonction doit être écrit comme "..." dans la liste des paramètres formels.  Avant les "..." il faut déclarer un paramètre de type int qui doit contenir le nombre de paramètres qui va suivre lors de l'appel de la fonction. Noter que le nombre de paramètres doit être le dernier paramètre obligatoire à la fonction.
  • Déclarer une variable de type va_list (définie dans stdarg.h) dans la fonction.
  • Utiliser va_start pour initialiser la variable de type va_list au premier paramètre
  • Utiliser va_arg pour retrouver chacun des paramètre. IMPORTANT: il faut connaitre le type de chaque paramètre.
  • Utiliser va_end pour faire un clean-up de la mémoire assignée à la variable de type va_list avant que la fonction retourne.

 

Voir exemple ci-dessous de la fonction moyenne qui calcule la moyenne d'un nombre variable de paramètres.

 

#include <stdio.h>

#include <stdarg.h>

 

double     moyenne(int nb, ...)

{

   va_list     valist;

   double     somme = 0.0;

   int             i;

 

   /* initialiser valist  */

   va_start(valist, nb);

 

   /* acceder a chacun des arguments assigne a valist. 

       Dans notre cas tous les parametres sont de type int

    */

   for (i = 0; i < nb; i++)

  {

        somme += va_arg(valist, int);

   }

 

   /* faire un clean-up de la memoire reservee a valist */

   va_end(valist);

 

   return   somme/nb;

}

 

int     main()

{

   printf("Moyenne de 5, 7, 9, 3 = %f\n", moyenne(4, 5,7,9,3));

   printf("Moyenne de 15, 11, 6 = %f\n", moyenne(3, 15,11,6));

   

   return 0;

 

}

 

Exercice 18: Le Préprocesseur de C

Le préprocesseur de C est un executable qui est appelé par le compilateur C avant que la compilation commence. Un certain nombre de directives qui commencent par le caractere "#" peuvent être utilisés en C. Ces directives sont utilisées dans l'inclusion de header files (fichiers .h), la compilation conditionnelle, ou dans les macros.

 

Inclusion de Header Files (Fichiers .h)

La directive "include" est utilisee comme "#include"

Exemple:

#include <stdio.h>

#include "myIncFile.h"

 

Dans cet exemple le préprocesseur remplace "#include <stdio.h>" par le texte du fichier "stdio.h" et remplace "#include "myIncFile.h" par le contenu du fichier "myIncFile.h".

 

Les autres directives utilisées sont: #ifndef et #endif. 

Ces directives sont utilisées dans les header files de la library de C et dans les header files de l'utilisateur.

Par exemple on trouve au début du header file (stdio.h):

#ifndef _STDIO_H_

#define _STDIO_H_

 

et a la fin de stdio.h on trouve

 

#endif /* _STDIO_H_ */

 

Cela permet a ce que stdio.h soit incluse une seule fois dans un fichier .c.

 

Généralement on a un fichier .c pour chaque module donné et pour chaque fichier .c on a un fichier .h (header file) ou on définie les structures de données que ce fichier .c manipule. Supposons par exemple qu'on a un fichier .c de nom mod.c alors on aura un fichier .h de nom mod.h et au début de mod.h on aura:

#ifndef _MOD_H_ 

#define _MOD_H_

 

et a la fin de mod.h 

 

#endif /* _MOD_H_ */

 

Compilation Conditionnelle

Les directives utilisées sont: #if, #ifdef, #ifndef, #else, #elif et #endif.

La compilation conditionnelle est généralement utilisée pour afficher des informations dont on a besoin lors du debugging ou bien lorsqu'on a un code destiné à plusieurs platformes (par exemple Unix et Windows).

 

Exemples:

#ifdef DEBUG

   printf("Valeur de i = %d\n", i);

#endif

 

 

#ifdef _unix_

#include <unistd.h>

#elif defined _windows_

#include <windows.h>

#endif

 

Macros

Voir exercice 19.

 

 

Exercice 19: Les Macros en C

Une macro est un fragment de code à qui on a attribué un nom. Chaque fois que ce nom est rencontré dans le code, ce nom est remplacé par le code de la macro. Il y'a 2 types de macros en C selon leur utilisation: macros utilisées comme données et macros utilisées comme fonctions.

 

La définition d'une macro commence par le mot cle "#define". Une macro peut se définir sur une seule ligne  ou bien sur plusieurs lignes. Si on utilise plusieurs lignes dans la définition d'une macro il faut ajouter le caractere "\" à la fin de toutes les lignes à l'exception de la dernière.

 

Macro Utilisee comme Donnée

Exemples:

#define BUFF_SIZE  1024

BUFF_SIZE est le nom d'une macro qui définie une constante et à chaque fois que le nom BUFF_SIZE est rencontré dans le code le préprocesseur de C va remplacer ce nom par la constante 1024. Par exemple lorsque le préprocesseur rencontre le code:

ptr = (char *) malloc (BUFF_SIZE);

il remplacera ce code par:

ptr = (char *) malloc(1024);

 

 

#define ListNumbers 1, 2, 3

int V[] = { ListNumbers };

 

Dans ce 2eme cas ListNumbers est remplacé par "1, 2, 3" par le préprocesseur et la déclaration de V devient: int V[]={1, 2, 3};

 

Macro Utilisée comme Fonction

Dans ce cas la macro ressemble à une fonction. Mais contrairement à une fonction à la rencontre du nom de la macro le préprocesseur remplacera ce nom par le code de la macro.

Ci-dessous  un exemple de 2 macros; l'une définie sur une seule ligne et l'autre en utilisant 2 lignes.

 

#include <stdio.h>

 

#define square(x) (x * x)

 

#define MAXI(x, y) \

  ((x > y) ? x : y)

 

int main()

{

   printf("4 au Carre = %d\n", square(4));

 

   printf("Maximum de 12 et 19 = %d\n", MAXI(12, 19));

 

   return 0;

}

 

Les macros ressemblant aux fonctions prennent des arguments comme des fonctions seulement il faut faire attention lorsque le nom de la macro est remplacée par son code les arguments de la macros peuvent être evalués plusieurs fois. Comme dans l'exemple ci-dessous ++y est evaluée 2 fois et on sort avec le maximum égale à 20.

 

#define MAX(x, y) \

  ((x) > (y) ? (x) : (y))

 

int main()

{

    int x = 11;

    int y = 18;

 

    printf("Maximum de x et y = %d\n", MAX(++x, ++y));

 

    printf("x = %d and y = %d\n", x, y);

 

    return 0;

}

 

Ce programme donne comme output:

Maximum de x et y = 20

x = 12 and y = 20

 

Il faut noter qu'il y'a un certain nombre de macros prédefinies en C tels que: __FILE__, __DATE__ et __LINE__.

Pour plus de détails sur les macros se référer au lien suivant.

 

Exercice 20: Traitement des erreurs en C

La plupart des fonctions faisant partie de la library de C retournent -1 ou bien NULL en cas d'erreur et dans ce cas mettent dans la variable globale errno (declarée dans errno.h) la valeur qui correspond au cas d'erreur. Aussi les fonctions perror() et strerror permettent d'afficher le texte correspondant à l'erreur en question.

 

L'exemple ci-dessous reflete le cas où on essaie d'ouvrir un fichier inéxistant:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

 

int main ()

{ 

   FILE * pf;

  

   pf = fopen ("FNom.txt", "rb");

 

   if (pf == NULL)

  {

      printf("Valeur de errno: %d\n", errno);

      printf("Erreur lors Ouverture Fichier FNom.txt: %s\n", strerror( errno ));

      perror("Erreur Donnee par perror: "); 

   }

   else

   {

      fclose (pf);

   }

 

   return 0;

 

}

 

L'execution de ce programme lorsque le fichier "FNom.txt" n'existe pas affiche les messages suivants:

 

Valeur de errno: 2

Erreur lors Ouverture Fichier FNom.txt: No such file or directory

 

Erreur Donnee par perror: : No such file or directory

 

 

Exercice 21: assert() en C

La macro assert(expression) est utilisée pour diagnostiquer des erreurs lors de l'execution d'un programme. La syntaxe est: assert(expression) ou expression est une expression booléenne. Si l'expression booléenne s'evalue à Vrai (valeur differente de 0) lors de l'execution du programme alors assert() ne fait rien. Si expression s'evalue à Faux (valeur égale à 0) alors le programme s'arrête et un message d'erreur contenant le numero de la ligne où le assert() est dans le code source est affiché. Pour pouvoir utiliser cette macro il faut inclure assert.h.

 

L'exemple ci-dessous montre une utilisation de cette macro.

 

#include <stdio.h>

#include <assert.h>

 

 

int main ()

{

   FILE * pf;

 

   pf = fopen ("FNom.txt", "rb");

   assert(pf != NULL);

   fclose (pf);

 

   return 0;

 

}

 

 

L'execution de ce programme lorsque le fichier "FNom.txt" n'existe pas affiche les messages suivants (la ligne 10 est le numero de la ligne assert(pf != NULL)):

 

Assertion failed!

 

Program: C:\Users\djgue_000\CodeBlockCFiles\tmp108\bin\Debug\tmp108.exe

File: C:\Users\djgue_000\CodeBlockCFiles\tmp108\main.c, Line 10

 

Expression: pf != NULL

 

Process returned 3 (0x3)   execution time : 2.369 s

 

Press any key to continue.