Académique Documents
Professionnel Documents
Culture Documents
ENIT - 1A GE
3
Structure d’un programme C
Un programme C comporte:
Une entête (header) constitué de méta-instructions ou
directives destinées au préprocesseur (Exp: Inclusion de
librairies de fonctions prédéfinies)
Un bloc Principal appelé main() qui représente la
fonction principale
Le corps des fonctions placées avant ou après le
main() dans un ordre quelconque, les unes après les
autres
Partout, les variables et les fonctions font l’objet d’une
déclaration précisant leurs types
4
Fichier C (extension .c)
/* exemple de programme C :
- somme des nb de 1 à 10 et affichage
de la valeur*/ En C le programme principal 1
#include <stdio.h> 0 s'appelle toujours main
void main () 1
déclarations de variables de
{ type entier (cases mémoire
2
int somme; int i; 2 pouvant contenir un entier)
somme = 0; 3 instruction d'affectation de 3
for (i = 1; i <= 10; i++) valeur à la variable somme
4 { instructions exécutées
somme = somme + i; en séquence
} l'instruction entre accolades 4
printf ("%d\n", somme); 5 est exécutée pour les valeurs
} de i allant de 1 à 10
affiche à l'écran la valeur de 5
l'entier contenu dans somme
5
Exemple: Calcul de la surface d’un cercle
En Pascal En C
6
Structure d’un programme C
Programme typique en C
include
Main() est toujours la
1ère fonction appelée
main()
instructions
7
1. Les éléments de base du langage C
Considérations Lexicales
Les Identificateurs
Un identificateur est constitué de lettres (‘a’...‘z’, ‘A’..‘Z’), de
chiffres (‘0’...‘9’) et éventuellement du caractère souligné (‘_’).
Un identificateur doit impérativement commencer par une
lettre ou un ’_’ et ne pas faire partie de la liste des mots
réservés.
Attention, une distinction est faite entre les caractères
majuscules et minuscules (NbLignes et nblignes sont deux
identificateurs différents).
La norme ANSI a fixé à 31 le nombre de caractères
significatifs d’un identificateur bien que la longueur de ce
dernier puisse être plus importante.
9
Les mots clés
10
Les Commentaires
11
L’appel d’une bibliothèque : #include
12
Les éléments de base du langage C
Les Constantes
Définition Constante
14
Nombres Entiers
15
Nombres Flottants
Une constante littérale est l'expression d'un nombre flottant si elle présente, dans
l'ordre :
une suite de chiffes décimaux (la partie entière)
un point, qui joue le rôle de virgule décimale
une suite de chiffres décimaux (la partie fractionnaire)
une des deux lettres E ou e
éventuellement un signe + ou -
une suite de chiffres décimaux
Les trois derniers éléments forment l'exposant.
Exemple : 123.456E-78.
On peut omettre :
la partie entière ou la partie fractionnaire, mais pas les deux
le point ou l'exposant, mais pas les deux.
Exemples : .5e7, 5.e6, 5000000., 5e6
Une constante flottante est supposée de type double, à moins de comporter un suffixe
explicite :
les suffixes F ou f indiquent qu'elle est du type float
les suffixes L ou l indiquent qu'elle est du type long double
Exemples : 1.0L, 5.0e4f
16
Caractères et Chaînes de Caractères(1)
17
Caractères et Chaînes de Caractères(2)
18
Caractères et Chaînes de Caractères(3)
19
Déclaration Constante
20
Les éléments de base du langage C
22
C possède 4 types de base
Plage de
Type Signification Taille (bits)
valeurs
int Entier 16 (2 o) -32768 à +32767
Réel en double
double 64 (8 o) ±10-307 à ± 10+308
précision
char Caractère 8 (1 o)
23
Les littéraux entiers
Les notations utilisées pour écrire les littéraux entiers sont :
la notation décimale,
la notation octale, (doit commencer par 0)
et la notation hexadécimale. (doit finir par h et commencer par 0 si le
premier est une lettre).
Il existe la possibilité d’ajouter un attribut (ou modificateur ) :
unsigned : entier ou caractère non signé, de 0 à 65535
long : entier double longueur, de -2.109 à +2.109
short : entier court, de -32768 à +32768
On peut omettre d’écrire int avec un attribut.
24
Les littéraux réels
25
Les littéraux caractères(1)
Les littéraux caractères permettent de désigner les valeurs
entières correspondant aux code ASCII des caractères. Les
notations possibles sont :
la notation éditable : présentation du caractère entre apostrophes,
la notation décimale : valeur décimale du code ASCII correspondant,
la notation octale : valeur octale du code ASCII correspondant,
la notation hexadécimale : valeur hexadécimale du code ASCII
correspondant,
la notation symbolique : certains littéraux caractères possèdent des
notations symboliques, le tableau ci-dessous les résume :
26
Les littéraux caractères(2)
Notation Signification
'\a' Signal sonore
'\n' Passage à la ligne suivante
'\r' Retour du curseur en début de ligne
'\b' Déplacement du curseur d’un caractère vers la gauche
'\f' Déplacement vers la page suivante
'\t' Déplacement du curseur d’une tabulation vers la droite
27
Les littéraux chaînes de caractères
28
Définition de nouveaux types
29
Les éléments de base du langage C
Les Opérateurs
Les opérateurs
Symboles simples :
( ) [ ] . ! ~ < > ? :
= , + - * / % & ^
Symboles composés :
-> ++ -- <= >= == != && || << >>
+= -= *= /= %= <<= >>= |= &= ^=
Tous ces symboles sont reconnus par le compilateur comme des
opérateurs.
Il est interdit d'insérer des caractères blancs à l'intérieur d'un
symbole composé. En outre, il est conseillé d'encadrer par des
blancs toute utilisation d'un opérateur.
31
Classes d’opérateurs
32
Les opérateurs arithmétiques
+ : addition,
- : soustratction,
* : multiplication,
/ : division :
division entière : si les 2 opérandes sont entières,
division réelle : si au moins une des 2 opérandes est réel
% : reste de la division
Exp: float x;
x = 3 / 2; /*affecte à x la valeur 1 */
x = 3 / 2.0; /*affecte à x la valeur 1.5 */
L'opérateur % ne s'applique qu’à des opérandes de type entier.
En C, il n’y a pas d’opérateur effectuant l’élévation à la puissance.
Donc, il faut utiliser la fonction pow(x,y) de la librairie math.h pour
calculer xy
33
Les opérateurs relationnels
== : égal,
!= : différent,
> : Strictement supérieur,
>= : supérieur ou égal,
< : Strictement inférieur,
<= : inférieur ou égal.
34
Les opérateurs logiques
! : négation unaire,
&& : ET logique binaire
|| : OU logique binaire
Le type booléen n’existe pas en C, la valeur
retournée par ces opérateurs est un entier
qui vaut 0 si la condition est faux.
35
Les opérateurs de traitement de bits
~ : complément à 1 0
1
0
1
1
0
36
Les opérateurs d’incrémentation et de décrémentation
37
L’opérateur d'affectation
38
Les opérateurs d'affectation composée
39
L'opérateur virgule
40
L'opérateur de conversion de type
41
L'opérateur de référencement &
42
Opérateur de déréférencement *
43
L’Opérateur SizeOf()
44
Priorité des opérateurs(1)
Exemple
45
Priorité des opérateurs(2)
Exemple
Dans l'instruction: X = 2*(A+3)*B+4*C;
(En reprenant les valeurs du dernier exemple, le résultat
sera 164)
46
Priorité des opérateurs(3)
Classes de priorités
Priorité 1 (la plus forte): ( )
Priorité 2: ! ++ --
Priorité 3: * / %
Priorité 4: + -
Priorité 6: == !=
Priorité 7: &&
Priorité 8: ||
47
Priorité des opérateurs(4)
48
Priorité des opérateurs(5)
Exemples
L'expression 10+20+30-40+50-60 sera évaluée comme suit:
10+20 ==> 30 - 30+30 ==> 60 - 60-40 ==> 20 - 20+50 ==> 70 - 70-60 ==> 10
49
Conversion Implicite de type(1)
50
Conversion Implicite de type(2)
Exemple
51
Conversion Implicite de type(3)
52
Conversion Implicite de type(4)
53
Les éléments de base du langage C
Les Variables
Déclaration de variables
55
Les fonctions d’E/S standards
Les fonctions d’E/S standards
scanf()
lecture formatée de données
putchar()
écriture d'un caractère
getchar()
lecture d'un caractère
57
Ecriture formatée de données(1)
Syntaxe
printf( "Chaine de format" {, argument});
58
Ecriture formatée de données(2)
Les spécifications de format commencent toutes par le
caractère pourcent (’%’), peuvent ensuite contenir une
indication de largeur (minimale), de précision (maximale)
et finalement un caractère code pour le type de
l’argument. Voici les codes de type essentiels
• d pour les entiers
• f pour les nombres flottants sous forme décimale
• e pour les nombres flottants sous forme scientifique
• c pour les caractères
• s pour une chaîne de caractères
• % pour afficher le caractère ’%’
59
Ecriture formatée de données(3)
Remarque :
Il doit y avoir au moins autant d’arguments
qu’il en est prévu dans la chaîne de format;
sinon, le résultat n’est pas prévisible
60
Ecriture formatée de données(4)
Exercice:
Déclarer une constante contenant le nom d’une
classe exemple "1AENIT"
Déclarer une var nom de type chaîne de caractère
et lui assigner une valeur exemple "ahmed"
Déclarer 2 variables test et exam de type réel et
leur assigner 2 valeurs quelconques exemple 12.5
et 17.0
Afficher à l’écran le nom de la classe, le nom de
l’étudiant, les notes de test et examen ainsi que sa
moyenne ( test * 0.4 + exam * 0.6 )
61
Ecriture formatée de données(5)
Remarques :
62
Lecture formatée de données(1)
63
Lecture formatée de données(2)
64
Lecture formatée de données(3)
65
Lecture formatée de données(4)
66
Lecture formatée de données(5)
Remarques:
On peut placer la longueur de la variable entre le signe
% et la lettre spécificateur. Par exemple « %3d »
indique qu’on va lire un entier de 3 chiffres et le reste
des chiffres sont ignorés. scanf("%3d",&i) ;
Si l’on sépare les spécificateurs de format par des
espaces ou par des virgules alors les valeurs à lire
seront séparées par les mêmes séparateurs.
Exercice:
Changer l’Exercice précédent de telle façon que le nom de
l’étudiant et ses notes seront demandés de l’utilisateur.
67
La fonction putchar
68
La fonction getchar()
69
2. Les structures de contrôle
Contrôle de flux
71
Les structures conditionnelles
Elles permettent au programme de suivre
plusieurs chemins différents, en fonction de
conditions que l’on teste en cours
d’exécution. Le programme choisira un
chemin et n’exécutera qu’un sous-ensemble
des instructions données. Les structures
conditionnelles de C sont
• if ... else ... (si ... sinon ...)
• switch (choix multiple : Selon)
• l’opérateur ( ... )? ... : ...; (alternative)
72
Les structures de contrôle
L’instruction if … else
if...else... (1)
Syntaxe
Sans alternative:
if ( expression )
instruction ou bloc d’instructions
Avec alternative:
if ( expression )
instruction1 ou bloc d’instructions 1
else
instruction2 ou bloc d’instructions 2
74
if...else... (2)
if (expression) action1 ;
else action2 ;
fin de l'instruction if
calcul …
Vrai "calcul juste" if (z == 3) {
z = z+10 printf("calcul juste");
z=3 ? z=z+10;
}
Faux else printf ("problème");
"problème" printf ("%d",z);
…
Imprimer z
75
if...else... (3)
76
if...else... (4)
Exp1:
if (A-B)
printf("A est différent de B\n");
else
printf("A est égal à B\n");
Exp2:
if (a==0)
printf("a est nul\n");
else if (a<0)
printf("a est strictement négatif\n");
else
printf("a est strictement positif\n");
77
if...else... (5)
Exemple: Lire un nombre à partir du clavier et tester si ce nombre est pair ou impair
#include <stdio.h>
void main() {
int nbr;
printf ("Entrez un nombre SVP ");
scanf ("%d", &nbr);
if (nbr > 0)
if (nbr % 2 == 0)
printf ("C\'est un nombre pair\n");
else
printf ("C'est un nombre impair\n");
else
printf ("C\'est un nombre negatif\n");
getch();
}
78
Opérateur (condition )? (vrai): (faux)
79
Opérateur (condition )? (vrai): (faux)
80
Les structures de contrôle
z? z=4
"PB1"
autres
"PB2"
Action 3
…
switch (z) {
case 3 : printf("ok"); break;
case 4 : printf("PB1"); break;
default : "printf ("PB2");
};
…
82
switch(2)
switch (expression )
{
case constante1: liste d'instructions 1
break;
case constante2: liste d'instructions 2
break;
...
case constanten: liste d'instructions n
break;
default: liste d'instructions
break;
}
83
Switch(3)
84
switch(4)
Exemple:
int mois ;
scanf(" %d" ,&mois) ;
switch ( mois )
{
case 1 : printf(" janvier" ) ;
break ;
case 2 : printf(" fevrier" ) ;
break ;
…
case 12 : printf(" décembre" ) ;
break ;
default : printf(“erreur”)
}
85
3. Les structures Répétitives
Les structures itératives
87
Les structures Répétitives
La boucle while
La boucle while(1)
Syntaxe
while ( expression )
instruction ou bloc d’instructions
Fonctionnement
Lorsque le programme atteint l’instruction while, il évalue
l’expression entre parenthèses.
Si le résultat est vrai (différent de zéro), alors il exécute
l’instruction ou le bloc d’instructions qui suit puis il
recommence. Il évalue une nouvelle fois l’expression, etc.
Si le résultat est faux (au premier, deuxième,... ou Xème
passage) alors il n’exécute pas l’instruction et arrête de boucler,
passant à l’instruction suivante.
89
La boucle while(2)
Remarques :
Le test s’effectuant au début, il est très possible
que l’instruction ne soit jamais exécutée.
L’expression entre les parenthèses est
obligatoire.
Il est important que l’instruction ou le bloc puisse
influencer sur l’évaluation de l’expression entre
les parenthèses (par exemple en modifiant une
variable de fin ou un compteur), sinon, le
programme bouclera indéfiniment (CTRL-C pour
l’arrêter).
90
La boucle while(3)
Exemple1:
/* Afficher les nombres de 1 à 10 */
int I = 0 ;
while (I<10)
printf(“%d\n”,++I);
Exemple2:
/* calculer la somme des N premiers entiers naturels*/
int somme=0, i = 0;
while (i<N)
{
somme += i;
i++ ;
}
91
La boucle while(4)
92
Les structures Répétitives
do
{
bloc d’instructions;
} while ( expression );
Le bloc d'instructions est exécuté au moins une
fois et jusqu’à ce que l'expression fournit une
valeur égale à zéro (false).
Les { } ne sont pas obligatoires, si le bloc
d’instruction contient une seule instruction.
95
while.. et do…while(3)
do - while est comparable à la structure Repeat du
langage Pascal si la condition finale est inversée
logiquement.
La structure do - while est semblable à la structure
while, avec la différence suivante :
while évalue la condition avant d'exécuter le bloc d'instructions.
do - while évalue la condition après avoir exécuté le bloc
d'instructions. Ainsi le bloc d'instructions est exécuté au moins
une fois.
Une application typique de do - while est la saisie de
données qui doivent remplir une certaine condition.
96
La boucle do…while(4)
97
Les structures Répétitives
La boucle for…
La boucle for ... (1)
L’instruction for est une sorte de while plus complexe
que l’on utilise généralement dans le cas de boucles où
le nombre d’itérations est connu. Son usage n’est
toutefois pas limité à ce seul cas comme dans d’autres
langages.
Syntaxe
for ( initialisation; continuation; progression )
instruction ou bloc d’instructions
initialisation, continuation et progression sont des
expressions C quelconques.
99
La boucle for ... (2)
Fonctionnement
Lorsque le programme arrive à une instruction for, l’expression
initialisation est évaluée. Ensuite, l’expression continuation est
évaluée, si elle est vraie, l’instruction ou le bloc est exécuté et
l’expression progression est évaluée. On revient ensuite à
l’évaluation de l’expression de continuation et on recommence
jusqu’à ce qu’elle soit fausse.
Il est possible que les expressions initialisation, continuation ou
progression soient vides; dans ce cas, il faut tout de même mettre
le bon nombre de points-virgule. Il manquera une des étapes et le
comportement sera différent. Si l’expression continuation est
absente, la boucle ne s’arrêtera jamais, à moins qu’une instruction
de rupture appropriée soit rencontrée.
100
La boucle for ... (3)
Equivalence
Une instruction for peut
être transformée en son
équivalent while:
initialisation;
while ( continuation) {
instruction ou bloc d’instructions
progression;
}
101
La boucle for ... (4)
102
Les ruptures
• goto (aller à)
• break (arrêter)
• continue (passer à l’itération suivante)
• return (sortie de fonction)
• exit (fin du programme)
103
4. Les Tableaux
Les tableaux
105
Tableaux à une dimension(1)
106
Tableaux à une dimension(2)
Remarques:
Le nombre d’éléments peut être n’importe quelle
expression constante dont le résultat est un nombre
entier positif.
Cette expression ne peut pas contenir de variables ou
d’appels de fonctions.
Le premier élément du tableau est obligatoirement
d’indice 0. ainsi, les éléments sont numérotés de 0 à
taille-1.
107
Tableaux à une dimension(3)
108
Tableaux à une dimension(4)
Exemple
Typedef int TabEntier[10]
TabEntier notes;
notes[2] = 5;
Opérations sur les tableaux :
Il n’y a pas d’opérations globales (affectation, comparaison)
sur les tableaux ou sur des tranches de tableaux dans le
langage.
La seule exception est pour l’initialisation du tableau lors de
sa déclaration.
Exemple: int tab[2]={10,15};
109
Tableaux à une dimension(5)
110
Les tableaux et les chaînes(1)
111
Les tableaux et les chaînes(2)
112
Tableaux à plusieurs dimensions(1)
113
Tableaux à plusieurs dimensions(2)
114
Exercice
115
5. Les Fonctions
Les fonctions(1)
sous-programme :
Un sous-programme est un petit programme
qui résout une partie du problème que le
programme principal doit traiter.
On peut ainsi isoler la résolution de sous
problèmes différents dans autant de sous-
programmes.
Intérêt : résoudre de très gros problèmes
117
Les fonctions(2)
dans un programme, on peut avoir besoin de
faire plusieurs fois la même chose, avec de
petites variations Dans ce cas, il est plus
économique d’appeler un sous programme avec
des paramètres différents, plutôt que d’écrire
plusieurs fois les mêmes lignes de code.
118
Les fonctions(3)
119
Définition de fonctions(1)
120
Définition de fonctions(2)
121
Définition et déclaration "ANSI"(1)
122
Définition et déclaration "ANSI"(2)
123
Définition et déclaration "ANSI"(3)
124
Définition et déclaration "ANSI"(4)
125
Retour d’une fonction(1)
L’instruction return expression; n’importe
où dans le bloc de la fonction a pour effet de
sortir de la fonction et de retourner le résultat
de l’expression au point d’appel de la
fonction.
Il peut y avoir plusieurs sorties de ce type
dans une même fonction. Nous pouvons
affecter la valeur retournée à une variable ou
continuer à l’utiliser dans l’expression qui a
déclenché l’appel de la fonction. Nous
pouvons aussi négliger ce résultat
complètement.
126
Retour d’une fonction(2)
127
Appel d’une fonction
128
Exercice
129
Récursivité(1)
130
Récursivité(2)
131
Paramètres d’une fonction
132
Passage de paramètre à une fonction(1)
133
Passage de paramètre à une fonction(2)
Résultat de ce programme:
debut programme principal :
a=2 b=5
debut fonction :
a=2 b=5
fin fonction :
a=5 b=2
fin programme principal :
a=2 b=5
134
Passage de paramètre à une fonction(3)
Pour qu'une fonction modifie la valeur d'un de ses arguments, il faut qu'elle
ait pour paramètre l'adresse de cet objet et non sa valeur.
Par exemple, pour échanger les valeurs de deux variables, il faut écrire :
135
Passage de paramètre à une fonction(4)
void main()
{
int a = 2, b = 5;
printf("debut programme principal :\n a = %d \t b = %d\n",a,b);
echange(&a,&b);
printf("fin programme principal :\n a = %d \t b = %d\n",a,b);
}
136
Portée des variables
137
Portée des variables: Exemple(1)
int n; Résultat
void fonction()
{ appel numero 1
n++; appel numero 2
printf("appel numero %d\n",n); appel numero 3
return; appel numero 4
} appel numero 5
main()
{
int i;
for (i = 0; i < 5; i++)
fonction();
}
138
Portée des variables: Exemple(2)
139
Portée des variables: Exemple(3)
int n = 10; Résultat
void fonction()
{ appel numero 1
static int n; appel numero 2
n++; appel numero 3
printf("appel numero %d\n",n); appel numero 4
return; appel numero 5
}
main()
{
int i;
for (i = 0; i < 5; i++)
fonction();
}
140
Remarques
141
6. Application
143
Problème de tri(2)
144
Tri par sélection
145
Tri par échange
Idée : L’idée est de faire remonter les grands éléments à la fin
du tableau. Comparer toute paire d’éléments contigus et les
permuter s’ils ne sont pas dans le bon ordre. Répéter le
processus jusqu’à ce qu’il n’y ait plus de permutations.
do{
permute = 0 ;
for ( i = 0 ; i < N‐1 ; i = i+1)
if ( T[ i ] > T[ i+1 ] )
{
X = T[ i ] ; T[ i ] = T[ i+1 ] ; T[ i+1 ] = X ;
permute = 1;
}
} while ( permute) ;
146
Tri par insertion
147
Tri rapide(1)
148
Tri rapide(2)
149
7. Les types composés
Les types composés
151
Les types composés
Les structures
Les structures(1)
C'est un mécanisme permettant de grouper un certain nombre de variables de types
différents au sein d'une même entité en utilisant le concept d’enregistrement.
Un enregistrement est un ensemble d'éléments de types différents repérés par un
nom. Les éléments d'un enregistrement sont appelés des champs. Le langage C
possède le concept d'enregistrement appelé structure.
Déclaration de structure :
Méthode 1 : déclaration en précisant un nom pour la structure
struct personne
{
char nom[20];
char prenom[20];
int n_cin;
};
On peut ensuite utiliser ce type structure pour déclarer des variables, de la manière
suivante :
struct personne p1,p2; /* qui déclare deux variables de type
struct personne de noms p1 et p2 */
153
Les structures(2)
Méthode 2 : déclaration en précisant un nom pour la structure et en déclarant des variables
struct personne
{
char nom[20];
char prenom[20];
int n_cin;
} p1,p2;
Déclare les deux variables p1 et p2 et donne le nom personne à la structure. Là aussi, on pourra
utiliser ultérieurement le nom struct personne pour déclarer d'autres variables.
154
Les structures(3)
Accès aux membres d’une structure :
Pour désigner un membre d'une structure, il faut utiliser l'opérateur de sélection de
membre '.' (Point).
Exemple:
struct personne
{
char nom[20];
char prenom[20];
int age;
};
struct personne p1,p2;
p1.age=15 ; /* accès au troisième champs + affectation */
scanf ("%s",p1.nom) ; /* lecture du premier champs */
printf("%d",p1.age) ; /* affichage du troisième champs */
p2.nom[0] = 'X';
155
Les structures(4)
156
Les structures(5)
Affectation de structures:
On peut affecter une structure à une variable
structure de même type.
struct personne pr1,pr2;
pr1 = pr2 ;
Comparaison de structures:
Aucune comparaison n'est possible sur les
structures, même pas les opérateurs == et !=.
157
Tableau de structures
158
Composition de structures
Composition de structures:
Une structure permet de définir un type. Ce type peut être utilisé dans la
déclaration d’une autre structure comme type d’un de ses champs.
Exemple :
struct date
{
unsigned int jour;
unsigned int mois;
unsigned int annee ;
};
struct personne
{
char nom[20];
char prenom[20];
struct date d_naissance;
};
159
Exercice
Créer une structure point{int num;float x;float y;}
Saisir 4 points, les ranger dans un tableau puis les afficher.
160
Les énumérations
161
Les unions(1)
162
Les unions(2)
163
Les unions(3)
Puis on peut les utiliser de la manière suivante :
a1.typ_val = ENTIER;
a1.u.i = 10;
a2.typ_val = FLOTTANT;
a2.u.f = 3.14159;
Si on passe en paramètre à une fonction un
pointeur vers une struct arith, la fonction
testera la valeur du membre typ_val pour
savoir si l'union reçue possède un entier ou
un flottant.
164
8. Les Pointeurs
Les Pointeurs
Notion de pointeur
Intérêt des pointeurs en C
La plupart des langages de programmation offrent la
possibilité d'accéder aux données dans la mémoire de
l'ordinateur à l'aide de pointeurs, c.-à-d. à l'aide de
variables auxquelles on peut attribuer les adresses
d'autres variables.
En C, les pointeurs jouent un rôle primordial dans la
définition de fonctions:
Comme le passage des paramètres en C se fait
toujours par la valeur, les pointeurs sont le seul
moyen de changer le contenu de variables déclarées
dans d'autres fonctions.
De même le traitement de tableaux et de chaînes de
caractères dans des fonctions serait impossible sans
l'utilisation de pointeurs
167
Adressage de variables
168
Adressage direct
La valeur d'une variable se trouve à un endroit spécifique
dans la mémoire interne de l'ordinateur. Le nom de la
variable nous permet alors d'accéder directement à cette
valeur.
Adressage direct: Accès au contenu d'une variable par
le nom de la variable.
Exemple:
169
Adressage indirect
On peut copirer l'adresse d’une variable dans une variable
spéciale P, appelée pointeur. Ensuite, on peut retrouver
l'information de la variable A en passant par le pointeur P.
Adressage indirect: Accès au contenu d'une variable, en
passant par un pointeur qui contient l'adresse de la variable.
Exemple: Soit A une variable contenant la valeur 10 et P un
pointeur qui contient l'adresse de A. En mémoire, A et P
peuvent se présenter comme suit:
170
Pointeur
Un pointeur est une variable spéciale qui peut contenir
l'adresse d'une autre variable.
En C, chaque pointeur est limité à un type de données. Il peut
contenir l'adresse d'une variable simple de ce type ou l'adresse
d'une composante d'un tableau de ce type.
Si un pointeur P contient l'adresse d'une variable A, on dit que
'P pointe sur A'.
Remarque : Les pointeurs et les noms de variables ont le même
rôle: Ils donnent accès à un emplacement dans la mémoire
interne de l'ordinateur. Il faut quand même bien faire la
différence:
Un pointeur est une variable qui peut 'pointer' sur différentes
adresses.
Le nom d'une variable reste toujours lié à la même adresse.
171
Déclaration d'un pointeur
<Type> *<NomPointeur>
déclare un pointeur <NomPointeur> qui peut recevoir des
adresses de variables du type <Type>
Exemple:
Signifie que
int *PNUM; *PNUM est du type int
ou
PNUM est un pointeur sur int
ou
PNUM peut contenir l'adresse d'une
variable du type int
172
Les opérateurs de base(1)
173
Les opérateurs de base(2)
174
Les opérateurs de base(3)
Le programme complet effectuant les transformations de l'exemple ci-dessus
peut se présenter comme suit:
main() Ou bien main()
{ {
/* déclarations */ /* déclarations */
short A = 10; short A, B, *P;
short B = 50; /* traitement */
short *P; A = 10;
/* traitement */ B = 50;
P = &A; P = &A;
B = *P; B = *P;
*P = 20; *P = 20;
return 0; return 0;
} }
175
Les opérations élémentaires sur un pointeur(1)
Priorité de * et &
Les opérateurs * et & ont la même priorité
que les autres opérateurs unaires (la
négation !, l'incrémentation ++, la
décrémentation --). Dans une même
expression, les opérateurs unaires *, &, !,
++, -- sont évalués de droite à gauche.
Si un pointeur P pointe sur une variable X,
alors *P peut être utilisé partout où on peut
écrire X.
176
Les opérations élémentaires sur un pointeur(2)
Exemple
Après l'instruction P = &X; les expressions suivantes, sont
équivalentes:
Y = *P+1 Y = X+1
*P = *P+10 X = X+10
*P += 2 X += 2
++*P ++X
(*P)++ X++
177
Les opérations élémentaires sur un pointeur(3)
Le pointeur NUL
Seule exception: La valeur numérique 0 (zéro) est utilisée pour
indiquer qu'un pointeur ne pointe 'nulle part'.
int *P;
P = 0;
Finalement, les pointeurs sont aussi des variables et peuvent être
utilisés comme telles. Soit P1 et P2 deux pointeurs sur int, alors
l'affectation
P1 = P2;
copie le contenu de P2 vers P1. P1 pointe alors sur le même objet
que P2.
178
Les opérations élémentaires sur un pointeur(4)
Résumé:
Après les instructions:
int A;
int *P;
P = &A;
A désigne le contenu de A et &A désigne l'adresse de A
P désigne l'adresse de A et *P désigne le contenu de A
En outre:
&P désigne l'adresse du pointeur P
*A est illégal (puisque A n'est pas un pointeur)
179
Les Pointeurs
Pointeurs et tableaux
Adressage des composantes d'un tableau(1)
181
Adressage des composantes d'un tableau(2)
182
Adressage des composantes d'un tableau(3)
Exemple
Soit A un tableau contenant des éléments de type
float et P un pointeur sur float:
float A[20], X;
float *P;
Après les instructions,
P = A;
X = *(P+9);
X contient la valeur du 10-ième élément de A, (c.-à-d.
celle de A[9]). Une donnée du type float ayant besoin
de 4 octets, le compilateur obtient l'adresse P+9 en
ajoutant 9 * 4 = 36 octets à l'adresse dans P.
183
Adressage des composantes d'un tableau(4)
184
Adressage des composantes d'un tableau(5)
185
Résumons
Les variables et leur utilisation int A; déclare une variable
simple du type int
A désigne le contenu de A
&A désigne l'adresse de A
186
Résumons
int *P;
déclare un pointeur sur des éléments du type int.
P peut pointer sur des variables simples du type int ou sur les
composantes d'un tableau du type int.
P désigne l'adresse contenue dans P
(Cette adresse est variable)
187
Les Pointeurs
189
Arithmétique des pointeurs(2)
190
Arithmétique des pointeurs(3)
191
Arithmétique des pointeurs(4)
- négatif, si P1 précède P2
- zéro, si P1 = P2
- positif, si P2 precède P1
- indéfini, si P1 et P2 ne pointent pas dans le même tableau
192
Arithmétique des pointeurs(5)
193
Les Pointeurs
De la même façon qu'un pointeur sur int peut contenir l'adresse d'un nombre isolé ou
d'une composante d'un tableau, un pointeur sur char peut pointer sur un caractère
isolé ou sur les éléments d'un tableau de caractères. Un pointeur sur char peut en
plus contenir l'adresse d'une chaîne de caractères constante et il peut même être
initialisé avec une telle adresse.
Affectation
On peut attribuer l'adresse d'une chaîne de caractères constante à un pointeur sur
char:
Exemple
char *C;
C = "Ceci est une chaîne de caractères constante";
Nous pouvons lire cette chaîne constante (p.ex: pour l'afficher), mais il n'est pas
recommandé de la modifier, parce que le résultat d'un programme qui essaie de
modifier une chaîne de caractères constante n'est pas prévisible en ANSI-C.
195
Pointeurs sur char et chaînes de caractères constantes(2)
Initialisation
Un pointeur sur char peut être initialisé lors de la déclaration si on lui affecte l'adresse
d'une chaîne de caractères constante:
char *B = "Bonjour !";
Remarque:
Il existe une différence importante entre les deux déclarations:
char A[] = "Bonjour !"; /* un tableau */
char *B = "Bonjour !"; /* un pointeur */
A est un tableau qui a exactement la grandeur pour contenir la chaîne de
caractères et la terminaison '\0'. Les caractères de la chaîne peuvent être
changés, mais le nom A va toujours pointer sur la même adresse en
mémoire.
B est un pointeur qui est initialisé de façon à ce qu'il pointe sur une chaîne
de caractères constante stockée quelque part en mémoire. Le pointeur peut
être modifié et pointer sur autre chose. La chaîne constante peut être lue,
copiée ou affichée, mais pas modifiée
196
Pointeurs sur char et chaînes de caractères constantes(3)
Modification
Si nous affectons une nouvelle valeur à un pointeur sur une chaîne
de caractères constante, nous risquons de perdre la chaîne
constante. D'autre part, un pointeur sur char a l'avantage de pouvoir
pointer sur des chaînes de n'importe quelle longueur:
Exemple
char *A = "Petite chaîne";
char *B = "Deuxième chaîne un peu plus longue";
A = B;
Maintenant A et B pointent sur la même chaîne; la "Petite chaîne" est
perdue:
197
Pointeurs sur char et chaînes de caractères constantes(4)
Remarque:
Les affectations discutées ci-dessus ne peuvent pas être effectuées avec des tableaux de
caractères:
Exemple
char A[45] = "Petite chaîne";
char B[45] = "Deuxième chaîne un peu plus longue";
char C[30];
A = B; /* IMPOSSIBLE -> ERREUR !!! */
C = "Bonjour !"; /* IMPOSSIBLE -> ERREUR !!! */
Dans cet exemple, nous essayons de copier l'adresse de B dans A, respectivement l'adresse de la
chaîne constante dans C. Ces opérations sont impossibles et illégales parce que l'adresse
représentée par le nom d'un tableau reste toujours constante.
Pour changer le contenu d'un tableau, nous devons changer les composantes du tableau l'une
après l'autre (p.ex. dans une boucle) ou déléguer cette charge à une fonction de <stdio> ou
<string>.
198
Pointeurs sur char et chaînes de caractères constantes(5)
Conclusions:
Utilisons des tableaux de caractères pour déclarer
les chaînes de caractères que nous voulons
modifier.
Utilisons des pointeurs sur char pour manipuler
des chaînes de caractères constantes (dont le
contenu ne change pas).
Utilisons de préférence des pointeurs pour
effectuer les manipulations à l'intérieur des
tableaux de caractères.
199
Les Pointeurs
Pointeurs et tableaux à
deux dimensions
Pointeurs et tableaux à deux dimensions(1)
201
Pointeurs et tableaux à deux dimensions(2)
Problème
Comment pouvons-nous accéder à l'aide de pointeurs aux éléments de
chaque composante du tableau, c.à-d.: aux éléments M[0][0], M[0][1], ... ,
M[3][9] ?
Discussion
Une solution consiste à convertir la valeur de M (qui est un pointeur sur un
tableau du type int) en un pointeur sur int. Par exemple:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{10,11,12,13,14,15,16,17,18,19},
{20,21,22,23,24,25,26,27,28,29},
{30,31,32,33,34,35,36,37,38,39}};
int *P;
P = M; /* conversion automatique */
Cette dernière affectation entraîne une conversion automatique de l'adresse
&M[0] dans l'adresse &M[0][0]. (Remarquez bien que l'adresse transmise reste
la même, seule la nature du pointeur a changé).
202
Pointeurs et tableaux à deux dimensions(3)
203
Pointeurs et tableaux à deux dimensions(4)
Exemple
Les instructions suivantes calculent la somme de tous les éléments du tableau M:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{10,11,12,13,14,15,16,17,18,19},
{20,21,22,23,24,25,26,27,28,29},
{30,31,32,33,34,35,36,37,38,39}};
int *P;
int I, SOM;
P = (int*)M;
SOM = 0;
for (I=0; I<40; I++)
SOM += *(P+I);
Remarque:
Lors de l'interprétation d'un tableau à deux dimensions comme tableau
unidimensionnel il faut calculer avec le nombre de colonnes indiqué dans la
déclaration du tableau.
204
Les Pointeurs
Tableaux de pointeurs
Tableaux de pointeurs(1)
Si nous avons besoin d'un ensemble de pointeurs du même type, nous pouvons les
réunir dans un tableau de pointeurs.
Déclaration d'un tableau de pointeurs
<Type> *<NomTableau>[<N>]
déclare un tableau <NomTableau> de <N> pointeurs sur des données du type
<Type>.
Exemple
double *A[10];
déclare un tableau de 10 pointeurs sur des rationnels du type double dont les
adresses et les valeurs ne sont pas encore définies.
Remarque
Le plus souvent, les tableaux de pointeurs sont utilisés pour mémoriser de façon
économique des chaînes de caractères de différentes longueurs. Dans la suite, nous
allons surtout considérer les tableaux de pointeurs sur des chaînes de caractères.
Initialisation
Nous pouvons initialiser les pointeurs d'un tableau sur char par les adresses de
chaînes de caractères constantes.
206
Tableaux de pointeurs(2)
Exemple
char *JOUR[] = {"dimanche", "lundi", "mardi", "mercredi", "jeudi",
"vendredi", "samedi"};
déclare un tableau JOUR[] de 7 pointeurs sur char. Chacun des pointeurs
est initialisé avec l'adresse de l'une des 7 chaînes de caractères.
207
Tableaux de pointeurs(3)
On peut afficher les 7 chaînes de caractères en fournissant les adresses
contenues dans le tableau JOUR à printf :
int I;
for (I=0; I<7; I++)
printf("%s\n", JOUR[I]);
Comme JOUR[I] est un pointeur sur char, on peut afficher les premières
lettres des jours de la semaine en utilisant l'opérateur 'contenu de' :
int I;
for (I=0; I<7; I++)
printf("%c\n", *JOUR[I]);
208
Tableaux de pointeurs(4)
int *D[]; déclare un tableau de pointeurs sur des éléments du type int
209
Les Pointeurs
Allocation dynamique
de mémoire
Déclaration statique de données(1)
211
Déclaration statique de données(2)
Pointeurs
Le nombre d'octets à réserver pour un pointeur dépend
de la machine et du 'modèle' de mémoire choisi, mais il
est déjà connu lors de la compilation. Un pointeur est
donc aussi déclaré statiquement. Supposons dans la
suite qu'un pointeur ait besoin de p octets en mémoire.
(En DOS: p =2 ou p = 4)
Exemples
double *G; /* réservation de p octets */
char *H; /* réservation de p octets */
float *I[10]; /* réservation de 10*p octets */
212
Allocation dynamique
Problème
Souvent, nous devons travailler avec des données dont nous ne pouvons pas prévoir
le nombre et la grandeur lors de la programmation. Ce serait alors un gaspillage de
réserver toujours l'espace maximal prévisible. Il nous faut donc un moyen de gérer la
mémoire lors de l'exécution du programme.
Exemple
Nous voulons lire 10 phrases au clavier et mémoriser les phrases en utilisant un
tableau de pointeurs sur char. Nous déclarons ce tableau de pointeurs par:
char *TEXTE[10];
Pour les 10 pointeurs, nous avons besoin de 10*p octets. Ce nombre est connu dès le
départ et les octets sont réservés automatiquement. Il nous est cependant impossible
de prévoir à l'avance le nombre d'octets à réserver pour les phrases elles-mêmes qui
seront introduites lors de l'exécution du programme ...
Allocation dynamique
La réservation de la mémoire pour les 10 phrases peut donc seulement se faire
pendant l'exécution du programme. Nous parlons dans ce cas de l'allocation
dynamique de la mémoire.
213
La fonction malloc
214
L'opérateur unaire sizeof(1)
sizeof A s'évalue à 20
sizeof B s'évalue à 50
sizeof 4.25 s'évalue à 8
sizeof "Bonjour !" s'évalue à 10
sizeof(float) s'évalue à 4
sizeof(double) s'évalue à 8
215
L'opérateur unaire sizeof(2)
Exemple
Nous voulons réserver de la mémoire pour X valeurs du type int; la
valeur de X est lue au clavier:
int X;
int *PNum;
printf("Introduire le nombre de valeurs :");
scanf("%d", &X);
PNum = malloc(X*sizeof(int));
exit
S'il n'y a pas assez de mémoire pour effectuer une action avec
succès, il est conseillé d'interrompre l'exécution du programme à
l'aide de la commande exit (de <stdlib>) et de renvoyer une valeur
différente de zéro comme code d'erreur du programme.
216
Exemple(1)
Le programme suivant lit 10 phrases au clavier, recherche des blocs
de mémoire libres assez grands pour la mémorisation et passe les
adresses aux composantes du tableau TEXTE[]. S'il n'y a pas assez
de mémoire pour une chaîne, le programme affiche un message
d'erreur et interrompt le programme avec le code d'erreur -1.
Nous devons utiliser une variable d'aide INTRO comme zone
intermédiaire (non dynamique). Pour cette raison, la longueur
maximale d'une phrase est fixée à 500 caractères.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main() {
/* Déclarations */
char INTRO[500];
char *TEXTE[10];
int I;
217
Exemple(2)
/* Traitement */
for (I=0; I<10; I++) {
gets(INTRO);
/* Réservation de la mémoire */
TEXTE[I] = malloc(strlen(INTRO)+1);
/* S'il y a assez de mémoire, ... */
if (TEXTE[I]) /* copier la phrase à l'adresse fournie par malloc, ... */
strcpy(TEXTE[I], INTRO);
else { /* sinon quitter le programme après un message d'erreur. */
printf("ERREUR: Pas assez de mémoire \n");
exit(-1);
}
}
return 0; }
218
Remarques(1)
Dans le programme :
#include <stdio.h>
main()
{
int i ;
int *p ;
p=&i ;
}
i et *p sont identiques et on n’a pas besoin d’allocation
dynamique puisque l’espace mémoire à l’adresse &i est
déjà réservé pour un entier.
219
Remarques(2)
220
La fonction free
Si nous n'avons plus besoin d'un bloc de mémoire que nous avons
réservé à l'aide de malloc, alors nous pouvons le libérer à l'aide de
la fonction free de la bibliothèque <stdlib>.
free( <Pointeur> )
libère le bloc de mémoire désigné par le <Pointeur>; n'a pas d'effet si
le pointeur a la valeur zéro.
Remarque:
La fonction free peut aboutir à un désastre si on essaie de libérer de la
mémoire qui n'a pas été allouée par malloc.
La fonction free ne change pas le contenu du pointeur; il est conseillé
d'affecter la valeur zéro au pointeur immédiatement après avoir libéré le
bloc de mémoire qui y était attaché.
Si nous ne libérons pas explicitement la mémoire à l'aide de free, alors
elle est libérée automatiquement à la fin du programme.
221