Académique Documents
Professionnel Documents
Culture Documents
• Introduction au langage C
• Types de base, variables, constantes
• Opérateurs et expressions
• Les entrées sorties en C
• Les structures de contrôle
• Les tableaux
• Les pointeurs
2
1
Introduction
• Définition : Un langage informatique est un
ensemble de caractères, de symboles et de
mots-clés régis par des règles. Cet ensemble est
utilisé pour donner des instructions à un
ordinateur.
Introduction
• Définition 1 : Un algorithme est une suite finie
d’opérations (instructions) à exécuter dans un
ordre déterminé sur un certain nombre d’objets
considérés comme données, puis il fournit un
nouvel ensemble d’objets constituants les
résultats souhaités. En plus il est écrit avec un
langage humain, non compréhensible par la
machine.
2
Introduction
• Définition 2 : Un programme est l’expression (la
traduction) d’un algorithme dans un langage de
programmation donné.
Introduction
3
Introduction
• La description d’un algorithme consiste à :
– Définir les données d’entrée et les résultats
souhaités du problème.
– Définir les relations entre les données.
– Définir les opérations à réaliser sur les données.
Introduction
– L’étape de compilation consiste à :
• Traduire le programme en langage machine.
• Cette traduction s’effectue à l’aide d’un programme appelé
compilateur.
4
Programmation en langage C
• Le langage C est l’un des programmes les plus
utilisés dans le domaine de la programmation.
Ceci est dû au fait que les programmes
développés avec le langage C soient lisibles,
facilement modifiables et portables.
• Le langage C est un langage de haut niveaux
(structures de contrôles et structures de
données); aussi c’est un langage du bas niveau
(manipulation de la mémoire: bit et l’octet) .
C’est le point fort et la particularité de ce
langage. 9
10
5
– compilation du programme source à l’aide d’un
programme particulier appelé compilateur. Ce
programme vérifie la syntaxe, la grammaire et la
sémantique du programme source, et génère par la suite
le programme exécutable portant le même le nom que le
programme source mais avec une extension différente
qui est ".exe"
– Ce programme exécutable n’est autre que la traduction en
langage machine du programme source ;
<directives de compilation>
<déclaration des noms externes>
<textes (corps) des fonctions>
6
Structure d’un programme C.
• La partie déclaration des noms externe on trouve
la déclaration des noms globaux dont la portée
est l’ensemble du programme, comme une
variable globale.
7
exemples
15
Corps du programme
• La fonction main ou le programme principal
commence toujours par l’instruction
main()
{
...
}
• L’exécution du programme consiste à exécuter
les instructions de la fonction main qui se
trouvent entre les accolades.
16
8
Instructions
• Une instruction est une action à faire par
l’ordinateur. Elle peut être simple comme elle peut
être composée.
#include<stdio.h>
main()
{
printf(" premier programme en c ") ;
getch();
} 18
9
Structure d’un programme C.
#include<stdio.h>
#define N 10
int i=1;
main()
{
printf(" N = %d et i = %d " , N , i ) ;
getch();
}
19
20
10
Déclaration et stockage des variables
Exemples d’identifiants :
21
11
Déclaration et stockage des variables
– Un type : indique
• la nature des informations qui seront représentées
dans la variable,
• La déclaration du type d’un objet permet de définir
l’espace mémoire qu’il lui sera alloué et les fonctions
qui lui sont applicables
• les opérations qu'on peut appliquer à cette variable.
– Une valeur :
• C’est la donnée stockée dans la variable.
• Une variable doit être déclarée avant son
utilisation!
23
12
Type caractère
• Le mot clé char est utilisé pour déclarer les
caractères.
• Le langage C ne fait pas de distinction entre un
caractère (exemple le caractère ‘a’) est son
code ASCII (97 en décimal pour le caractère
‘a’).
• Un caractère a besoin d’un octet pour être
représenté.
25
Type entier
• Le mot clé int est réservé pour déclarer les entiers. Ce mot
clé peut être précédé par l’un des deux attributs de
précision short et long.
• De même, le mot clé int peut être précédé par l’un des
deux attributs de représentation signed et unsigned.
• Les types signed int peuvent représenter des entiers
signés, alors que le type unsigned int ne représente que
des entiers non signés.
26
13
Type réel
• Les nombres flottants peuvent être manipulés
selon trois types différents selon la précision
requise dans le programme.
27
14
Type Signification Taille Plage de valeurs
(Algorithme) (octet)
unsigned Entier court 2 0 à 65535
short non signé
long int entier long 8 - pow(2,63) à
pow(2,63) - 1
unsigned long entier long 8 0 à pow(2,64) - 1
int non signé
float réel simple 4 - 3.4*1038 à 3.4*1038
précision
double réel double 8 - 1.7*10308 à 1.7*10308
précision
Long double réel long 10 -3.4*104932 à
double 3.4*104932
précision 29
30
15
Exemples
• int i ;
Déclare une variable de type entier qui a le nom i
• short int j ;
Déclare une variable de type entier court qui a le
nom j
• float x , y ;
Déclare deux variables de type réel qui ont
respectivement les noms x et y
• char c1 , c2 ;
Déclare deux variables de type caractère qui ont
respectivement les noms c1 et c2
31
Aucune valeur
32
16
La valeur d’une variable
• Une variable peut avoir une valeur comme elle peut ne
pas l’avoir.
• Mais toute variable qui subit une opération arithmétique
doit avoir une valeur avant cette opération.
• Une variable peut recevoir une valeur de deux façons
différentes:
– de manière interne: le programme lui-même qui donnera la
valeur à cette variable. Soit par initialisation soit par affectation
d’une expression ayant une valeur (opération arithmétiques sur
des variables)
– de manière externe: le programme demandera la valeur de la
variable. L’utilisateur qui donnera cette valeur au programme
par le clavier ( la saisie de la valeur la saisie de la variable 33
)
Exemple :
i = 1 ; après cette affectation, le contenu de la
variable i est égal à 1.
c = ‘B' ; après cette affectation, le contenu de la
variable c est égal à ‘B’ .
17
Initialisation
• Il est possible de donner des valeur à des
variables pendant leurs déclarations
(initialiser), l’instruction est :
– Exemple :
• char c = 'A' ;
• int i = 1 ;
• float x = 1.5 ;
35
18
Exemples
• #define Pi 3.141592
19
Sa syntaxe est la suivante :
20
Les sorties d’un programme
• Dans l’autre sens, d’autres instructions permettent au
programme de communiquer des valeurs à l’utilisateur
en les affichant à l’écran. Cette opération est l’écriture.
avec : 41
21
Exemples
#include<stdio.h>
main()
{
float x ;
double y ;
scanf("%f%lf", &x, &y);
printf(" la valeur de x=%f et de y=%lf ", x, y);
getch();
}
43
Autre écriture
#include<stdio.h>
main()
{
float x ;
double y ;
scanf("%f%lf", &x, &y);
printf(" la valeur de x=%f et de y=%f ", x, y) ;
getch();
}
44
22
Exemple
#include<stdio.h>
main()
{
double x=10.5 , y=2.5 ;
printf("%e divisé par %e égal à %e", x , y , x/y ) ;
}
23
B=getchar() joue le même rôle que l’instruction :
scanf("%c",&B );
#include<stdio.h>
main()
{
char B;
B = getchar();
printf("vous avez tapé le caractère : \n") ;
putchar(B);
printf ("vous avez tapé le caractère :%c" , B ) ;
}
ce programme permet de lire un caractère à partir du
clavier et de l’afficher à l’écran par les deux fonctions
putchar et printf.
48
24
#include<stdio.h>
main()
{
char c = 'A' ;
printf(" le code Ascii de %c est %d ", c , c ) ;
}
Ce programme va afficher à l’écran :
le code Ascii de A est 65
Séquences d’échappement
• permettent d'afficher des caractères non
imprimables :
– \n : nouvelle ligne.
– \a : déclenche un signal sonore (alarme).
– \\ : back slash ( \ ). Affiche la barre oblique \
– \f : Provoque un saut de page
– \t : tabulation horizontale .
– \v : Génère une tabulation verticale
– \’ : apostrophe.
– \" : guillemet.
50
25
Opérateurs
• Les opérateurs sont des symboles qui
permettent d’effectuer des opérations sur des
variables pour produire un résultat.
51
Operateurs
• On distingue plusieurs types d'opérateurs :
52
26
Opérateur d'affectation
• L’opérateur d’affectation permet d’affecter une
nouvelle valeur, une variable ou une expression
à une variable.
• La syntaxe est :
Var = expression ;
• Cette instruction permet d’affecter à la variable
Var la valeur obtenue suite à l’exécution de
l’instruction expression.
53
Opérateurs arithmétiques
• Les opérateurs arithmétiques sont présentés de la façon
suivante :
+ Addition nom_var = expression1 + expression2 ;
- Soustraction nom_var = expression1 - expression2 ;
* Multiplication nom_var = expression1 * expression2 ;
/ Division nom_var = expression1 /expression2 ;
% Modulo (reste de la division euclidienne) pour le
type entier seulement
nom_var = expression1 %expression2 ;
• Rq : si l’opération division est réalisée sur deux variables
de type entiers alors le résultat est un entier.
54
27
Les opérateurs d’affectation :
• Les opérateurs d’affectation :
– Ajouter à : +=
– Diminuer de : -=
– Multiplier par : *= Non commutatifs
– Diviser par : /=
– Modulo : %=
Exemples
i += 1 ; i=i+1 ;
j -= 2 + i*k ; j = j – ( 2 + i*k ) ;
p *= i ; p=p*i ;
x /= 2 ; x=x/2 ;
y %= k ; y=y%k ;
56
28
Opérateurs d'incrémentation
• Les opérateurs d’incrémentation et de
décrémentation permettent d’augmenter ou de
diminuer d’une unité les valeurs des variables.
– Opérateur d’incrémentation : ++
• L’expression x = i++ affecte d'abord la valeur de i à x et
incrémente la variable i après.
i++ ; i += 1 ; i=i+1 ;
57
Opérateurs et expressions.
– Opérateur de décrémentation : --
i-- ; i -= 1 ; i = i – 1 ;
58
29
Opérateurs et expressions.
– Exemples :
• si i=1, alors :
– x = i++ donne x=1 et i=2.
– x = ++i donne x= 2 et i=2.
• si i=2, alors :
– x = i-- donne x=2 et i=1.
– x = --i donne x= 1 et i=1.
59
Opérateurs de comparaison
• Ces opérateurs sont utilisés dans les tests logiques entre deux ou
plusieurs variables. Le résultat de ces operateurs soit vrai soit faux.
• En C il n y a pas le type logique ou booléen. Tous ce qui est nul est faux
et tous ce qui est non nul est vrai
exp1 op exp2
• != Différence logique
60
30
Opérateurs logiques
• Les expressions comportant des opérateurs
logiques donnent un résultat vrai (équivalent à
toute valeur différente de zéro) ou faux
(équivalent à l’entier 0). Les opérateurs logiques
sont :
• && et logique
• || ou logique
• ! négation logique
61
Exemples :
Expression Résultat
62
31
Commentaires :
• Un commentaire est un texte explicatif destinée au
lecteur du programme et qui n’a aucune incidence
sur sa compilation, c’est-à-dire n’est pas pris en
compte par le compilateur.
• Les commentaires sont formés de caractères
quelconques placés entre les symboles /* et */
• Peuvent être insérées à tout endroit du programme
et sur plusieurs lignes.
• Le symbole // permet d’insérer un commentaire
d'une seule ligne, à partir du // jusqu'à la fin de la
ligne.
• Les commentaires ne peuvent pas être imbriqués.
63
Séparateurs
• Les séparateurs (espaces, plusieurs espaces,
tabulations, saut de ligne) permettent
d’organiser la présentation du code source. Ils
sont utiles pour délimiter certaines parties du
code source. Par exemple, un espace est
nécessaire pour déclarer une variable de type
entier dont l'identificateur est a :
int a ;
64
32
Délimiteurs
Les délimiteurs servent à délimiter les éléments du
programme, ils sont au nombre de cinq :
33
Le tableau suivant donne la priorité de tous les opérateurs.
La priorité est décroissante de haut en bas dans le tableau.
Catégorie Opérateurs
Référence ( ) [ ] ->
Unaire ++ --
arithmétique * / %
arithmétique + -
relationnel < <= > >=
relationnel == !=
logique &&
logique ||
Affectation = += -= *= /= %=
séquentiel ,
67
Exemples
• * est plus prioritaire que + , ainsi 2 + 3 * 7
vaut 23 et non 35
• 13%3*4 vaut 4 et non 1
34
Fonctions mathématiques
• les fonctions mathématiques sont déclarées dans
la bibliothèque math.h
Fonctions mathématiques
• sin(x) : sinus de x
• cos(x) : cosinus de x
• tan(x) : tangente de x
• asin(x) : arc sinus de x
• acos(x) : arc cosinus de x
• atan(x) : arc tangente de x
• sinh(x) : sinus hyperbolique de x
• cosh(x) : cosinus hyperbolique de x
• tanh(x) : tangente hyperbolique de x
70
35
Fonctions mathématiques
• exp(x) : exponentielle de x
• log(x) : logarithme népérien de x (ln(x), x>0)
• log10(x) : logarithme à base 10 (log10(x), x>0)
• pow(x,y) : x exposant y (x^y ) (il se produit une
erreur si x=0 et y 0 , ou si x<0 et y n'est
pas un entier)
• sqrt(x) : racine carrée de x (x0)
• fabs(x) : valeur absolue de x (|x|)
• floor(x) : le plus grand entier inférieur ou égal à x
• ceil(x) : le plus petit entier supérieur ou égal à x
71
Conversions implicites
• Les types short et char sont systématiquement convertis en
int indépendamment des autres opérandes
• La conversion se fait en général selon une hiérarchie qui ne
change pas les valeurs :
int long int float double long double
• Exemple1 : n * x + p ( int n , p ; float x ; )
exécution prioritaire de n * x : conversion de n en float
exécution de l'addition : conversion de p en float
• Exemple2 : p1 * p2 + p3 * x
(char p1 ; short p2 , p3 ; float x ; )
p1 , p2 et p3 d'abord convertis en int
p3 converti en float avant multiplication ;
après l’expression est convertie en float 72
36
Opérateur de forçage de type ( cast )
Il est possible d’effectuer des conversions explicites ou de
forcer le type d’une expression c’est le casting ou le
cast
Syntaxe : (<type>) <expression>
Exemple : int n , p ;
(double) (n / p) ; convertit l’entier (n / p) en double
(int) n / (int) p = 4 / 1 = 4
37
Structures de contrôle
75
38
L’instruction if…else
• Syntaxe : if ( expression )
bloc-instruction1 ;
else
bloc-instruction2 ;
• bloc-instruction1 et 2 peut être une seule instruction terminée par
un point virgule ou une suite d’instructions délimitées par des
accolades { }
• expression est évaluée, si elle est vraie (valeur différente de 0),
alors bloc-instruction1 est exécuté. Si elle est fausse (valeur 0)
alors bloc-instruction2 est exécuté
• La partie else est facultative. S’il n’y a pas de traitement à réaliser
quand la condition est fausse, on utilisera simplement la forme :
if (expression) bloc-instruction1 ; 77
exemples
float a , b , max ;
if (a > b)
max = a ;
else
max = b ;
int a ;
if ( (a%2) == 0 )
printf(" %d est paire" , a);
else
printf(" a est impaire ",a);
78
39
Imbrication des instructions if
On peut imbriquer plusieurs instructions if…else .
Ceci peut conduire à des confusions, par exemple :
if (N>0)
if (A>B)
MAX=A;
else
MAX=B ;
(interprétation 1 : si N=0 alors MAX prend la valeur B)
(interprétation 2 : si N=0 MAX ne change pas)
40
Exemples
#include<stdio.h>
main()
{
float a, b, c, d ;
printf (" entrer trois réels ");
scanf ("%f%f%f", &a,&b,&c) ;
if ( (b != 0) && (c != 0) )
{
d = a/(b* c);
printf("%f divisé par %f * %f = %f \n",a,b,c,d) ;
}
else
printf("Division impossible\n");
}
81
41
L’instruction d’aiguillage switch
switch (<expression>) {
case constante_1 : instruction_1 ;
case constante_2 : instruction_2 ;
⁞
⁞
case constante_N : instructions_N ;
default : instructions ;
}
84
42
• expression : est une expression de type entier ou
caractère.
exemple
#include <stdio.h>
main() {
int a;
printf("Donner l'un des chiffres 1, 2, 3 et 4 : ");
scanf("%d", &a);
switch(a) {
case 1 : printf("la valeur est un \n");
case 2 : printf("la valeur est deux \n");
case 3 : printf("la valeur est trois \n");
case 4 : printf("la valeur est quatre \n");
default : printf("le nombre est different de 1, 2, 3 et 4.\n");
}
getch();
}
86
43
• Le programme s’exécute de la manière suivante :
une fois le compilateur rencontre l’instruction
switch, il évalue la valeur de l’expression figurant
après le mot switch (la variable a dans notre
exemple), puis cherche dans le bloc de switch s’il
existe un cas (case) identique à cette valeur et
exécute les instructions se trouvant après ce cas
et celles qui le suivent.
• Dans le cas contraire (aucun des cas ne
correspond à la valeur de l’expression figurant
après le mot switch), le compilateur exécute
l’instruction se trouvant après "default".
87
44
l’exemple précédent devient :
switch(a) {
case 1 : printf("la valeur est un \n");
break;
case 2 : printf("la valeur est deux \n");
break;
case 3 : printf("la valeur est trois \n");
break;
case 4 : printf("la valeur est quatre \n");
break;
default: printf("le nombre est different de 1, 2, 3 et 4.\n");
}
getch() ; }
En saisissant 2 dans cet exemple, le programme donne :
la valeur est deux 89
45
Instruction "while"
91
46
Exemples :
47
L’instruction do…while
• Permet de répéter l’exécution de certaines
instructions jusqu’à ce qu’une condition est
fausse. Contrairement à l’instruction " while",
l’instruction "do … while" teste sa condition
après exécution des instructions. Sa syntaxe est :
do
{
suite d'instructions ( ; )
}
while (expression) ;
95
96
48
Le programme suivant permet d’afficher les
nombres de 0 à 9.
main() {
int i = 0;
do
{
printf("%d \n", i) ;
i++;
}
while (i<10) ;
}
97
main()
{ int N;
do {
printf (" Entrez une note comprise entre 0 et 20 \n");
scanf("%d",&N);
} while ( (N < 0) || (N > 20) ) ;
....
.....
}
98
49
Instruction "for"
• Permet de répéter l’exécution d’une suite
d’instructions un nombre de fois défini. Cette
boucle peut être perçue comme étant une
variante de l’instruction "while".
• Sa syntaxe est :
for(expression1 ; expression2 ; expression3 )
{
Suite d’instructions ( ; )
} ;
99
50
• Exemples :
Le programme suivant affiche le carré des
nombres de 0 à 10
main()
{
int i;
for (i=0 ; i <=10 ; i ++ )
printf("Le carré de %d est %d \n ", i, i*i ) ;
}
101
#include <stdio.h>
main()
{
int i, n, s;
printf("Donner un entier : \n ");
scanf("%d", &n);
s=0;
for(i=1; i<=n; i++)
s = s+i; les { } ?
printf(" la somme est %d ",s);
}
102
51
N. B. dans la syntaxe de la boucle for, expression1
et expression3 peuvent être une séquence. En
pratique, expression1 et expression3 contiennent
souvent plusieurs initialisations ou
réinitialisations, séparées par des virgules :
52
calcul du factoriel d’un entier N.
#include<stdio.h>
main( ) {
int N , p , i ;
printf("Donner la valeur de N : ");
scanf("%d", &N);
for ( i=1 , p=1 ; i<=N ; i++ )
p=p*i;
printf("le factoriel de %d est %d \n",N , p ) ;
getch ();
}
105
expr1 ; expr1 ;
for ( ; expr2 ; expr3 ) for ( ; expr2 ; )
{ {
instructions ; instructions ;
} ; expr3 ;
} ;
106
53
Instructions de branchement non conditionnel
54
Exemple: Table de multiplication
#include<stdio.h>
main() {
int i,j;
for ( i=1 ; i<= 10 ; i++ )
{
for ( j=1 ; j<= 10 ; j++ )
{
printf ("%6d ", i * j) ;
};
printf ("\n \n") ;
}
getch() ; }
109
j 110
55
Juste la partie sous la diagonale
#include<stdio.h>
main(){
int i,j;
for ( i=1 ; i<= 10 ; i++ )
{
for ( j=1 ; j<= 10 ; j++ )
{
printf ("%6d ", i * j) ;
if(i==j) break ;
} ;
}
getch() ; }
111
j 112
56
Instruction "continue"
#include<stdio.h>
main( ) {
int i =2 ;
do {
i++;
if(i==5)
continue;
printf("\n la valeur de i est %d",i);
}
while (i<7);
getch (); }
La valeur de i est 3
La valeur de i est 4
La valeur de i est 6
La valeur de i est 7
114
57
Table de multiplication Sans la diagonale
116
58
Instruction "goto"
• Cette instruction provoque un saut à un
endroit du programme repéré par une
étiquette.
• Le programme continue alors à l’instruction
se trouvant juste après cette étiquette.
#include<stdio.h>
#include<math.h> /* bibliothèque des fonctions math */
main( ) {
float a , t , s=1;
Int i=1 ;
printf("Donner la valeur de a = "); scanf("%f",&a);
while (i<50) {
t=s;
s = s + pow(a,i) ;
i++;
if( (s-t) < 0.00001 )
goto terminer ;
} ;
terminer : ; /* l’étiquette */
printf("\n la limite de la serie est %f", s) ;
getch (); }
118
59
Les Tableaux
119
Les tableaux
Représentation des variables en mémoire
a x c x1 x2 y i j k
120
60
Les tableaux
121
Les tableaux
• Un tableau est une variable composée (une
variable structurée, une structure de données)
d’un nombre fini de variables élémentaires de
même type.
• C’est donc une zone mémoire constituée de cases
de taille identique et dans lesquelles sont rangées
des données de même type. Ce sont des
représentations naturelles des vecteurs et des
matrices.
• Un tableau représente une suite d’éléments
comme une variable simple représente un élément
simple.
122
61
• Le nombre de cases d’un tableau est connu au
moment de sa déclaration et la zone mémoire
réservée à un tableau possède un début et une fin.
• Un tableau est composé d’éléments de même type
et chaque élément d’un tableau est référencé ( ou
récupéré) par un indice unique.
• Pour accéder à une case, on utilise un indice qui
repère le numéro de la case à laquelle on fait
référence.
• Le premier élément du tableau porte l’indice 0 et le
i-ème élément porte l’indice (i-1).
• Comme toute variable, une case d'un tableau doit
être initialisée avant d'être utilisée.
123
Type Nom_du_Tableau[Taille]
où
Type = le type des variables élémentaires du tableau
Nom_du_Tableau = le nom du tableau
Taille = la taille du tableau
124
62
Exemple :
int A[5] ;
cette déclaration permet de réserver un espace
mémoire pour cinq variables de type entier. Le
nom du tableau est A.
Pour accéder à une case du tableau, nous
utilisons un indice qui repère le numéro de la
case à laquelle on fait référence.
Le premier élément du tableau porte l’indice 0
et le i-ème élément porte l’indice (i-1).
Représentation en mémoire
• Les éléments d’un tableau T de taille N sont
rangés dans la mémoire à des emplacements
contigus (l’un après l’autre).
Un indice
0 1 2 3 4 i N-1
• • • • • • •
T[0] T[1] T[2] T[3] T[i] T[N-1]
Un élément
63
Opérations sur un tableau
• Toutes les opérations réalisées sur une variable peuvent
être appliquées aux éléments des tableaux.
• En effet, nous pouvons par exemple opérer les
opérations suivantes sur les éléments du tableau A de
l’exemple ci-dessus :
64
scanf("%d", &X[2]) ; /* je lis X[2] */
65
Initialisation des tableaux à une dimension
66
• On ne peut pas affecter un tableau à un autre.
L’affectation globale de tableau est interdite en C.
• int A[5]={ } ;
initialise le tableau A à {0,0,0,0,0}, c.à.d.
A[0]=0 , A[1]=0 , A[2]=0 , A[3]=0 et A[4]=0 .
67
Initialisation par affectation
• Nous affectons les valeurs au tableau un par
un si la taille est petite ( < 10 )
• Exemple :
int A[5];
A[0]=2;
A[1]=4;
A[2]=6;
A[3]=8;
A[4]=10;
135
• Exemple :
int A[25];
for (i=0 ; i<25 ; i++)
A[i]=2*(i+1) ;
136
68
Lecture d’un tableau
• Les éléments du tableau de taille N sont lus un
par un par une boucle qui répétera l’instruction
de la lecture N fois
69
Exemples
#include <stdio.h>
main() {
int i;
char A[7]={'b','o','n','j','o','u','r'} ;
/* Initialisation à la
déclaration */
printf("Affichage du tableau A : \n") ;
for (i=0 ; i<7 ;i++ )
printf("%c",A[i]) ;
Affichage du tableau B :
B[0] = 1.000000
B[1] = 3.000000
B[2] = 5.000000
140
70
int C[5] ; /* Initialisation par lecture */
printf("\n \n Donner les valeurs du tableau C \n") ;
for (i=0 ; i<5 ;i++ ) {
printf("Donner la valeur de C[%d] : \n",i);
scanf( "%d" , &C[i]);
} ;
printf("Affichage du tableau C : \n");
for (i=0;i<5;i++)
printf(" C[%d] = %d \t ",i , C[i] ) ;
getch();
}
141
71
Calcul du nombre d'étudiants ayant une note supérieure à 10 :
main ( )
{ float notes[30] ;
int nbre , i ;
for(i=0;i<30;i++)
{ printf ("Entrez notes[%d] \n ",i) ;
scanf(" %f" , ¬es[i]); } ;
nbre=0;
for (i=0; i<30; i++)
if (notes[i]>10)
nbre+=1 ;
printf (" le nombre de notes > à 10 est égal à : %d", nbre); 143
}
72
Exemple
• Quelle est la taille des tableaux suivants :
#define N 10 15, constante numérique
const int K = 5;
int a[15]; 10, constante #define N
int b[N];
char c[K]; 5, constante symbolique
float d[2 * N];
double e[N + K + 1]; 20, expression symbolique
#define N 30
main ( )
{ float notes[N] ; /* avant été 30 */
int nbre , i ;
for(i=0;i < N ; i++)
{ printf ("Entrez notes[%d] \n ",i);
scanf(" %f" , ¬es[i]);
} ;
nbre=0;
for (i=0; i<N; i++)
if (notes[i]>10) nbre+=1;
printf (" le nombre de notes > à 10 est égal à : %d", nbre);
}
146
73
main ( ) {
int N ;
scanf(" %d", &N) ;
float notes[N] ;
int nbre , i ;
for(i=0;i<N ; i++)
{ printf ("Entrez notes[%d] \n ",i); scanf("%f", ¬es[i]) ; };
nbre=0;
for (i=0; i<N; i++)
if (notes[i]>10) nbre+=1;
printf (" le nombre de notes > à 10 est égal à : %d", nbre ) ;
74
Tableau à plusieurs dimensions
• On peut définir un tableau à n dimensions de la
façon suivante:
Type Nom_du_Tableau[D1][D2]…[Dn] ;
où Di est le nombre d’éléments dans la dimension i
• Exemple : pour stocker les notes de 20 étudiants en
5 modules dans deux examens, on peut déclarer un
tableau :
float notes[20][5][2] ;
notes[i][j][k] est la note de l’examen k dans
le module j pour l’étudiant i
149
75
Tableaux à deux dimensions (Matrices)
151
Type Nom_du_Tableau[Taille1][Taille2]
où
Type = le type des variables élémentaires du tableau
Nom_du_Tableau = le nom du tableau
Taille1= le nombre de lignes
Taille2 = le nombre de colonnes
int A[2][3] ;
On peut représenter le tableau A de la manière suivante :
A[0][0] A[0][1] A[0][2]
A[1][0] A[1][1] A[1][2]
152
76
Exemple : float A[4][3] ;
on réserve un espace mémoire pour 4×3 variables réel. Chaque
élément du tableau (relatif au premier indice) est un tableau de
taille 3 (ce sont les lignes du tableau). Les éléments du tableau A
sont notés A[i][j] où 1 ≤ i ≤ 4 et 1 ≤ j ≤ 3. Ainsi, les éléments du
tableau A sont ordonnés comme suit :
A[0][0]
A[0][1]
A[0][2]
A[1][0]
A[1][1]
A[1][2]
A[2][0]
A[2][1]
A[2][2]
A[3][0]
A[3][1]
A[3][2] 153
float A[3][4] = { {-1.5, 2.1, 3.4, 0} , {8.3, 7.5,1, 2.7 } , {3.1, 0, 4,-2} } ;
77
Nous affectons les valeurs au tableau au moment de sa
déclaration.
Exemple :
int A[2][3]={ {2,4,6} ,{-8,10,-4} } ;
Suite à cette déclaration, nous avons
A[0][0]=2 A[0][1]=4 A[0][2]=6
A[1][0]=-8 A[1][1]= 10 A[1][2]=-4
Remarque : la déclaration
int A[2][3]={ {2,4,6} , {-8,10,-4} };
est équivalente à
int A[2][3]={2,4,6,-8,10,-4} ; 155
78
Initialisation par affectation
De manière générale : Si A est un tableau de taille
N×M et on veux initialiser chaque élément par la
formule A[i][j] = 2i – j alors nous pouvons le faire de
la manière suivante :
int A[N][M] ;
int i , j ;
for (i=0 ; i<N ; i++)
for (j=0 ; j<M ;j++)
A[i][j]=2*i – j ;
Bien sûr N et M ont des valeurs avant la déclaration
du tableau 157
int A[N][M];
int i , j ;
printf(" Donner les valeur du tableau ");
for (i=0 ; i<N ; i++)
for (j=0 ; j<M ; j++)
scanf( "%d", &A[i][j]);
158
79
Affichage des éléments d’une matrice
for(i=0;i<N;i++)
{
for(j=0;j<M;j++)
printf (" %d \t", A[i][j] ) ;
printf(" \n ") ;
};
159
80
Exemples
#include<stdio.h>
#define N 5
main() {
int T[N][N] , i , j ;
for(i = 0; i N; i++)
for(j = 0; j N; j++)
scanf("%d", &T[i][j]) ;
for(i = 0; i N; i++)
{ for(j = 0; j N; j++)
printf("%d\t", T[i][j]);
printf(" \n ") ;
};
}
161
162
81
#include <stdio.h>
#define n 3
#define m 2
#define k 4
main() {
int A[n][m] = {{3,-5},{0,4},{1,-1}} , B[m][k] = {{1,-2,5,0},{0,3,-6,4}} ;
int C[n][k] , i , j , p , S ;
82
Les Pointeurs
165
166
83
Exemple
Un programme qui calcule la taille en octets des types de
base du langage C.
#include<stdio.h>
main() {
printf(" un char occupe %d octets \n ", sizeof(char));
printf(" un int occupe %d octets \n ", sizeof(int));
printf(" un float occupe %d octets \n ", sizeof(float));
printf(" un double occupe %d octets \n ", sizeof(double));
}
Le programme affichera:
1 4 4 8
167
Exemple
• Calculer la taille en octets de chacune des variables
suivantes :
• int a, b[100];
• char c[15], d[10];
• float x[10], z[20];
• La taille d’un tableau de N éléments et de type T est :
N sizeof(T).
• La taille du tableau b sera alors
100 sizeof(a).
168
84
#include<stdio.h>
main() {
int a, b[100];
char c[15], d[10];
float x[10], z[20];
printf(" Taille de a : %d octets \n ", sizeof(a) );
printf(" Taille de b : %d octets \n ", sizeof(b) );
printf(" Taille de c : %d octets \n ", sizeof(c) );
printf(" Taille de d : %d octets \n ", sizeof(d) );
printf(" Taille de x : %d octets \n ", sizeof(x) );
printf(" Taille de z : %d octets \n ", sizeof(z) );
}
Le programme affichera:
4 400 15 10 40 80
169
85
Exemple
Un programme qui déclare une variable a de type int et une
variable b de type float, puis affiche l’adresse de a et celle de b
#include<stdio.h>
main() {
int a ;
float b ;
printf(" Adresse de a : %d \n ", &a) ;
printf(" Adresse de b : %d \n ", &b ) ;
}
Le programme affichera:
Adresse de a : 2686792
Adresse de b : 3546124
171
Modes d’adressage
• Avant de parler de pointeurs, nous allons brièvement
passer en revue les deux modes d'adressage
principaux :
86
Adressage direct
• int x ;
• x=5;
• Adressage direct = Accès au contenu d'une variable
par le nom de la variable.
Ad Ad+1 Ad+2 …
x 173
Adressage indirect
• Adressage indirect : Si nous ne voulons pas ou
nous ne pouvons pas utiliser le nom d'une
variable X, nous pouvons copier l'adresse de cette
variable dans une variable spéciale P.
• Ensuite, nous pouvons retrouver l'information de
la variable X en passant par la variable P.
• Adressage indirect = Accès au contenu d'une
variable X, en passant par une variable qui
contient l'adresse de la variable X.
174
87
Adressage indirect
• Soit X une variable contenant la valeur 5 et P
une variable qui contient l'adresse de X.
• En mémoire, X et P peuvent se présenter
comme suit:
Ad
Ad 5
P X
175
88
Notion de pointeur
• Un pointeur est une variable dont la valeur est l’adresse
d’une autre variable. Si un pointeur p contient l’adresse
d’une variable a, on dit que p pointe vers a. Un pointeur
est identifié dans un programme par un nom et un type.
Valeur a
p
89
• Le langage C permet de manipuler les adresses
par le biais de variables appelées pointeurs.
90
Intérêts des pointeurs
• Ils permettent de créer des structures de données
(listes et arbres) dont le nombre d’éléments peut
évoluer dynamiquement. Ces structures sont très
utilisées en programmation.
91
Déclaration d’un pointeur
• Le type d’un pointeur dépend du type de la variable pointée.
Ceci est important pour connaître la taille de la valeur
pointée. En C, chaque pointeur est lié à un type de donnée
(même si la valeur d’un pointeur, qui est une adresse, est
toujours un entier).
183
Exemple
• On déclare un pointeur vers un entier, un
pointeur vers un char, un pointeur vers un float,
et un pointeur vers un double.
• Solution :
int *a ;
char *b ;
float *x ;
double *y ;
184
92
• Dans la déclaration d’un pointeur, le symbole * est associé
au nom de la variable pointeur et non pas au type. Ainsi, la
déclaration :
int *a , b ;
• Ne déclare pas deux pointeurs a et b, mais un pointeur a vers
un int et une variable simple b de type int.
• Pour déclarer deux pointeurs a et b dans la même ligne, il
faut écrire :
int *a , *b ;
• la valeur d’un pointeur donne l’adresse du premier octet
parmi les n octets où la variable est stockée
• Lors du travail avec des pointeurs, nous utilisons :
– un opérateur 'adresse de': & pour obtenir l'adresse d'une variable
– un opérateur 'contenu de': * pour accéder au contenu d'une adresse
185
93
p=&a;
3 Ad1 7
b p a
Ad2 Ad1
p=&b;
3 Ad2 7
b p a 187
94
Exemple
• Si un pointeur p pointe sur une variable a, alors
toutes les opérations réalisées sur la variable a
(accès direct )peuvent être réalisées sur *p (accès
indirect) .
• int a=3 , b , *p ;
• p = &a ;
• b=a–1 /* est équivalente à b = *p - 1 */
• a += 2 /* est équivalente à *p += 2 */
• ++a /* est équivalente à ++*p */
• a++ /* est équivalente à (*p)++ */
189
#include <stdio.h>
main() {
int n=2 , m= -4 , k ;
int *p , *q ;
p=&n ;
q=&m ;
k=*p**q;
printf(" %d \t %d \t %d",*p,*q,k); /* affiche 2 -4 -8 */
(*p)-- ;
(*q)++ ;
printf(" %d \t %d \t %d",*p,*q,n); /* affiche 1 -3 1 */
(*p)+=4 ;
(*q)-=2 ;
printf(" %d \t %d \t %d",*p,*q,m); } /* affiche 5 -5 -5 */
190
95
• int *p;
on déclare un pointeur vers une variable de type int
• int i=10, j=30;
deux variables de type int
• p=&i;
on met dans p, l’adresse de i (p pointe sur i)
• printf("*p = %d \n",*p);
On affiche : *p = 10
• *p=20;
On met la valeur 20 dans la case mémoire pointée par p (i
vaut 20 après cette instruction)
• p=&j;
p pointe sur j
• i=*p;
On affecte le contenu de p à i (i vaut 30 après cette
instruction)
191
float a, *p;
p=&a;
printf("Entrez une valeur : \n");
scanf("%f ",p);
// supposons qu’on saisit la valeur 1.5
printf("Adresse de a= %d, contenu de a= %f \n" , p,*p);
*p+=0.5;
printf ("a= %f \n" , a);
// affiche a=2.0
192
96
Remarque : si un pointeur P pointe sur une
variable X, alors *P peut être utilisé partout où
on peut écrire X
97
Aussi l’écriture suivante provoque un problème
mémoire
int *p ;
p=200 ; // ou p= 231564 ;
98
Exemple :
int *p , b=3 ; p=&b ;
printf (" %d " , b) ; /* affiche 3 */
printf (" %d " , *p) ; /* affiche 3 */
*p est la valeur se trouvant dans l’adresse de b.
Exemple :
int *p , a=5 , b=3 ;
p=&a ; /* p pointe sur la variable a */
b=*p ; /* affecte à la variable b la valeur pointée par p */
*p = 1 ; /* affecte la valeur 1 à la variable pointée par p */
printf (" %d " , b) ; /* affiche 5 */
printf (" %d " , *p) ; /* affiche 1 */
printf (" %d " , a) ; /* affiche 1 */
197
Affectation de pointeurs
99
Exemple
• On déclare une variable a de type int et on
l’initialise par la valeur 7, puis on déclare deux
pointeurs de type int qui vont pointer vers la
variable a déclarée.
• Solution :
int a = 7 ;
int * p = &a , * q ;
q =p ; 199
100
p a
p+3
201
Exemple
• Soit a une variable de type int et p est un pointeur vers a.
Sachant que le type int est codé sur 4 octets et que
l’adresse de la variable a est 1030, quel est le contenu du
pointeur p+3 ?
1030 + 3 * 4 = 1042.
202
101
#include <stdio.h>
main() {
double i = 3;
double *p, *q, *r;
p = &i;
q = p + 1;
r = p+ 3 ;
printf("p = %d \t q = %d \t r = %d \n",p,q,r);
getch() ; }
il affiche
p=2686784 q=2686792 r=2686808 203
204
102
p-5
p a
205
Exemple
• Soit a une variable de type int et p est un pointeur vers
a. Sachant que le type int est codé sur 4 octets et que
l’adresse de la variable a est 1030, quel est le contenu
du pointeur p-5 ?
103
#include <stdio.h>
main() {
int i = -1;
int *p, *q, *r;
p = &i;
q = p - 1;
r=p-3;
printf("p = %d \t q = %d \t r = %d \n",p,q,r);
getch() ; }
il affiche
p=2686788 q=2686784 r=2686776
207
104
Exemple
• La variable a de type int est rangée en mémoire à l’adresse
1000. Quel est le contenu de chacun des pointeurs suivants :
int * p = &a;
int * q = p++;
int * r = ++q;
• Le pointeur p pointe vers a, donc le contenu de p est l’adresse
de a, c’est-à-dire 1000.
• q = p++; est équivalente à q = p; p = p + 1; Donc le contenu du
pointeur q est 1000, mais le pointeur p est incrémenté et par
suite, le contenu de p est 1004.
• r = ++q; est équivalente à q = q + 1; r = q; Donc le contenu du
pointeur q est 1004, et celui de r est 1004.
• La variable a ?? Elle n’est plus pointée par ces pointeurs 209
210
105
Exemple
• La variable a de type int est rangée en mémoire à l’adresse 1000.
Quel est le contenu de chacun des pointeurs suivants :
int * p = &a;
int * q = p--;
int * r = --q;
212
106
Exemple
• Les variables a et b de type int sont rangées en
mémoire respectivement aux adresses 1000 et
1040. Quel est la valeur de l’expression (p - q)
où p est un pointeur vers a et q est un pointeur
vers b ?
• Solution :
• La valeur de (p – q) est
(&a - &b) / sizeof(int)
= (1000 - 1040) / 4 = -10.
213
#include <stdio.h>
main()
{
int a;
int *p, *q;
p=&a ;
q=p+3;
printf("%d", q-p); /* cette instruction va afficher 3 */
printf("%d", p-q); /* cette instruction va afficher -3 */
getch();
}
214
107
Relation entre un tableau et un pointeur
108
écrire un programme qui saisit dix valeurs
entières, les stocke dans un tableau et les
affiche, en utilisant un pointeur.
#include<stdio.h>
main() {
int T[10] ;
int i , n;
for(i = 0; i 10; i++)
{ printf(" Valeur numero %d : ", i );
scanf("%d", T+i );
} ; 217
109
Relation entre un pointeur et un tableau
• En C, un pointeur p vers une variable de type T
est considéré comme un tableau d’éléments de
type T dont le premier élément et *p (c’est-à-
dire la variable pointée par p).
• Par conséquent, on peut accéder au premier
élément de ce tableau en utilisant soit *p, soit
p[0].
• Généralement, si k est un entier, alors
* (p + k) est équivalent à p[k].
• Aucune réservation de l’espace mémoire; c’est
dangereuse comme écriture , il faut associer ce
pointeur à un tableau déjà déclaré
219
110
Version 1:
main(){
float T[100] , *pt;
int i,n;
do { printf("Entrez n \n " ); scanf(" %d" ,&n);
} while(n<0 ||n>100);
pt=T;
for(i=0;i<n;i++)
{ printf ("Entrez T[%d] \n ",i );
scanf(" %f" , pt+i);
};
for(i=0;i<n;i++)
printf (" %f \t",*(pt+i));
221
111
quelle est la valeur de la variable s après
l’exécution de ce programme :
#include<stdio.h>
main() {
int x[6] = {1, 2, 3, 4, 5, 6} , i, s = 0;
int * p = &x[0] ; // ou int *p=x ;
for(i = 0; i 6; i++)
{
s + = * (p + i);
};
}
223
112
#include <stdio.h>
main()
{
int t[8]={-2,6,3,-2,1,9,0,-1} ;
int *p;
for(p=t ;p<t+8 ;p++)
printf("%d \t", *p);
/* cette instruction va afficher : -2 6 3 -2 1 9 0 -1 */
getch();
}
225
#include<stdio.h>
#define N 10
main() {
int T[N]={3,6,0,8,97,0,5,6,0,8} ;
int *Deb, *Fin, *Comp ;
Deb = &T[0] ;
Fin = &T[N-1];
for(Comp=Deb ; Comp <= Fin ; Comp++)
printf("%d \n ",*Comp) ;
getch(); }
il affiche le tableau T
226
113
Pointeurs de type pointeurs
• Puisque les pointeurs sont des variables, et on peut déclarer
des pointeurs qui pointent sur n’importe quel type de
variables, alors il est possible de déclarer des pointeurs qui
pointent sur des pointeurs.
• La syntaxe de la déclaration est la suivante :
Type **Nom_pointeur ;
114
float A[N][M];
• Cette instruction est interprétée par le compilateur comme
étant une déclaration d’un tableau à N lignes et M
colonnes. Chaque ligne de ce tableau est considérée
comme un tableau à une dimension. La ième ligne du
tableau peut être notée A[i]. Ainsi, A[i] est un tableau de
taille M dont les composantes sont :
A[i][0] , A[i][1] , … , A[i][M] .
• Par conséquent, le nom A[i] du tableau est équivalent à
&A[i][0] (adresse de A[i][0]).
• par suite, pour tout 0 ≤ j ≤ M-1, nous avons :
A[i]+j est équivalent à &A[i][j] .
• La matrice A est considérée ici comme un pointeur de
pointeur. La matrice est normalement représentée par un
pointeur de pointeur.
229
115
• Remarque : Les différences entre les tableaux et les
pointeurs peuvent être résumées ainsi :
• La déclaration d’un pointeur P réserve dans la
mémoire uniquement l’espace mémoire nécessaire
pour stocker sa valeur (qui est une autre adresse).
• La déclaration d’un tableau T réserve dans la
mémoire un espace mémoire proportionnel à la
taille du tableau et dont l’emplacement est statique.
• La taille du pointeur P peut changer au cours du
programme alors que celle du tableau T reste
constante et ne peut pas être modifiée.
• Les expressions P=T et P++ sont autorisées mais les
instructions T=P ou T++ ne le sont pas. 231
116
• Avant de manipuler un pointeur, et notamment de
lui appliquer l’opérateur d’indirection *, il faut
l’initialiser. Sinon, par défaut, la valeur du pointeur
est égale à une constante symbolique notée NULL
définie dans stdio.h. En général, cette constante
vaut 0.
• Le test p== NULL permet de savoir si le pointeur p
pointe vers un objet.
• On peut initialiser un pointeur p par une affectation
sur p. Par exemple, on peut affecter à p l’adresse
d’une autre variable. Il est également possible
d’affecter directement une valeur à *p. Mais pour
cela, il faut d’abord réserver à *p un espace-
mémoire de taille adéquate.
233
117
• Cette fonction retourne un pointeur de type char * pointant
vers un objet de taille N octets.
118
#include <stdio.h>
#include <stdlib.h>
main() {
int i = 3 ;
printf(" Adresse de i = %d\n",&i );
int *p ;
printf("valeur de p avant initialisation = %d\n",p);
p = (int*)malloc(sizeof(int));
printf("valeur de p apres initialisation = %d\n",p);
p = &i;
printf("valeur de *p = %d\n",*p);
printf("valeur de p apres redirection vers i = %d\n", p);
getch() ; }
237
Ce programme affichera
Adresse de i = 2293556
valeur de p avant initialisation = 4198592
valeur de p après initialisation = 7868248
valeur de *p = 3
valeur de p après redirection vers i = 2293556
119
#include <stdio.h>
#include <stdlib.h>
main() {
int i = 3 ; printf(" Adresse de i = %d\n",&i);
int *p ; printf("valeur de p avant initialisation = %d\n",p);
p = (int*)malloc(sizeof(int));
printf("valeur de p apres initialisation = %d \n",p);
*p=i;
printf("valeur de *p apres (*p=i ) est %d et de i = %d \n",*p,i);
*p=*p+2;
printf("valeur de *p apres (*p=*p+2 ) est %d et de i = %d \n",*p,i);
i=i-1;
printf("valeur de *p apres ( i=i-1 ) est %d et de i = %d \n",*p,i);
p = &i;
printf("valeur de *p apres (p=&i ) est %d de i est %d \n",*p,i);
printf("valeur de p apres (p=&i ) est %d\n",p) ; getch(); }
239
Ce programme affichera
Adresse de i = 2293556
valeur de p avant initialisation = 4198592
valeur de p apres initialisation = 8195928
valeur de *p apres (*p=i) est 3 et de i = 3
valeur de *p apres (*p=*p+2) est 5 et de i = 3
valeur de *p apres (i=i-1) est 5 et de i = 2
valeur de *p apres (p=&i ) est 2 et de i est 2
valeur de p apres (p=&i) = 2293556
120
La fonction malloc permet également d’allouer un espace
pour plusieurs objets contigus en mémoire.
On peut écrire par exemple
#include <stdio.h>
#include <stdlib.h>
main(){
int *p ;
p = (int*)malloc(3 * sizeof(int));
*p = 3 ; *(p+1)=6 ; *(p+2)=13 ;
Le programme affiche
p = 3608472 *p = 3
p+1 = 3608476 *(p+1) = 6
p+2 = 3608480 *(p+2) = 13
242
121
• A la fin de ce code, p est un pointeur contenant une
adresse qui a été réservée par le système d’exploitation.
En pratique, nous sommes devant deux possibilités :
• L'allocation a réussi, et dans ce cas notre pointeur
contient une adresse.
• L'allocation a échoué, et dans ce cas notre pointeur
contient l'adresse NULL (qui est équivalent à l’absence
d’adresse).
int *p;
p = (int*)malloc(4*sizeof(int)) ;
if (p == NULL) // Si l'allocation a échoué
exit(0);
// On arrête immédiatement le programme
// dans le cas contraire, on continue le programme
// normalement.
• Si le pointeur est différent de NULL, le programme
peut continuer, sinon il faut afficher un message
d'erreur ou même mettre fin au programme (en
utilisant l’instruction exit(0)), par ce qu'il ne pourra
pas continuer correctement s'il n'y a plus de place
en mémoire.
244
122
Exemple :
#include <stdio.h>
#include <stdlib.h> /* à ajouter */
main() {
int *p,*q , i ;
p=(int*)malloc(5*sizeof(int));
q=(int*)malloc(5*sizeof(int));
if (p==NULL)
exit(0);
else {
for (i=0;i<5;i++) {
*(p+i)=i+1; /* ou p[i]=i+1; */
printf("%d \t", *(p+i)); } ; } ;
245
printf("\n") ;
if (q==NULL)
exit(0);
else {
for (i=0;i<5;i++) {
q[i]=i-1; /* ou *(q+i)=i-1 ; */
printf("%d \t", q[i]); } ;} ;
getch() ; }
Après exécution, le programme affiche :
1 2 3 4 5
-1 0 1 2 3
246
123
Autres fonctions d’allocation
• Il existe d’autres fonctions d’allocation
dynamique de mémoire dans la bibliothèque
<stdlib.h>
247
248
124
Allocation dynamique de la mémoire
pour un pointeur de type pointeur
• Etant donné qu’il est possible d’allouer de la
mémoire pour un pointeur sur n’importe quelle type
de variable, l’allocation dynamique de la mémoire
pour un pointeur de type pointeur est donc possible.
Sa syntaxe est :
float **p;
p=(float**)malloc(n*sizeof(float *));
for (i=0;i<n;i++)
p[i]=(float*)malloc(m*sizeof(float));
249
p=(float**)malloc(n*sizeof(float*));
if (p==NULL){
printf(" On n'a pas pu allouer de la mémoire " );
exit(0) ; } ;
for (i=0;i<n;i++) {
p[i]=(float*)malloc(m*sizeof(float));
if (p[i]==NULL) {
printf(" On n'a pas pu allouer de la mémoire " ) ;
exit(0) ; } ; } ;
250
125
Par exemple, pour créer avec un pointeur de pointeur
une matrice à k lignes et n colonnes à coefficients
entiers, on écrit :
main()
{
int k, n;
int **tab;
tab = (int**)malloc(k * sizeof(int*));
for (i = 0; i < k; i++)
tab[i] = (int*)malloc(n * sizeof(int));
....
} 251
126
#include <stdio.h>
#include <stdlib.h>
main() {
int t[6][4] , i , j ; int **p ;
p=(int**)malloc(6*sizeof(int*));
for (i=0;i<6;i++)
{ p[i]=(int*)malloc(4*sizeof(int)) ; p[i]=&t[i][0] ; };
for (i=0;i<6;i++)
{ for (j=0;j<4;j++) t[i][j]=(i+1)*(j+1) ; } ;
for (i=0;i<6;i++){
for (j=0;j<4;j++) {
printf("%d \t", *(p[i]+j));} ; /* c'est équivalent à
printf("%d \t", p[i][j]); */
printf("\n"); } ;
getch(); } 253
127
#include <stdio.h>
#include <stdlib.h>
main()
{
int t[6][4] , i , j ;
int **p ;
p=(int**)malloc(6*sizeof(int*));
if (p==NULL){
printf(" On n'a pas pu allouer de la mémoire " );
exit(0) ;
};
255
for (i=0;i<6;i++) {
p[i]=(int*)malloc(4*sizeof(int));
if (p[i]==NULL) {
printf(" On n'a pas pu allouer de la mémoire " ) ;
exit(0) ; }
else
p[i]=&t[i][0] ; };
for (i=0;i<6;i++)
{ for (j=0;j<4;j++) t[i][j]=(i+1)*(j+1) ; } ;
for (i=0;i<6;i++) {
for (j=0;j<4;j++) { printf("%d \t", *(p[i]+j)) ; } ;
printf("\n"); } ;
getch(); }
256
128
#include <stdio.h>
#include <stdlib.h>
main() {
int **p , i , j ;
p=(int**)malloc(6*sizeof(int*));
if (p==NULL){
printf("le système n'a pas pu allouer de l espace memoire");
exit(0) ;} ;
for (i=0;i<6;i++) {
p[i]=(int*)malloc(4*sizeof(int));
if (p[i]==NULL) {
printf(" On n’a pas pu allouer de la mémoire ");
exit(0) ; } ; } ;
257
for (i=0;i<6;i++) {
for (j=0;j<4;j++)
p[i][j]=(i+1)*(j+1); } ;
for (i=0;i<5;i++) {
for (j=0;j<4;j++) { printf("%d \t", *(p[i]+j)) ; } ;
printf("\n"); } ;
getch() ; }
129
• Contrairement aux tableaux à deux dimensions,
on peut choisir des tailles différentes pour
chacune des lignes tab[i].
• Par exemple, si l’on veut que tab[i] contienne
exactement i+1 éléments, on écrit
Fonction free
• L’un des avantages de la gestion dynamique est la
possibilité de libérer les espaces mémoires affectés
aux pointeurs une fois on en a plus besoin. En
effet, la fonction free permet de libérer tout
espace mémoire alloué à un pointeur qui ne sera
plus utilisé dans la suite du programme.
130
#include <stdio.h>
#include <stdlib.h>
main() {
int i , *p;
p=(int*)malloc(5*sizeof(int));
if (p==NULL)
exit(0);
else {
for (i=0;i<5;i++) {
*(p+i)=i; /* ou p[i]=i; */
printf("%d \t", *(p+i)); } ;} ;
free(p);
getch(); }
131
• L’un des principaux avantages de l’utilisation
des pointeurs et la possibilité de libérer
l’espace alloué au pointeur avant la fin du
programme.
263
La fonction realloc
• Passons maintenant à la dernière fonction d'allocation : la
fonction realloc() voici sa syntaxe :
realloc ( P , N )
132
• Comme d'habitude, si une erreur se produit, la
fonction retourne le pointeur NULL. Si en
revanche, tout s'est bien passé, la fonction
renvoie un pointeur vers l'adresse du nouveau
bloc réalloué.
La fonction realloc
• La fonction realloc (P , N ) de <stdlib.h> permet de
modifier la taille d’une zone préalablement allouée
par malloc, calloc ou realloc.
133
• Lorsque la nouvelle taille demandée est
supérieure à l’ancienne, le contenu de l’ancienne
zone est conservé (quitte à le recopier si la
nouvelle adresse est différente de l’ancienne).
• Dans le cas où la nouvelle taille est inférieure à
l’ancienne, le début de l’ancienne zone (c’est-à-
dire taille octets) verra son contenu inchangé.
• Ceci est donc utile pour un tableau dynamique :
en effet, on peut ajouter ou enlever une case à la
fin du tableau sans le modifier.
• Rien de tel qu'un exemple pour illustrer
comment fonctionne la fonction : 267
int * tab ;
/* Création d'un tableau de 3 entiers */
tab = (int*)calloc ( 3 , sizeof(int) ) ;
tab[0] = 1 ; tab[1] = 2 ; tab[2] = 3 ;
/* Ajout d'un element au tableau */
tab = (int*) realloc (tab , 4 * sizeof(int) );
tab[3] = 4;
for ( i = 0 ; i < 3 ; i++ ){
printf(" tab[%d] = %d \n", i , tab[i] );}
268
134
• Voici donc comment gérer les réallocations mémoire
de manière correcte. Tout d'abord, au lieu d'affecter
le retour de la fonction realloc à notre tableau
initial, nous affectons le retour à une variable
temporaire.
• Celle ci, si elle vaut NULL après l'appel à la
fonction realloc(), indique une erreur de
réallocation.
• Si tel est le cas, nous affichons un message d'erreur,
nous libérons la mémoire occupée par notre tableau
initial et nous quittons le programme.
• Si la réallocation s'est bien passé, c'est à dire
que realloc() n'a pas retourné NULL, alors, on
affecte le résultat de la fonction à notre tableau 269
135
int *tab= NULL , *temp ;
/* Création d'un tableau de 3 entiers */
tab= (int*) realloc ( tab , 3 * sizeof(int) ) ;
if ( tab== NULL ){ printf("Allocation impossible") ; exit(0); } ;
tab[0] = 1 ; tab[1] = 2 ; tab[2] = 3 ;
/* Redimensionnement*/
temp = (int*) realloc (tab, 4 * sizeof(int ) );
if ( temp == NULL ){
printf("Reallocation impossible");
free(tab); exit(0);}
else{
tab = temp;} ;
/* Ajout d'un élement */
tab[3] = 4;
for ( i = 0 ; i < 3 ; i++ )
{ printf(" tab[%d] = %d \n", i , tab[i] ); } ;
271
136
#include <stdio.h>
#include <stdlib.h>
main() {
int nombre , *tableau = NULL , x = 0;
scanf("%d", &nombre);
tableau = (int*)malloc(sizeof(int) * nombre);
for (x = 0; x < nombre; x++) {
scanf("%d", &tableau[x]); } ;
for (x = 0; x < nombre; x++) {
printf ("Nombre %d : %d\n", (x + 1), tableau[x]); };
tableau = (int*)realloc(tableau, sizeof(int) * (nombre + 1));
tableau[nombre] = 100;
for (x = 0; x < nombre + 1; ++x) {
printf ("Nombre %d : %d\n", (x + 1), tableau[x]); };
free(tableau); getch();}
273
137
#include <stdio.h> #include <stdlib.h> main() {
int nombre = 0 , *tableau = NULL , *temp = NULL , x = 0 ;
scanf ("%d", &nombre);
tableau =(int*) malloc(sizeof(int) * nombre);
for (x = 0; x < nombre; x++) { scanf ("%d", &tableau[x]); } ;
for (x = 0; x < nombre; x++) {
printf ("Nombre %d : %d\n", (x + 1), tableau[x]); } ;
temp = (int*)realloc(tableau, sizeof(int) * (nombre + 1));
if (temp != NULL)
tableau = temp;
else
exit(0);
tableau[nombre] = 100;
for (x = 0; x < nombre + 1; x++) {
printf ("Nombre %d : %d\n", (x + 1), tableau[x]); } ;
free(tableau); }
275
276
138
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main() { char *str ;
str = (char *) malloc(15); // où str = malloc(15);
strcpy(str, "tutorialspoint");
// recopie la chaîne "tutorialspoint" dans str
printf("mot = %s, Address = %d \n", str , str ) ;
str = (char *) realloc(str, 25) ;
strcat(str, ".com");
// recopie la chaîne ".com" à la suite de la chaîne str
printf("mot = %s, Address = %d \n", str , str ) ;
free(str); }
277
139
Fin du programme
Bonne chance
279
140