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:
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.