420 likes | 742 Views
Le langage C en une leçon. Mise en garde. Un cours exhaustif sur le langage C est en général une formation sur plusieurs jours à temps complet Les planches qui vont suivre sont destinées à éclairer les syntaxes utilisées dans les projets en langage C. Ressources.
E N D
Mise en garde • Un cours exhaustif sur le langage C est en général une formation sur plusieurs jours à temps complet • Les planches qui vont suivre sont destinées à éclairer les syntaxes utilisées dans les projets en langage C
Ressources • Support de cours de Patrick Corde sur le site de l ’IDRIS : www.idris.fr(chercher dans les supports de cours sur les langages) • Ouvrage de référence « The C programming language », publié en 1978 par Brian Kernighan et Dennie Ritchie, une seconde édition a été publiée en 1988.
Généralités • Langage successeur du langage B dans les années 60, premières normes en 1978 puis norme ANSI en 1989 et ISO en 1990. • Langage de base sous UNIX • Langage généraliste de syntaxe « simple » • Langage compilé (et non interprété) • Usage des pointeurs, récursivité
Intérêts du langage (cf. IDRIS, P. Corde) • Langage polyvalent permettant le développement de systèmes d'exploitation, de programmes applicatifs scientifiques et de gestion. • Langage structuré. • Langage évolué qui permet néanmoins d'effectuer des opérations de bas niveau (<< assembleur d'Unix >>). • Portabilité (en respectant la norme !) due à l'emploi de bibliothèques dans lesquelles sont reléguées les fonctionnalités liées à la machine. • Grande efficacité et puissance. • Langage permissif
prog_principal.c sous_prog1.c sous_prog2.c prog_principal.o sous_prog1.o sous_prog2.o prog_executable Comment créer un programme « exécutable » ? (fichiers sources) 1) Compilation (fichiers objets) 2) Edition des liens 0) Ecriture des fichiers sources *.c 1) Compilation : cc -c prog_principal.c (idem pour les sous programmes) 2) Liens : cc prog_principal.o sous_prog1.o sous_prog2.o -o prog_executable 3) Reste à lancer prog_executable … La procédure de compilation est souvent automatisée à l ’aide d ’un fichier nommé Makefile
Types de variables manipulées en C • Toutes les variables doivent être explicitement typées (pas de déclaration implicite comme en fortran) • Il y a globalement trois (quatre?) types de variables : • Les entiers : int, short int, longint • Les réels : float, double, long double • Les caractères : char • Rien … : void • exemples : short int mon_salaire; double cheese; char avoile;NB : la présence d ’une ou plusieurs étoiles devant le nom de la variables indique un pointeur, dans nos applications il s ’agira en général de tableaux.ex : double **mat permet de définir une matrice
Les structures • Il est possible de créer des ensembles de variables regroupées sous le même nom, on parle de variables agglomérées. • Exemple :struct individu {char nom[30];char prenom[50];int age; } • Utilisation : struct individu eleve,professeur,directeur;eleve.nom = « einstein »;eleve.prenom = « albert »;eleve.age = 25; • On peut utiliser des tableaux de structures, des pointeurs de structures ...
Le programme principal • La fonction « main » contient le programme principal • Le programme exécutable binaire commence par exécuter les instructions de ce programme principal • Sans fonction main, il est impossible de générer un programme exécutable
Structure d ’un programme • Un programme est composé de plusieurs fonctions qui échangent et modifient des variables • Chaque fonction, y compris la fonction main, comprend les éléments suivants : • Directives du préprocesseur • Déclaration d ’objets externes (variables, fonctions) • Interface de la fonction • Bloc d ’instructions
/*Directives du preprocesseur*/ #include <math.h> #include <stdio.h> #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* Variables externes, globales, déclarées dans le programme principal */ extern double a; extern long int imax; extern double * rho; extern double * fphy; extern double * fnum; void flux_numerique (void) /* Interface de la fonction */ { /* Debut de bloc d ’instructions */ long int i; /* Variable locale */ /* Schema decentre */ for (i = 0 ; i <= imax-1 ; i++) { fnum[i] = 0.5*(fphy[i] + fphy[i+1]) - 0.5*fabs(a)*(rho[i+1]-rho[i]); } } /* Fin de bloc */ Exemple de fonction
Qu’est-ce qu’un bloc d ’instructions ? • Un bloc débute par une accolade ouvrante et se termine par une accolade fermante • Il contient des déclarations de variables internes au bloc et des instructions • Les instructions peuvent être elles aussi des blocs ou des commandes du langage C. • Les lignes d ’instructions se terminent par des points virgules.
Directives du préprocesseur • #include <math.h> : insère les interfaces des fonctions mathématiques comme par exemple fabs() qui évalue la valeur absolue d ’un réel • #include <stdio.h> : entrées sorties standard • #define chaine1 chaine2 : remplacement littéral de la chaîne de caractères chaine1 par chaine2
Interface d ’une fonction • Syntaxe générale :type_retourné nom_de_fonction (paramètres);Le type retourné ainsi que le type des paramètres définit l ’interface ou le prototype de la fonction.Exemples : • void main(void); • void sauve(double *rho,long int imax,char filename,long int num); • int somme(int a, int b); • NB : Si une fonction f1 fait appel à une fonction f2, f1 doit avoir connaissance de l’interface de f2. Ceci se fait en rappelant l’interface de f2 dans le fichier contenant f1 (voir le code d ’advection linéaire)
Boucles et branchements • La boucle while :while(test) { instructions;} • exemple … int i; i = 0; while (i < 10) { printf(« i = %d \n »,i); i++; } …« Tant que i est inférieur à 10, écrire i à l ’écran, incrémenter i »
La boucle do … while :do { instructions; } while (test); • permet d ’exécuter au moins une fois les instructions avant d ’évaluer le test
La boucle for : for (initialisation ; test ; instruction) { instructions }; • Exemple : for (i = 0 ; i <= 50 ; i++) { printf(« i = %d\n »,i); }« Commencer à i =0, tant que i <= 50 , exécuter l ’instruction printf et incrémenter i »
Les tests • Syntaxes : • if (expression_test) bloc d ’instructions 1Si expression_test est vraie on exécute le bloc d ’instructions 1, sinon on passe à la suite. • if (expression_test) bloc d ’instructions 1else bloc d ’instructions 2Si expression_test est vraie on exécute le bloc d ’instructions 1 sinon on exécute le bloc 2.
Tests (suite) • Enchaînement de if :if (expression_test1) bloc_d_instructions_1else if (expression_test2) bloc_d_instructions_2 else if (expression_test3) bloc_d_instructions_3... else bloc_d_instructions_final
Expressions évaluées dans les tests • if (a = b) : erreur fréquente, expression toujours vraie ! • if (a == b) : a égal à b • if (a != b) : a différent de b • if (a > b) : a supérieur à b • if ((a >=b)&&(a>0)) : a supérieur ou égal à b et a positif • if ((a<=b)||(a>0)) : a inférieur ou égal à b ou a positif • ...
Visibilité des variables • Les variables déclarées à l ’intérieur du bloc d ’une fonction sont « visibles » à l ’intérieur du bloc uniquement • Le variables déclarées à l ’extérieur du bloc d ’une fonction sont globales et accessibles à toutes les fonctions du fichier en question
EXEMPLE : tout le texte suivant est dans le même fichierint a; /* variable globale a */ void fct1(double a) {a = 0.5; /* Modif du paramètre local a */}void fct2(void) {double a;a = 0.5; /* Modif de la variable locale a */}void fct3(void) {a = 1; /* Modif de la variable globale a */}
Visibilité des variables (suite) • Il peut être nécessaire que plusieurs fonctions accèdent aux mêmes variables. Pour cela, ces variables doivent être « visibles » des deux fonctions. On peut alors : • « passer » les variables en question dans les paramètres des fonctions • travailler sur des variables externes globales
Exemple : variables globales /* programme principal main.c */ int a,b; /* Variables globales a et b */ int somme(void); /* sous programme externe */ void main(void) { int c; a = 10.0; b = 5.0; c = somme( ); /* Appel au sous programme externe */ } Le sous programme somme n ’a pas de paramètres car il va travailler sur les variables globales a et b
Exemple : variables globales externes (suite) /* sous programme somme.c dans un fichier séparé du programme principal */ extern int a; extern int b; int somme(void) { int c; /* variables locale c */ c = a + b; return c; } le calcul de la somme est effectué sur les variables globales externes au fichier, déclarées dans main.c
Exemple : passage de paramètres /* programme principal main.c */ int somme(int a, int b); /* prototype du sous programme externe somme */ void main(void) { int a,b,c; /* Variables locales a, b et c */ a = 10.0; b = 5.0; c = somme( a,b); /* Appel au sous programme externe */ } La fonction somme dispose ici de deux paramètres a et b
Exemple : passage de paramètres (suite) /* sous programme somme.c */ int somme(int a, int b) { int c; /* variable locale c */ c = a + b; return c; } Dans ce cas, les variables a et b ne sont pas globales. On parle ici de passage d ’argument par valeur : le sous programme somme effectue une copie locale des variables a et b que lui transmet le programme principal et travaille ensuite sur ces copies. Conséquence : les variables a et b sont locales au sous programme somme.
L ’allocation dynamique de mémoire(les tableaux) • Le programme doit disposer d ’un espace mémoire où ranger les éléments d ’un tableau • Il se peut qu ’on ne sache pas le nombre d ’éléments à stocker dans un tableau avant l ’exécution du programme. On a alors deux solutions : • On surdimensionne le tableau en question en évaluant une taille maximale raisonnable de celui-ci • On alloue dynamiquement la mémoire au moment où on en a besoin (on peut aussi ensuite la restituer) : usage de la fonction malloc
#include <stdlib.h> void main(void) { double * tab; /* déclaration d ’un tableau de taille variable (en réalité c ’est un pointeur) */ double tabfixe[50]; /* Tableau de taille fixe, 50 éléments */ int imax; imax = 10; /* taille du tableau calculée à l ’exécution */ tab = (double *) malloc((imax+1)*sizeof(double)); tab[0] = 0.0; tab[10] = 5.0; /* tab[i] est le ième élément du tableau */ tabfixe[5] = tab[0]; ... } Allocation de mémoire (suite) Réserver (imax+1) espaces mémoire de la taille d ’un réel de type double On définit ainsi un tableau 1D nommé tab de variables réelles en double précision. Les indices valides des éléments vont de 0 à imax.
Déclaration des tableaux à deux dimensions (matrices) Il y a deux manières de déclarer les tableaux 2D : • On connaît a priori la taille du tableau :ex : double tab[50][40];définit un tableau nommé tab de 50 par 40 éléments.Les indices vont de 0 à 49 pour la première dimension et de 0 à 39 pour la seconde • On ne connaît pas la taille du tableau :ex : double ** tab; /* Il faut ensuite allouer un espace mémoire au tableau */
Déclaration d ’un tableau (suite) Le C ne manipule simplement que des tableaux 1D (des vecteurs). Il s’ensuit qu’un tableau 2D (matrice) sera considéré comme un vecteur dont chaque élément est un vecteur. Ce principe peut être étendu à des tableaux à n entrées.
0 1 2 3 0 0 0 0 1 1 1 1 2 2 2 2 4 4 4 4 5 5 5 5 Espace de stockage du tableau Représentation d ’un tableau 2D « Pointeurs » vers des vecteurs : tableau 1D dont chaque élément « pointe » vers un vecteur tab[2][4], tab[2] est l’adresse du début du vecteur d’indice 2, c’est un pointeur vers tab[2][0]
Déclaration des tableaux (suite) /*Exemple de tableau avec allocation de mémoire dynamique */ double ** tab; /* déclaration d ’un tableau 2D de réels */ int imax, jmax; imax = 50; jmax = 40; /* Allocation de mémoire pour la première dimension */ tab = (double**) malloc((imax+1)*sizeof(double*)); /* Allocation de mémoire pour la seconde dimension */ for(i = 0 ; i <= imax ; i++) tab[i] = (double *) malloc((jmax+1)*sizeof(double));
Qu ’est-ce qu ’un pointeur ? • Syntaxe : on utilise l ’étoile « * », ex : double * desneiges • Un pointeur contient l ’adresse ou est stockée la variable de type pointeur : dans l ’exemple ci-dessus, desneiges est l ’adresse ou l ’on stocke la variable *desneiges de sorte que l ’on peut écrire *desneiges = 10.0; mais ce qui suit est incorrect desneiges = 10.0; /* erreur */car desneiges est une adresse... • Les tableaux, comme nous l ’avons vu auparavant, disposent d ’une syntaxe un peu particulière
L ’opérateur de référence & • Lorsqu’une variable n ’a pas été déclarée en tant que pointeur, on peut tout de même accéder à son adresse à l ’aide de l ’opérateur « & »Si on définit une variable par « double cheese; »,alors l ’adresse de cette variable est « &cheese ;» • Pour les tableaux, si on a défini « double **tab; », alors tab est l ’adresse du premier élément du tableau soit &tab[0][0]
Les entrées sorties : printf, scanf • Le prototype de ces fonctions est dans <stdio.h> • printf permet d ’afficher du texte à l ’écran • scanf permet d ’entrer du texte au clavier • fprintf et fscanf permettent de lire et d ’écrire dans des fichiersLe programme de résolution de l’équation d’advection linéaire scalaire donne de nombreux exemples d’usage de ces fonctions
printf, scanf (suite) • Ces fonctions utilisent des formats qui permettent de lire/écrire des variables de différents types :%e , %f : réels%le, %lf : réels de type double%d : entiers%ld : entiers long int%s : chaîne de caractères%c : un caractère
printf, scanf (suite) • Le caractère « \ » (backslash) permet d ’utiliser certains caractères « non imprimables » , les plus utilisés sont les suivants :\n : fin de ligne\t : tabulation horizontale\a : sonnerie
printf, scanf : exemples #include<stdio.h> int i; printf(" i = %d \n",i); /* Imprime i = valeur de i et va à la ligne */ fscanf(infile, "%d ",&i); /* lit dans le fichier infile la valeur de i, on passe à fscanf l ’adresse de i c ’est à dire &i */ nb: Comme fscanf doit modifier la variable i, le paramètre passé à la fonction est l ’adresse de la variable i.
La suite ... • Il reste de nombreux points à détailler : • boucles et branchement : switch, break, goto, continue • les fichiers : le type FILE * ... • les bibliothèques standards : stdio.h, math.h, stdlib.h … • les structures • les pointeurs (pointeurs de structure, de fonction …), • les conversions de type, typedef… • plus un peu de pratique.