Td corrigé Chapitre 6 : type énumération, opérateurs spéciaux, pdf

Chapitre 6 : type énumération, opérateurs spéciaux,

note[0] pour la note d'examen d'intra. note[1] pour la note ... est plus clair que Poste = 'P' ; (est-il pianiste ?) ..... plusGrand = 65535 (en décimal) et ffff (en hexadécimal). Appuyez ... Pour corriger la situation, on peut utiliser l'opérateur de "cast".




part of the document



permettant d'attribuer un identificateur
significatif à une constante énumérée dans la liste.

Exemple 1 :

enum jour { Dimanche, Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi };

"enum jour" est le nom du type énumération.

Cette déclaration est équivalente à la série de #define suivante :

#define Dimanche 0
#define Lundi 1
etc ....
#define Samedi 6


Pour cette raison, on peut écrire une instruction du genre :

int k ;

for ( k = Dimanche ; k y ? x : y ;
}

Syntaxe :

condition ? E1 : E2

E1 et E2 sont souvent des expressions :

int age, enfant ;

#define VRAI 1
#define FAUX 0

enfant = age = 12 && age < 18 ? printf("Adolescent ") :
printf("Non!") ;
Fonctionnement :

Si la condition vaut vraie, c'est E1, sinon c'est E2.

Opérateur séquentiel (avec , (virgule)) :

Pour fournir une liste "séquentielle" des instructions :

1. Dans l'échange du tri :

if ( indMin != i )
{
temporaire = t[i] ;
t[i] = t[indMin] ;
t[indMin] = temporaire ;
}

on peut simplifier comme suit :

if ( indMin != i )
temporaire = t[i] , t[i] = t[indMin],
t[indMin] = temporaire ;

2. Dans le test si une chaîne de caractères est un palindrôme :

int palind ( char * ch )
{
int n = strlen(ch), i, j ;

for ( i = 0 , j = n-1 ; i < j ; i++, j-- )

if ( ch[i] != ch[j] ) return 0 ;

return 1 ;
}

3. Dans la lecture d'un fichier binaire (plus tard dans le cours) :

while ( fread (.... ) , !feof(aLire) )
{ .....
.....
}

C) Compréhension des déclarations en C :

En C, certaines déclarations sont faciles à comprendre :

1. float taille[10] ;

taille est un tableau de 10 réels.

2. float * P ;

P est un pointeur vers un élément de type réel.

etc ....

Cependant, l'interprétation d'une déclaration sans utiliser
la définition de type (typedef) n'est pas sans difficultés.

Avec la déclaration :

float * P[10] ;

P est-il un tableau de 10 pointeurs vers le type réel ?
P est-il un pointeur vers un tableau de 10 réels ?

Avec une autre déclaration : char * (*f) () ;

Comment peut-on interpréter f ?

Pour répondre à ces questions, on va analyser quelques
déclarations dans les exemples suivants :

Exemple 1 :

1. Interpréter la déclaration suivante :

char (*P) [10] ;

2. Donner une utilisation valide de P.

Solution :

1. Interprétation de la déclaration : char (*P) [10] ;

(*P) d'abord => P est un pointeur vers ...

char ... [10]=> est un tableau de 10 caractères

Donc: P est un pointeur vers un tableau de 10 caractères.


2. Une utilisation valide de P :

char t[10] = { 'B', 'k'} ; /* initialiser partiellement*/
P = &t ;

Exemple 2 :

1. Interpréter la déclaration suivante :

char * (*P) () ;

2. Écrire un programme qui démontre une utilisation valide de P.

Solution :

1. Interprétation de la déclaration : char * (*P) () ;

(*P) d'abord ==> P est un pointeur vers ....

char * () ==> fonction qui retourne un pointeur
vers le type caractère

Donc P est un pointeur vers (une fonction qui retourne un
pointeur vers le type caractère).

2. Une utilisation valide de P :

/* Fichier Declare1.C */

#include
#include
#include

/* P est un pointeur vers une fonction qui re‡oit une chaîne de
caractères comme paramètre, la fonction retourne une autre
chaîne de caractères (pointeur vers le type char) comme résultat.
*/

char * (*P) (char * ch) ;

char * majuscule ( char * ch )

{ int n = strlen(ch), i ;

for ( i = 0 ; i < n ; i++ )
if ( ch[i] >= 'a' && ch[i] f est une fonction qui retourne ...

(* ...) ==> un pointeur vers ....

int ... () ==> une fonction qui retourne un entier.

Donc : f est une fonction qui retourne un pointeur vers (une
fonction qui retourne un entier comme résultat).

Exercices :

Interpréter les déclarations suivantes :

1. float * (*P) [5] ;
2. char A[5] [2] ;
3. int * B[6] [3] ;

Donner quelques utilisations valides de ces déclarations.

D) Paramètres de type fonction :

On utilise les fonctions comme paramètres dans les exemples
de calcul numérique : résoudre f(x) = 0, calculer l'intégrale
définie, etc.

L'exemple suivant permet de voir une application de la
transmission d'une fonction comme argument d'une autre
fonction.


/* Fichier : PAR_FONC.C : paramètres de type fonction
Calcul de l'intégrale définie : Intégrale de f(x) dx où x
varie dans ]a, b[
Ce n'est pas une matière exigée pour les étudiants du cours. */

#include
#include
#include

/* première fonction à intégrer (facile à vérifier) :
on va l'intégrer entre 1 et e, le résultat théorique est
Log(e) - Log(1) = 1 - 0 = 1 */

float f (float x)
{
return 1 / x ;
}


/* On va l'intégrer entre 0 et PI sur 2
( théoriquement, ‡a vaut 1 ) */


float g ( float x )
{
return x * sin (x) ;
}


/* Méthode de trapèzes :
a) On divise ]a, b[ en N intervalles de longueur :
h = (b-a) / N

b) On estime l'intégrale de f(x) dx dans ]a, b[ avec la
formule :

Aires = h * [ {(f(a) + f(b)} / 2 + f(a+h) + f(a+2h) +
... + f(a+(N-1)h) ]
*/

void integrer ( float (*P) (float z), float a, float b, int n )
{
float h = (b-a) / n ;

printf("Début du calcul en divisant ]%6.3f, %6.3f[ en %d "
"intervalles", a, b, n);
printf("\n\n");

float aires = ( (*P) (a) + (*P) (b) ) / 2 ;

for (int i = 1 ; i < n ; i++)
aires += (*P) ( a + i * h ) ;
aires = h * aires ;

printf("L'intégrale entre %8.3f et %8.3f est %12.6f\n\n",
a, b, aires);
}
void main()
{ const float PI = 3.14159 ;

integrer (&f, 1.0, exp (1.0), 10000);

integrer (&g, 0.0, PI / 2, 12500);

printf("Appuyez sur Entrée ");
fflush(stdin);
getchar();
}

Exécution :

Début du calcul en divisant ] 1.000, 2.718[ en 10000 intervalles

L'intégrale entre 1.000 et 2.718 est 1.000000

Début du calcul en divisant ] 0.000, 1.571[ en 12500 intervalles

L'intégrale entre 0.000 et 1.571 est 0.999998

Appuyez sur Entrée

E) Fichier non texte (binaire) :

Un fichier binaire est un fichier dont chaque composant n'est pas
obligé d'être de type caractère comme un fichier texte. Il est
possible de créer des fichiers binaires "homogènes" : chaque
composant d'un tel fichier binaire est de même type structure et
ainsi de même taille : fichier binaire des employés, fichier binaire
des personnes, etc.


Les exemples suivants démontrent les techniques pour déclarer et
manipuler des fichiers binaires "homogènes".

Dans le fichier "Struct2.A95" présenté au chapitre 5, on a vu la
création, le tri d'un tableau des personnes :

......
#define MAX_PERS 25
#define LONG_NP 30

typedef struct
{ char nomPre[LONG_NP+1] ;
char sexe ;
float taille, poids ;
}
Personne ;

Personne pers[MAX_PERS] ;
int nbPers ;
.....

Exemple 1 :

Écrire une fonction et son appel pour créer un fichier binaire
des personnes.

Solution :

void fabriquer (Personne pers[], int nbPers)

{ int i ,
nbOctets = sizeof(Personne);

/* 1. Déclaration du fichier binaire */

FILE * aCreer = fopen("Personne.Bin", "wb");

/* 2. Écriture les informations dans le fichier binaire */

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

fwrite (&Pers[i], nbOctets, 1, aCreer);

/* 3. Fermeture du fichier binaire : */

fclose(aCreer);
}

Explications :

1. L'ouverture d'un fichier binaire est semblable à un fichier
texte, on ajoute un suffixe "b" pour désigner le mode d'accès
en binaire (binary).

2. L'écriture est comme suit :

fwrite(&variable, sizeof(variable), k, nomdu_Fichier) ;

On écrit le contenu de la variable, k blocs de n octets (où n
vaut sizeof(variable)) dans le fichier. Très souvent k vaut 1.

3. Appel d'une telle fonction :
.........
fabriquer(pers, nbPers);

Exemple 2 :

Écrire une fonction et son appel pour lire le fichier binaire
"Personne.Bin" qu'on vient de créer et afficher à l'écran la
liste des hommes qui mesurent plus que 1.80 mètre.

Solution :

1. Appel de la fonction :
Afficher ( "Personne.Bin", 'F', 1.80 ) ;

2. Écriture de la fonction :

void afficher( char * nomALire, char sexeVoulu,
float borne)
{ FILE * aLire = fopen(nomALire, "rb");

Personne unePers ;
int nbOctets = sizeof(Personne);

while ( fread(&unePers, nbOctets, 1, aLire),
!feof(aLire) )

if ( toupper(unePers.sexe) == sexeVoulu &&
unePers.taille > borne )

printf("%s %6.2f %6.2f %c\n", unePers.nomPre,
unePers.taille, unePers.poids,
unePers.sexe);
}

notes :

1. On voit l'opérateur séquentiel : on lit et on vérifie
la fin du fichier.

2. On reviendra plus tard avec autres détails sur les
fichiers binaires si le temps nous permet.

F) Opérateurs de manipulation de bits


& et (bit à bit)

| ou (bit à bit)

> décalage à droite

Représentation binaire d'un entier :

Un entier occupe 2 octets (16 bits) dans notre environnement
de travail.

29 (décimal) = 16 + 8 + 4 + 1

4 3 2 1 0
= 1 x 2 + 1 x 2 + 1 x 2 + 0 x 2 + 1 x 2

La représentation binaire de 29 est : 0000000000011101

Avec le même mécanisme, la représentation binaire
de 6 est 0000000000000110


Soit N vaut 29 et K vaut 6, on a :


N & K : 0000000000011101 (seulement 1 & 1 vaut 1)
& 0000000000000110
----------------
0000000000000100


N | K : 0000000000011101 (1 | 0, 0 | 1, 1 | 1 donne 1)
| 0000000000000110
----------------
0000000000011111


N > 2 : 0000000000000111


Exemple 1 :

Écrire une fonction permettant d'afficher un entier N en
décimal et en binaire.


Solution :

void afficherBinaire(int n)
{
/* 2 bytes = 16 bits pour un entier */
#define NB_BITS 16

/* en binaire, BORNE vaut 1000000000000000 */
#define BORNE 0x8000
printf("En décimal : n = %d, ", n);

printf("en binaire : n = ");

for (int i = 0 ; i < NB_BITS ; i++)
{ if (n & BORNE)
printf("1");
else printf("0");

n 1);
printf("\n\n");
printf("Le dernier bit de %3d est %d\n", n, n & 0x0001);
printf("Le dernier bit de %3d est %d\n", k, k & 0x0001);
printf("\n\n");
printf("Le nombre %3d est %s\n",n, n & 0x0001 ? "impair" : "pair");
printf("Le nombre %3d est %s\n",k, k & 0x0001 ? "impair" : "pair");
}

void main()
{ demontrerForme () ;
demontrerOperBit () ;

printf("Appuyez sur Entrée pour quitter ");
fflush(stdin);
getchar();
}

Exécution :

L'entier k en décimal (avec les chiffres de 0 à 9) 29

1) en base octale (base 8, chiffres de 0 à 7) k = 35

1 0
29 = 3 x 8 + 5 = 3 x 8 + 5 x 8 = 35 en octal

2) en base hexadécimale (base 16: chifres de 0 à 9, lettres
a à f) k = 1d
1 0
29 = 1 x 16 + 13 = 1 x 16 + 13 x 16 = 1d
en hexadécimal d correspond à 13 en base 16

n = 65535 (en décimal) et ffff (en hexadécimal)
plusGrand = 65535 (en décimal) et ffff (en hexadécimal)

Appuyez sur Entrée

Décimal Binaire
n = 29 0000000000011101
k = 6 0000000000000110
n & k = 4 0000000000000100 (et : bit à bit)
n | k = 31 0000000000011111 (ou : bit à bit)
n 1 = 14 0000000000001110 (décalage à droite)

Le dernier bit de 29 est 1
Le dernier bit de 6 est 0

Le nombre 29 est impair
Le nombre 6 est pair
Appuyez sur Entrée pour quitter

G) Opérateur de "cast" : (type)

Cet opérateur permet d'une conversion forcée (conversion explicite).
Supposons qu'on désire calculer S = 1/2 + 2/3 + 3/4 + ... + 99/100.
Soient les déclarations suivantes: float somme ; int numerateur ;

Le bloc : somme = 0;
for (numerateur = 1 ; numerateur