Vous êtes sur la page 1sur 58

Langage C:

Notions de Bases et Rappels


Master : ME / OM

18/19

Pr. Chaabelasri
Généralité
Caractéristiques
• Structuré
• Modulaire: peut être découpé en modules qui peuvent être
compilés séparément.
• Universel: n'est pas orienté vers un domaine d'application
particulier.
• Typé: tout objet C doit être déclaré avant d’être utilisé.
• Portable: sur n'importe quel système en possession d'un
compilateur C.
Pour les scientifiques, C ou Fortran ?

Fortran et C, sont les langages les plus demandés et utilisés pour le calcul intensif
Outils de développements
• Editeur de texte, compilateur en même temps
( IDE ) => Dev-C++

• Y a-t-il autre méthode ? Compilation en ligne


de commande ( c’est la méthode la plus
utilisée ).
– Compilation et édition de lien de façon rapide.
– Possibilité de travailler à distance.
– … etc
Fortran VS C
Les bases de C
Comment créer un programme
« exécutable » ?
prog_principal.c sous_prog1.c sous_prog2.c
1) Compilation

prog_principal.o sous_prog1.o sous_prog2.o

2) Edition des liens


prog_executable
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
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
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.
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 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
Exemple 1
Exemple 2 : Directives de la
bibliothèque OpenMP
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, long int
– 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
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)
Opérateurs et expressions
• Opérateurs arithmétique:
– *,/,+,-
–% modulo

19
Opérateurs et expressions
• Opérateurs relationnels:
– <,>,<=,=>

• Opérateurs d’égalité:
– ==, !=

• Opérateurs logiques:
– && et
– || ou
20
Opérateurs et expressions
• Opérateur conditionnel:
– result = mode > 0 ? 1 : 0;
if mode>0 then result=1 else result=0.

• Opérateurs d’assignation:
– =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^=

21
Fonctions en C
• Plusieurs fonctions pré-définies:
– printf(), sin(), atoi(), …

• Le prototype de ces fonctions sont dans


fichiers d’entête (header file)
– printf() dans stdio.h
– sin() dans math.h

22
Boucle et branchement
Besoin:
•Calcul répétitif
•Calcul avec condition ( comparaison …)
boucle while
• 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 »
boucle : do … while
• La boucle do … while :
do {
instructions;
} while (test);

• permet d ’exécuter au moins une fois les


instructions avant d ’évaluer le test
Boucle: for
• 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 1
}

– if (expression_test){
bloc d ’instructions 1
}else{
bloc d ’instructions 2
}
Tests (suite)
• Enchaînement de if :
if (expression_test1)
bloc_d_instructions_1
else if (expression_test2)
bloc_d_instructions_2
else if (expression_test3)
bloc_d_instructions_3
...
else
bloc_d_instructions_final
Choix multiple: « switch case »
/* Utilisation de switch case */
main()
{
char choix;

switch(choix)
{
case ‘a’ : fonctionA();

case ‘b’ : fonctionB();

case ‘c’ : fonctionC();

default : erreur(3);
}
}

29
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
• ...
Les tableaux
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) {
Allocation de mémoire (suite)

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 */

tab = malloc( sizeof(double) );

tab[0] = 0.0; Allouer la mémoire à la table ‘tab’


tab[10] = 5.0; /* tab[i] est
le ième élément
du tableau */
tabfixe[5] = tab[0];
...
}

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.

Exemples d’application: Génération de maillage dynamique, Estimation des erreurs de calculs….


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)
Attention !
 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.
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 fichiers

Le 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
Gestion des fichiers
Lecture/Ecriture dans fichiers
• Principe : identique aux lecture/écriture sur clavier/écran
En fait, le clavier et l'écran sont des fichiers particuliers

• Il faut simplement en plus "ouvrir" le fichier c-à-d


- l'associer à un fichier physique (sur disque)
- l'associer à un variable interne du pgm

• Un fichier peut être


- soit lu (read)
- soit (ré-)écrit (write) bande magnétique
- soit écrit à la fin (append)

• Déclaration :
FILE * variable-interne
ex : FILE * f; // f est une variable spéciale de type "fichier"
Ouverture de fichier : fopen
• variable = fopen("nom du fichier sur disque",mode d'ouverture)
mode d'ouverture :
syntaxe : chaîne de caractères
1er caractère :
'r' = read = lecture
'w' = write = écriture
'a' = append = écriture à la fin

fopen renvoie la valeur NULL (=0) si le pb sur le fichier physique

• Exemple
FILE * f;
f = fopen ("c:\texte.txt","r1234");
if (f==NULL) printf ("le fichier est absent\n");
else printf ("ok\n");

Fermeture du fichier
Supprime l'association fichier physique-variable interne
: fclose
• La variable interne peut être associée à un autre fichier physique

• Syntaxe
fclose (variable interne)

• Exemple :
FILE * f;
f= fopen (fichier1, "r");
….
fclose (f);

f= fopen (fichier2,"w");

Lecture dans fichier
• Lecture :
fgetc() et getc() ↔ getchar()
exemple
c=fgetc(f);

fscanf() ↔ scanf()
exemple
fscanf(f,"%d",i) le 1er argument est la variable interne fichier
• le caractère EOF indique la fin de fichier
• Ecriture
fputc () ↔ putchar()
fprintf() ↔ printf()
Exemple: Ecriture
Ecriture d’une lettre Ecriture d’une phrase, voir un text
Exemple: Lecture

Lecture du clavier

Lecture dans un fichier, mais un


Seul caractère.
Exemple: Lecture

Lecture dans un fichier, une


chaine de caractère.
Les fonctions
Les fonctions
• Exemples : fonctions d'E/S (scanf, printf, …), mathématiques (sin, cos, …)
• Organisation d'un programme :
type fonction1 (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
type fonction2 (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
...
void main (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
Type de la
Exemple
valeur de
Argument
retour

char minus_majus (char c1) {


char c2; /* déclarations locales */
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
Instructions
else c2=c1;
return (c2);
}
Valeur renvoyée
void main() {
char c,majuscule; Appel de la fonction
printf("Donner un caractere\n");
c = getchar(); getchar();
majuscule = minus_majus(c);
printf ("La majuscule de %c est %c\n",c,majuscule);
}
Définition de fonction : syntaxe
type_fonction nom_fonction (type_arg1 arg1, …, type_argn argn) {

return (valeur retournée);
}

Dans l'exemple précédent :


type_fonction : char, c'est le type de la valeur renvoyée par return
nom_fonction : minus_majus

Le nombre d'arguments est quelconque, éventuellement aucun, les parenthèses


doivent toujours figurer (ex: main () )
Type de la fonction
• Une fonction peut ne pas renvoyer de valeur.
• Exemple
void print_majus (char c1) {
char c2;
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
else c2=c1;
printf("la majuscule de % est %c, c1, c2);
return; /* ou bien return (); */
}
• Dans ce cas, le type de la fonction est : void
• Le type de la fonction ne peut être que :
• int, float, char
• ni tableau, ni autre type complexe
Instruction
1/ Indique la valeur de retour de la fonction.
return
2/ Arrête l'exécution de la fonction
char minus_majus (char c1) {
if (c1 >= 'a' && c1 <= 'z')
return (c1+'A'-'a');
else return (c1);
printf("%c",c1); // jamais executée
}
Pour les fonction de type void, return est optionnel
void print_majus (char c1) {
char c2;
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
else c2=c1;
printf("la majuscule de % est %c, c1, c2);
}
Appel des fonctions
• L'appel d'une fonction se fait en donnant son nom, suivi de la liste des
paramètres entre parenthèses. L'ordre des paramètres correspond à celui des
arguments.
• Exemple
float puiss (float x, int n) {
float y=1.0;
if (n>0) for (i=1;i<=n;i++) y = y*x;
else for (i=1;i<=n;i++) y = y/x;
return (y);
}

void main () {
float z,t;
z = puiss(10.7,2);
t = puiss (z, -6);
...
}
Règles de déclaration et d'appel
• Toute fonction ne peut appeler que des fonctions déclarées avant elle ou elle-même (la fonction main ne
peut pas s'appeler).
... f1 (..) {
...
}
... f2 (...) {
...
}
... f3 (...) {
...
}
void main (...) {
...
}
la fonction main peut appeler f1,f2,f3
la fonction f3 peut appeler f1,f2,f3
la fonction f2 peut appeler f1, f2
la fonction f1 peut appeler f1

• Lorsqu'une fonction s'appelle elle-même, on dit qu'elle est "récursive".


Déclarations en "avance"
• Règle précédente contraignante
• Solution : Prototype
En début de programme on donne le type de chaque fonction , son nom, le
nombre et les types des arguments
• Information suffisante pour le compilateur.

float puiss (float,int); /*Prototype de la fonction puiss*/


void main(){
puiss (10.2, 5); /*Appel avant déclaration*/
...}
float puiss (float x, int n){ /*Déclaration de la fonction */
float y=1.0;
if (n>0) for (i=1;i<=n;i++) y = y*x;
else for (i=1;i<=n;i++) y = y/x;
return (y);
}
Fichier "header"
• Conseil de programmation :
Dans un fichier ".h" déclarer les prototypes de
toutes les fonctions, par exemple malib.h
Dans le ou les fichiers ".c", insérer la directive
#include "malib.h"
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]