Éléments de base du langage C Programme de baccalauréat en informatique Algorithmique et programmation IFT-17582 Abder Alikacem Semaine 5 Éléments de base du langage C Édition Hiver 2008 Département d’informatique et de génie logiciel
Plan Représentation des données Les expressions en C Instructions et séquencement Les entrées/sorties Fichiers de caractères Structure d’un programme sans fonctions Structure d’un programme avec fonctions Les pointeurs Lecture dans les notes de cours: chapitres 5, 6, 7 et 8 (sauf section 8.2)
De la conception à l’implantation Algorithme Problème Transcription Description formelle Programme Compilation Langage de programmation Ordinateur Exécution Solution Langage machine
#include <stdio.h> #include <math.h> #define NFOIS 5 int main() { int i; float x; float racx; printf ("Bonjour!\n"); printf ("Je vais vous calculer %d racines carrées.\n", NFOIS); for (i = 1; i <= NFOIS; i++) { printf ("Donnez un nombre : "); scanf ("%f", &x); if (x < 0.0f) printf ("Le nombre %f ne possède pas de racine carrée.\n", x); else { racx = sqrt (x); printf ("Le nombre %f a pour racine carrée : %f\n", x, racx); } printf ("Travail terminé - Au revoir!"); return 0; } les directives pour la pré-compilation le bloc principal début du bloc liste d’instructions déclaration de variables y ’a -t-il des règles d’écriture? fin du bloc principal
Déclarations de variables Syntaxe: type identificateur ; Pourquoi? taille de l ’espace mémoire requis opérations associés codage, décodage lisibilité (choix des identificateurs) détection d’erreurs
Types de base Types entiers: char - 8 bits - [-128, 127] unsigned char - 8 bits - [0, 255] short - 16 bits - [-32 768, 32 767] unsigned short - 16 bits - [0, 65 535] int - 32 bits - [-2 147 483 648, 2 147 483 647] unsigned int - 32 bits - [0, 4 294 967 295] long int - 32 bits - [-2 147 483 648, 2 147 483 647] unsigned long int - 32 bits - [0, 4 294 967 295] Types réels: float - 32 bits -- 3.4 E +/- 38 (7 chiffres significatifs) double - 64 bits -- 1.7 E +/- 308 (15 chiffres significatifs) long double - 64 bits -- 1.2 E +/- 4932 (19 chiffres)
Le type Booléen? Pas de type booléen en C On traite ça avec les entiers: 0 pour FAUX Sinon VRAI (tous les entiers positifs de zéro!) Astuce: #define FAUX 0 #define VRAI 1 typedef int Bool;
Les blocs d’instructions #include <stdio.h> int main() { int n1, n2; printf( "Entrez les 2 entiers : " ); scanf( "%d%d", &n1, &n2 ); { int Somme; Somme = n1 + n2; printf( "Somme = %d", Somme ); } printf(" \n " ); { int Difference; Difference = n1 - n2; printf( "Difference = %d", Difference ); return 0; { int Somme = n1 + n2; printf( "Somme = %d", Somme ); } printf(" \n " ); { int Difference = n1 – n2; printf( "Difference = %d", Difference ); printf( "Somme = %d", n1 + n2 ); printf(" \n " ); printf( "Difference = %d", n1 – n2 );
Règles d’écriture sur le choix des identificateurs les mots clés du langage les séparateurs vous avez dit ‘format libre’? les commentaires
Le format libre #include <stdio.h> #include <math.h> #define NFOIS 5 int main() { int i ; float x ; float racx ; printf ("Bonjour\n"); printf ("je vais vous calculer %d racines carées\n", NFOIS); for (i= 0 ; i<NFOIS ; i++) { printf("Donnez un nombre : ") ; scanf ("%f" , &x) ; if (x<0.0f) printf ("Le nombre %f ne possède pas de racine carrée\n", x); else { racx = sqrt (x) ; printf("Le nombre %f a pour racine carrée : %f\n", x , racx) ; } } printf ("Travail terminé - Au revoir") ; return 0;}
Les commentaires /* programme de calcul de racines carrées */ /* commentaire s’étendant sur plusieurs lignes de programmes source */ /* -------------------------------------------------------- * * * * commentaires un peu plus esthétiques * * et mis en boîte, pouvant servir, * * par exemple, d’en-tête de programme * *--------------------------------------------------------- */
Choix des identificateurs Le choix des identificateurs: Sont formés d’une suite de caractères (alphanumériques). Le premier caractère doit être une lettre. Les majuscules et minuscules peuvent être utilisées… Seulement les 31 premiers caractères sont « significatifs ». Exemple valeur_5 nb_etudiants _total _89 ligne Ligne
Les mots clés du langage C auto default float register struct break do for return switch case double goto short typedef char else if signed union const enum int sizeof unsigned continue extern long static void while volatile
Les séparateurs À l’exception des caractères suivant: , = ; * ( ) [ ] { } tous les identificateurs et mots réservés doivent être séparés soit par un espace, soit par un caractère de fin de ligne. On peut donc écrire: int x,y; ou int x, y; Mais pas: intxy; ou intx,y;
Les entrées/sorties Code de format Type en jeu %d int %i int DEMANDER scanf(liste de codes de format, liste d’adresses de variables); AFFICHER printf(liste de codes de format, liste de variables); Code de format Type en jeu %d int %i int %li long int %hi short int %u unsigned int %lu unsigned long int %hu unsigned short int %f float et double %lf long double %e float ou double (scientifique) %c char
Exemples printf ("Bonjour "); printf ("Bonjour \n"); printf ("%i", nb_etuds); printf ("\n"); printf ("le nb d’étudiants est : %i\n", nb_etuds); printf ("%i %i %i\n", nb_etuds, nb_int, nb_ext); printf ("%i %f %f %s", nb_montants, somme, moyenne, "ok"); scanf ("%d", &n ); scanf ("%f", &r); scanf ("%u", &i);
Gabarit d’affichage /* entier avec 3 caractères minimum */ (n = 20) printf ("%3d", n); ^20 (n = 3) printf ("%3d", n); ^^3 (n = 2358) printf ("%3d", n); 2358 (n = -5200) printf ("%3d", n); -5200 /* notation décimale avec gabarit par défaut */ (x = 12.3456789) printf ("%f", x); 12.345679 /* notation décimale, gabarit minimum de 10 */ (x = 1.2345) printf ("%10f", x); ^^1.234500 /* notation décimale avec gabarit de précision*/ x = 1.2345 printf ("%10.3f", x); ^^^^^1.235
Le gabarit peut comprendre les signes +/- Remarques Le gabarit peut comprendre les signes +/- La fonction printf fournit en fait une “ valeur de retour ”. Il s’agit du nombre de caractères qu’elle a réellement affichés (ou la valeur -1 en cas d’erreur).
Les entrées au clavier ^ 1 2 a b ^ ^ 1 3 . 5 a 1 2 1 1 ^ ^ scanf( "%d", &x) x = 12 scanf( "%c", &c1) c1 = a scanf( "%c", &c2) c2 = b scanf( "%c", &c3) c1 = ^ scanf( "%f", &r) r = 13.5 scanf( "%c", &c4) c4 = a scanf( "%c", &c5) c5 = 1 scanf( "%d", &z) z = 211
Si on s’amusait! c e c i ^ m o n ^ p r e m i e r $ ^ Buffer Écran scanf( "%c", &c); while ( c != '$' ) { printf( "%d", c - 'a' + 'A' ); } C E C I M O N P R E M I E R
Les macros getchar() et putchar() L’expression c = getchar(); joue le même le rôle que scanf ("%c", c); L’instruction putchar (c); joue le même rôle que: printf ("%c", c); getchar() et putchar() sont des macros dont les instructions figurent dans la librairie stdio.h.
Les fichiers Un fichier est une structure de données qui permet de conserver les données manipulées par un programme. Les fichiers sont stockés en mémoire secondaire. Tout comme pour les entrées/sorties avec l'usager, les opérations sur des fichiers sont effectuées par des fonctions standards. Il existe 2 types de fichiers: Les fichiers binaires, dans lesquels les informations sont enregistrées directement sous la même forme que celle existant en mémoire principale. Les fichiers texte, qui contiennent les informations sous forme de caractères.
Les fichiers Un fichier texte peut être manipulé par n'importe quel programme de traitement de texte. Dans un fichier texte, chaque octet représente un caractère. Afin de pouvoir manipuler un fichier dans un programme, on aura besoin des 3 choses suivantes: Un pointeur sur le fichier (nom interne): FILE* fichier; Un chemin d'accès au fichier: a:\mesdonnees\donnees.tp4 Le mode d'ouverture du fichier: lecture "rt" écriture "wt" ajout "at"
Les fichiers Utilisation d’un fichier formaté en mode d’écriture #include <stdio.h> int main (void) { int n; FILE * sortie; sortie = fopen ("Resultat.txt", "wt"); do printf ("Donnez un nombre : "); scanf ("%d", &n); if (n!=0) fprintf (sortie, "%d\n", n); } while (n!=0); fclose (sortie); return 0; }
Les fichiers Utilisation d’un fichier formaté en mode lecture #include <stdio.h> int main (void) { int n; FILE * entree; entree = fopen ( "FichierEnEntree" , "rt"); while (! feof (entree) ) fscanf (entree, "%d", &n); printf ("\n%d", n); } fclose (entree); return 0;
Nouvelle assertion! /* ouverture d'un fichier en lecture */ entree = fopen( "toto.txt", "rt" ); if (entree == NULL ) { printf("Erreur d'ouverture du fichier\n"); exit (1); } /* A: entree est un pointeur non NULL */
Les constantes explicites Éléments de base du C Les constantes explicites nb_etuds + 1 (int) nb_etuds >= 16000 (int) nb_etuds >= 35000 (int) nb_litres + 1.5 (double) nb_etuds + 1.0 (double) nb_litres + 1.5f (float) nb_litres + .15e1 (double) nb_litres + 15e-1 (double)
Éléments de base du C Les constantes explicites 'a' (char) '\n' (char) '\t' (char) '\\' (char) '\' ' (char) '\0' (char) "Bonjour" (chaîne) "Message" (chaîne) "Bonjour! \n \n" (chaîne) "Entrez votre mot de passe:" (chaîne) "a" (chaîne)
Les constantes symboliques L’instruction #define permet de donner une valeur à un symbole quelconque. Avant la compilation, le pré-processeur parcourt l’ensemble du programme et remplace chaque occurrence du symbole par la valeur définie. Exemple: #define TAUX_TPS 0.07f
L’affectation Il est possible de remplacer l’expression: y = y + 2; par: y += 2; ou mieux encore : a = a * b; par : a *= b; Il en va de même pour tous les opérateurs arithmétiques: += -= *= /= %=
Opérateurs arithmétiques
Dans les algorithmes En C Opérateur Expression < x < y £ x £ y Opérateurs relationnels Dans les algorithmes En C Opérateur Expression < x < y £ x £ y <= x <= y > x > y ³ x ³ y >= x >= y = x = y == x == y ¹ x ¹ y != x != y
Opérateurs relationnels Particularité du C, les expressions booléennes ne s’évaluent pas à VRAI ou FAUX, mais plutôt par une valeur entière: 0 pour FAUX Différent de 0 pour VRAI Dans une expression booléenne, tout nombre entier différent de 0 est considéré comme la valeur VRAI.
Les opérateurs logiques
Les priorités d’évaluation
Les opérateurs d’incrémentation et de décrémentation Post-incrémentation: i++ et i-- sont des expressions. Ces expressions ont pour valeurs la valeur de i avant d’être incrémentée ou décrémentée. Pré-incrémentation: ++i et --i sont une autre forme d’expressions. Elles ont pour valeurs la valeur de i incrémentée ou décrémentée. Les opérateurs ++ et -- ne s’appliquent qu’aux entiers. Exemple int i; int j; i = 0; j = i++; if( --j ) { printf( "%d\n", ++j ); printf( "%d\n", --i ); printf( "%d\n", i++ ); }
Incrémentation et décrémentation u = 5; v = u++; a = ++u; u ????????? v a
Incrémentation et décrémentation u = 5; v = u++; a = ++u; u 5 v ????????? a
Incrémentation et décrémentation u = 5; v = u++; a = ++u; u 5 v a ?????????
Incrémentation et décrémentation u = 5; v = u++; a = ++u; u 6 v 5 a ?????????
Incrémentation et décrémentation u = 5; v = u++; a = ++u; u 7 v 5 a ?????????
Incrémentation et décrémentation u = 5; v = u++; a = ++u; u 7 v 5 a
Incrémentation et décrémentation #include <stdio.h> int main() { int x=1, y=2, z=3; printf("%d \n\n", ++x + (z = (x++ * --y))); return 0; }
Les alternatives en C La partie Si condition Alors se traduit par: if (condition) La partie Sinon se traduit par: else Le else est toujours facultative: if(a < b) if(a < b) { { t = b; t = b; b = a; b = a; a = t; a = t; } } { … }
Les alternatives en C Le cas de if imbriqués Le bout de code suivant devrait-il être interprété de cette facon: if (a <= b) if (b <= c) printf("ordonne"); else printf("non ordonne"); Ou de la façon suivante: if (a <= b) if (b <=c)printf("ordonne");
Les alternatives en C Les if imbriqués Un else est toujours rattaché au dernier if rencontré et qui n’a pas de else qui l’accompagne. Le code précédent doit être interprété comme suit: if (a <= b) if (b <= c) printf("ordonne"); else printf("non-ordonne");
L’instruction switch S’écrit sous la forme suivante: switch(variable entière) { case v1: …; break; case v2: …; … }
#include <stdio.h> L’instruction switch #include <stdio.h> int main(void) { int n; printf("Donnez un entier"); scanf("%d", &n); switch(n) case 0: printf("nul\n"); break; case 1: printf("un\n"); case 2: printf("deux\n"); } printf("au revoir!"); return 0;
L’instruction switch #include <stdio.h> int main(void) { int n; printf("Donnez un entier"); scanf("%d", &n); switch(n) case 0: printf("nul\n"); break; case 1: printf("un\n"); case 2: printf("deux\n"); default: printf("grand\n"); } printf("au revoir!"); return 0;
Les boucles en C L’instruction Tant Que condition Faire du Pseudo-code se traduit en C par l’instruction while. Cette instruction a la forme: while (condition) Exemple. #include <stdio.h> int main(void) { int n; int somme; somme = 0; while (somme < 100) printf("\nEntrez un nombre: "); scanf("%d", & n); somme = somme + n; } printf("\nSomme obtenue: %d", somme); return 0; }
Les boucles en C En C, l’instruction do while vient remplacer l’instruction Répéter Tant Que condition du Pseudo-code. Elle a la forme: do { /* bloc d’instructions */ }while(condition)
Les boucles en C Exemple d’une validation de données #include <stdio.h> int main(void) { int n; do printf(" \nDonnez un entier positif > "); scanf("%d", &n); printf(" \nVous avez fourni l’entier : %d", n); } while (n <= 0); printf("Reponse correcte\n"); return 0; }
Les boucles en C #include <stdio.h> int main(void) { int n; int i; do printf("\nDonnez un entier positif > "); i=scanf("%d", &n); fflush(stdin); } while (i!=1 || n <= 0); printf("\nReponse correcte\n"); return 0; }
Les boucles en C #include <stdio.h> #define FPURGE() while(getchar() != '\n') int main(void) { int n; int i; do printf("\nDonnez un entier positif > "); i=scanf("%d", &n); FPURGE(); } while (i!=1 || n <= 0); printf("\nReponse correcte\n"); return 0; }
Les boucles en C L’instruction Répéter n Fois en Pseudo-code devient l’instruction for en C. Elle a la forme: for (initialisation; condition d’arrêt; instruction de fin de boucle) Exemple. #include <stdio.h> int main(void) { int i; for (i = 1; i <= 5; i++) printf( "Bonjour!\n "); printf( "%d fois.\n ", i); } return 0;
Les boucles en C L’exemple précédent est équivalent à celui-ci: #include <stdio.h> int main(void) { int i; i = 1; while (i <= 5) printf( "Bonjour!\n "); printf( "%d fois.\n" , i); i++; } return 0;
Synthèse
La structure d’un programme (sans fonction) #include . . . #define . . . . . . int main() { déclaration des variables (avec commentaires appropriés) traduction de l’algorithme (y compris les commentaires) return 0; }
La structure d’un programme (sans fonction) Bloc B1 : Obtenir un entier n 0 et en afficher la factorielle calculable. { Objectif : Obtenir un entier n, compris entre 0 et la valeur de l ’entier maximum dans le factoriel est calculable, et en afficher la factorielle. Méthode : Itération de produit. Besoins : Un entier n, NMAX dont NMAX! est calculable Connus : NMAX Entrée : Sortie : Résultat : n! Hypothèse: n 0 et NMAX n }
La structure d’un programme (sans fonction) Début { B1} Demander n {A: n 0 et NMAX n} i 0 fact 1 Tant que i < n Début i i + 1 fact fact * i Fin Afficher fact {A: fact contient n!} Fin {B1}
La structure d’un programme (sans fonction) #include <stdio.h> #define NMAX 12 int main(void) { int n, i; long int fact; do printf ("..un entier entre 0 et %d: ", NMAX); fflush(stdin); i=scanf ("%li", &n); } while (i !=1 || (n < 0 || n > NMAX)); /*A:n 0 et NMAX n*/ i = 0; fact = 1; while (i < n) { i = i + 1; fact = fact * i; } printf ("%li\n", fact); /*A:fact contient n!*/ return 0; }
La structure d’un programme (sans fonction) #include <stdio.h> #define NMAX 12 #define FPURGE() while(getchar()!='\n') int main(void) { int n, i; long int fact; do { printf ("..un entier entre 0 et %d: ", NMAX); i=scanf ("%li", &n); FPURGE(); } while (i !=1 || (n < 0 || n > NMAX)); /*A:n 0 et NMAX n*/ i = 0; fact = 1l; while (i < n) { i = i + 1; fact = fact * i; } printf ("%li\n", fact); /*A:fact contient n!*/ return 0; }
Blocs vs fonctions Algorithme C Bloc principal (B1) B2 B3 Bn .. main() Debut … FIN C main() f2 f3 fn .. { … } Problème
Exemple Bloc Principal (B1) Bloc B2 char toMaj( char c ) #include <stdio.h> #define FIN_SEQUENCE ‘.’ char toMaj( char c ); int main() { printf ("Bienvenue!\n"); c = getchar(); while ( c != FIN_SEQUENCE ) cMaj = toMaj( c ); printf("%c", cMaj ); } printf("\nTerminer!\n" ); return 0; Bloc Principal (B1) DEBUT DEMANDER c TANT QUE ( c Fin_Sequence ) Transformer le caractère en majuscule ( Bloc B2) AFFICHER cMaj FIN AFFICHER "Terminer! " char c, cMaj; Bloc B2 DEBUT car car – ‘a’ + ‘A’ FIN char toMaj( char c ) { car = c - 'a' + 'A'; return car; } char car;
Les fonctions et les entrées et sorties Bloc Bn: Somme de 2 entiers … { Besoins: un entier a et un entier b } { Entrées: a et b } { Sortie: res, la somme de a et b } DEBUT res a + b FIN int Somme … /* Besoins: un entier a et un entier b */ /* Entrées: a et b */ /* Sortie: res, la somme de a et b */ { res = a + b; } ( a, b ) int int res; return res;
Étapes de production d’un programme Solution1 C Solution2 Problème Solution Algo Analyse Conception Implantation Résolution Ambiguité Préciser au maximum les données en jeu Découper le problème en sous problème Solution abstraite (pas trop de détails) Bien documenté Identifier les fonction- nalités pouvant être réutilisées. Décider de certains choix d’implantation: - Affichage (%3d, etc.) - Lecture (getchar, scanf, …) Transformer les assertions en code de validation INDENTATION & Docs Décider, ultimement, de certains choix d’optimisation
Cycle de programmation devant l’ordinateur Algorithme Problème Transcription Description formelle Programme Compilation Langage de programmation Ordinateur Exécution Solution Langage machine
Saisie, Exemple Compile, Link #include<stdio.h> #define Fin_Sequence '.' char toMaj( char c ); int main(void ) /* Connus: une constante Fin_Sequence égale à ‘.’ */ { char c, cMaj; printf ("Bienvenue!"); c = getchar(); while ( c != Fin_Sequence ) { cMaj = toMaj( c ); printf("%c", cMaj ); } printf("Terminer!" ); return 0; char toMaj( char c ) /* Besoins: un entier c */ /* Entrées: c */ /* Sortie: car, un caractère en majuscule */ { char car; car = c - 'a' + 'A'; return car; Saisie, Compile, Link Exemple
La compilation d’un programme Afin de parvenir à générer le code d’un programme, les compilateurs procèdent en 4 phases: l’analyse lexicale; le crible; l’analyse syntaxique; l’analyse sémantique.
La compilation d’un programme L’analyse lexicale: C’est durant cette phase que le compilateur identifie les différents symboles qui composent le programme Le crible fait le tri parmi les symboles identifiés lors de l’analyse lexicale entre les symboles utiles, les symboles inutiles et les symboles qui s’adressent directement au compilateur.
La compilation d’un programme L’analyse syntaxique: C’est durant cette phase que le compilateur cherche à retrouver la structure du programme. Cette tâche est accomplie en analysant les symboles lexicaux. C’est également durant cette étape que les erreurs de syntaxe du programmeur sont détectées. L’analyse sémantique: Cette phase sert à détecter les autres erreurs que l’on peut commettre en écrivant un programme, comme entre autres, les erreurs de typage.
Qu'est-ce qu'un compilateur ? Les compilateurs Qu'est-ce qu'un compilateur ? int main(void) { int a; int b; int c; c = a + b; return 0; } 00100010010101 01100100011101 11011101000110 00110111011000 01110011010100 10110111101011 11101101000010 11101011000101 01110101000100 compilateur Correspondance en langage machine Fichier source
Les compilateurs portabilité Le rôle d ’un compilateur Un compilateur donné est toujours spécifique au processeur pour lequel il a été conçu. Par contre, si l'on suit bien les standards établis pour les langages de programmation que l'on utilise, les compilateurs de plusieurs processeurs différents seront en mesure de comprendre nos programmes et de générer le code machine correspondant. portabilité
Les compilateurs La portabilité d ’un programme
Les compilateurs La portabilité d ’un programme Certains des problèmes liés à la portabilité disparaissent avec une standardisation du langage, le plus souvent menée à bien par deux organisations: ANSI et ISO Organization. Cependant, des fabricants du compilateur ajoutent des “extensions” au langage qui le rendent incompatible avec certains environnements deprogrammation.
Programme en langage "machine" Compilation vs Interprétation Interpréteur Programme Processeur Programme en langage "machine" Compilateur
Compilation vs Interprétation Programme Interprété (VB, PHP, Java ...) Plus lent On passe toujours par l'interpréteur (lourd) Programme Compilé (C, Pascal ...) Plus rapide 1 compilation pour N exécutions Autonomie vis à vis du langage d'origine Code exécutable non portable