Telechargé par IMANE BOUASSEL

03 pointeur

publicité
Université Moulay Ismail
Faculté des Sciences et Techniques
Département d’Informatique
Errachidia
Filière Cycle d'Ingénieur « Génie Informatique »
Les Pointeurs en C
Prof. Y FARHAOUI
1
Année universitaire : 2021/2022
Les pointeurs
● La définition d’un pointeur
● L’utilisation des pointeurs
● La déclaration et l’initialisation des pointeurs
● L’utilisation des pointeurs avec des variables simples et
des tableaux
● Opérateurs unaires pour manipuler les pointeurs
● Passage de paramètres de fonction par référence
● Le passage des tableaux à une fonction avec les
pointeurs
● Utilisation des pointeurs avec les chaines de caractères
2
Les pointeurs, c'est quoi?
Un pointeur est une variable particulière, dont la
valeur est l'adresse d'une autre variable.
• Pointeur p: valeur 5A0F3 (adresse hexadécimale)
• Adresse 5A0F3: valeur 17 (correspondant à la valeur
d'un entier i)
i=17
p=5A0F3
5A0F3
17
p
i
On peut accéder indirectement à la variable et
donc la modifier.
Un pointeur est une adresse mémoire. On dit que le pointeur p
pointe vers i, puisque p pointe vers l’emplacement mémoire où
est enregistrée i.
3
Les pointeurs: pourquoi ?
• Les pointeurs sont nécessaires pour:
– effectuer les appels par référence (i.e. écrire
des fonctions qui modifient certains de leurs
paramètres)
– manipuler des structures de données
dynamiques (liste, pile, arbre,…)
– allouer dynamiquement de la place mémoire
4
Les pointeurs: pourquoi ?
L’étape suivante consiste à stocker l’adresse de vari dans la variable p_vari
en langage C, p_vari pointe sur vari, ou p_vari est un pointeur vers vari
p_vari
vari
En résumé
Un pointeur est une variable qui contient l’adresse d’une autre
variable.
5
Déclaration de pointeurs
Le symbole * est utilisé entre le type et le nom du
pointeur
int i;
• Déclaration d‟un entier:
 Déclaration d‟un pointeur vers un entier:
int *p;
Exemples de déclarations de pointeurs
int *pi;
float *pf;
/* pi est un pointeur vers un int
*pi désigne le contenu de l'adresse */
/* pf est un pointeur vers un float */
char c, d, *pc;
/* c et d sont des char*/
/* pc est un pointeur vers un char */
double *pd, e, f; /* pd est un pointeur vers un double*/
/* e et f sont des doubles */
double **tab;
/* tab est un pointeur pointant sur un pointeur qui
pointe sur un flottant double */
6
Exemple 1
Déclaration de pointeurs
#include <stdio.h>
int main(void)
{
int x = 2; /* déclaration d’une variable x */
int *p;
/* déclaration d’un pointeur p */
p = &x; /* p pointe sur x */
/* la valeur de p est l’adresse de x */
scanf("%d", p); /* lecture de la valeur de x au clavier */
printf("%d", x); /* affichage de la nouvelle valeur de x */
return 0;
}
RQ: Ne pas confondre l’usage de l’étoile lors de la déclaration
d’une variable de type pointeur avec l’usage de l’étoile qui permet
d’accéder à l’objet pointé par le pointeur.
*p = 3; /* l’objet pointé par p prend pour valeur 3 */
7
Exemple 2
Déclaration de pointeurs
#include <stdio.h>
int main(void)
{
int x=2;
int *p = &x; /* x et *p deviennent synonymes */
*p = 3;
printf("La nouvelle valeur de x est %d \n", x );
return 0;
}
/* doit afficher la valeur 3 */
8
Déclaration de pointeurs
Exemple 3
#include <stdio.h>
int main(void)
{
/* affiche 2 */
int x=2;
int *p = &x; /* x et *p deviennent synonymes */
printf("La valeur de x est %d\n", *p);
x=5;
printf("La nouvelle valeur de x est %d\n", *p );
/* doit afficher la valeur 5 */
return 0;
}
/* doit afficher la valeur 5 */
9
Opérateurs unaires pour manipuler les
pointeurs, & (adresse de) et * (contenu)
Exemple: int i = 8;
printf("VOICI i: %d\n",i);
printf("VOICI SON ADRESSE EN HEXADECIMAL: %p\n",&i);
nom_de_Pointeur = &nom_de_variable
void
{
main(void)
char
char
p
c
'a'
0x1132
c = 'a', d = 'z';
*p;
0x1132
p
p = &c;
*p = c;
printf("%c\n", *p);
p = &d;
}
printf("%c\n", *p);
d
'z'
0x91A2
0x91A2
L‟opérateur * (“valeur pointée par”)
a
z
10
* et ++
*p++ signifie:
int a[]={0,1,5};
int*p;
*p++ trouver la valeur pointée
!
p=a;//&a[0]==a
*p++ passer à l’adresse suivante Int b=*p++;//
p
a
(*p)++ signifie:
(*p)++; //*p=2
(*p)++ trouver la valeur pointée
Int c= (*p)++; //c=3
(*p)++ incrémenter cette valeur (sans changer le
p
pointeur)
a
*++p signifie:
*++p incrémenter d’abord le pointeur
Int d= *++p; / /d==5
*++p trouver la valeur pointée
p
++*P
a
100
104
108
0
1
5
100
104
108
0
3
5
100
104
108
0
3
5
11
#include <stdio.h>
void main() {
Exercice
int *p, x, y;
p = &x;
/* p pointe sur x */
x = 10;
/* x vaut 10 */
y = *p - 1;
printf(" y= *p - 1 =? = %d\n" , y);
y vaut ?
*p += 1;
printf(" *p += 1 =? *p = x= ? = %d %d\n" , *p, x);
x vaut ?
(*p)++;
printf(" (*p)++ =? *p = x= ? = %d %d alors y=%d \n" , *p, x, y);
incrémente aussi de 1 la variable pointée par p, donc x vaut ??.
y vaut 9
*p=0;
printf(" *p=0 x=? = %d\n" , x);
comme p pointe sur x, maintenant x vaut ?
*p++; *p=20; printf(" *p++ x=? = %d\n" , x);
}
comme p ne pointe plus sur x, x vaut 12
tjr ?
Utiliser des pointeurs
• On peut donc accéder aux éléments par pointeurs en
faisant des calculs d‟adresses (addition ou soustraction)
long v[6] ={1,2, 3,4,5,6 };
long
*p;
p = v; // v==&v[0]
printf("%ld\n", *p);
p++;
printf("%ld\n", *p);
p += 4;
printf("%ld\n", *p);
p += 4
p++
1
2
6
p
1000
1
v
2
3
4
5
6
1016
1000
1008
1012
1020
1004
13
Exercice
•
•
•
•
•
•
•
int t[] = {2,5,7};
int *p = t;
int a, b, c ; //*p=
b = *p++; // b =
b = *p;
// b =
a = (*p)++; // a=
c = *++p
// c =
14
Pointeurs
En résumé, lorsque p pointe sur x, la valeur de p est l’adresse
de x, toute modification de *p modifie x et toute modification
de x modifie *p.
La raison est que *p et x sont sur le même emplacement
mémoire dans la mémoire RAM.
Conseils
À faire
Veillez à bien comprendre ce que représente un pointeur et
comment il travaille. Le langage C et les pointeurs vont de pair.
À ne pas faire
N’utilisez pas un pointeur qui n’a pas été initialisé,
15
Pointeurs et types de variables
Les différents types de variables du langage C n’occupent pas tous la même
mémoire (int prend quatre octets, une variable double en prend huit, etc)
int vint = 12252;
char vchar = 90; //asci de Z
double vdouble = 1200.156004;
La variable int occupe quatre octets,
la variable char en occupe un,
et la variable double huit.
16
Pointeurs et types de variables
Voici la déclaration et l’initialisation des pointeurs vers ces trois variables :
int *p_vint;
char *p_vchar;
double *p_vdouble;
/* des instructions peuvent être insérées ici */
p_vint = &vint;
p_vchar = &vchar;
p_vdouble = &vdouble;
17
Passage des paramètres par valeur et
par adresse
#include <stdio.h>
void ech(int x,int y)
{
int tampon;
tampon = x;
x = y;
y = tampon;
}
a et b:
variables
locales à
main(). La
fonction ech
ne peut donc
pas modifier
leur valeur.
On le fait
donc en
passant par
l'adresse de
ces variables.
void main()
{
int a = 5 , b = 8;
ech(a,b);
a=?
printf(“ a=%d\n ”, a) ;
b=?
#include <stdio.h>
void ech(int *x,int *y) {
int tampon;
tampon = *x;
*x = *y;
*y = tampon;
}
void main()
{
int a = 5 , b = 8 ;
ech(&a,&b);
printf(“ a=%d\n ”, a) ;
a=?
b=?
printf(“ b=%d\n ”, b) ;
}
PASSAGE DES PARAMETRES
printf(“ b=%d\n ”,b) ;
}
PASSAGE DES PARAMETRES
18
Passage de paramètre par valeur
Lorsqu’on passe un paramètre à une fonction, la fonction ne peut pas modifier la
variable. La variable est automatiquement recopiée et la fonction travaille sur une
copie de la variable. La modification de la copie n’entraîne pas une modification de
la variable originale. C’est le passage de paramètre par valeur.
Exemple:
#include <stdio.h>
void NeModifiePas(int x)
{
x = x+1; /* le x local est modifié, pas le x du main */
}
int main(void)
{
int x=1;
NeModifiePas(x);
printf("%d", x);
/* affiche 1 : valeur de x inchangée */
return 0;
}
19
Passage de paramètre par adresse
L’idée du passage par adresse est que, pour modifier une variable par un
appel de fonction, il faut passer en paramètre non pas la variable, mais
un pointeur qui pointe sur la variable.
Ainsi, le paramètre est l’adresse de x. Lorsqu’on modifie la mémoire à
cette adresse, la donnée x est modifiée, car on travaille bien sur
l’emplacement mémoire de x
20
Exemple (PASSAGE DE PARAMÈTRE PAR ADRESSE)
#include <stdio.h>
/* la fonction suivante prend en paramètre un pointeur */
void Modifie(int *p)
{
*p = *p+1; /* p pointe sur x, la copie de p aussi */
/* le x du main est modifié */
}
int main(void)
{
int x=1;
/* la varible x n’est pas un pointeur */
int *p;
p = &x;
/* pointeur qui pointe sur x */
Modifie(p);
/* affiche 2 */
printf("%d", x);
return 0;
}
21
Exemple (PASSAGE DE PARAMÈTRE PAR ADRESSE)
RQ: L’utilisation explicite d’un pointeur dans le main est superflue, on peut
passer directement l’adresse de x à la fonction, sans avoir besoin de
définir une variable de type pointeur dans le main.
#include <stdio.h>
void Modifie(int *p) /* paramètre de type pointeur */
{
*p = *p+1; /* ce pointeur p a pour valeur &x (x du main) */
}
int main()
{
int x=1;
Modifie(&x); /* on passe directement l’adresse de x */
printf("%d", x); /* affiche 2 */
return 0;
}
Exemple
La fonction scanf, qui lit des variables au clavier, doit modifier le contenu
de ces variables. Pour cette raison, les variables sont passées à scanf par22
adresse, ce qui explique la nécessité des & devant les variables.
Pointeurs et tableaux
Les pointeurs sont très utiles pour travailler avec les variables, mais ils le
sont encore plus quand on les utilise avec les tableaux. En fait, les index
de tableaux ne sont rien d’autre que des pointeurs.
Noms de tableau et pointeurs
Un nom de tableau sans les crochets s’utilise la plupart du temps comme
un pointeur vers le premier élément du tableau. Si vous avez déclaré le
tableau T[] , T aura la valeur de l’adresse de T [0] et sera donc
équivalent à l’expression &T[0].
T------------------------------------ &T [0]
T+1 -------------------------------- &T [1]
T+i -------------------------------- &T[i]
T[i]---------------------------------- * (T+i)
nous obtenons les relations suivantes :
*(T)
== T[0]
*(T+1) == T[1]
*(T+2) == T[2]
etc.
23
*(T+i) == T[i]
Pointeurs et tableaux
char cTableau[]="abc";
/* cTableau pointe sur une zone de 4 octets
/* cTableau[0] = 'a', ….cTableau[3]='\0'
*/
char* cPointeur="abc";
/* l'allocation mémoire est faite directement */
*/
/* à la compilation
Attention :
♦ cTableau est un pointeur constant; le nombre d'octets pointés est constant.
Néanmoins, la chaîne (le contenu) n'est pas constante, puisqu'il s'agit d'un
tableau.
♦ cPointeur est modifiable car il s'agit d'une adresse. Cependant, on ne peut
pas changer directement son contenu.
char cLigne[]="Ali";
cTableau=cLigne;
/* Erreur, cTableau est constant, */
/* on ne peut pas changer son adresse */
strcpy(Ctableau, cLigne); /* Correct car changement de contenu */
cPointeur=cLigne;
/* Correct car changement d'adresse */
strcpy(cPointeur,"efg"); ok
24
Stockage des éléments d’un tableau
Les éléments d’un tableau sont stockés dans des emplacements mémoire
séquentiels, le premier élément ayant l’adresse la plus basse.
25
Stockage des éléments d’un tableau
Conclusion
Vous pouvez constater, à partir de ces exemples, qu’un pointeur devra
être incrémenté de quatre pour accéder à des éléments successifs d’un
tableau de type int, et de huit dans le cas d’un tableau de type double.
Généralement
Pour accéder à des éléments successifs d’un tableau contenant un type
de donnée particulier, le pointeur devra être incrémenté de la valeur sizeof
(typedonnée).
L’opérateur sizeof renvoie la taille en octets du type de donnée reçu en
argument.
RQ:
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.
26
 Ce principe peut être étendu à des tableaux à n entrées
Eemple : Création d’un tableau de taille quelconque
#include <stdio.h>
#include <stdlib.h>
// pour la fonction printf()
// pour la fonction malloc()
void main(){
int i , dim;
long* tableau;
// compteur et taille du tableau
// pointeur pour stocker l‟adresse du tableau
scanf (“%d”, &dim);
// saisie par l'utilisateur de la taille du tableau
tableau = (long*) malloc(dim * sizeof(long)); //allocation (dynamique) du tableau
//
remplissage du tableau, on utilise les indices 0 à (dim -1)
for (i = 0; i < dim; i++)
tableau[i] = i;
//
affichage du contenu du tableau
for (i = 0; i < dim; i++)
printf("%ld\n", tableau[i]);
//
destruction du tableau : libération de la mémoire réservée
free(tableau);
}
27
Un nom de tableau est un pointeur constant
Cas des tableaux à plusieurs indices
Comme pour les tableaux à un indice, l’identificateur d’un tableau, employé
seul, représente toujours son adresse de début.
Toutefois, si l’on s’intéresse à son type exact, il ne s’agit plus d’un pointeur sur
des éléments du tableau.
Lorsque le compilateur rencontre une déclaration telle que :
int t[4] [5] ;
t désigne un tableau de 4 éléments, chacun de ces éléments étant luimême un tableau de 5 entiers.
Autrement dit, si t représente bien l’adresse de début de notre tableau t, il
n’est plus de type int * mais d’un type « pointeur sur des blocs de 4 entiers »
t+1correspond à l’adresse de t, augmentée de 4 entiers (et non plus d’un
seul !).
28
Un nom de tableau est un pointeur constant
Cas des tableaux à plusieurs indices
Un tableau à plusieurs dimensions est un pointeur de pointeur.
les notations
t et &t[0][0] correspondent toujours à la même adresse
t[0] représente l’adresse de début du premier bloc (de 5 entiers) de t,
« Pointeurs » vers des vecteurs :
t[1], celle du second bloc...
0
les notations suivantes sont
totalement équivalentes :
t[0]
équiva &t[0][0]
t[1]
équiva &t[1][0]
1
2
tableau 1D dont chaque élément
« pointe » vers un vecteur
3
0
0
0
0
1
1
1
1
2
2
2
2
4
4
4
4
5
5
5
5
t[2][4],
t[2] est l’adresse
du début du vecteur
d’indice 2, c’est un
pointeur vers t[2][0]
Espace de stockage du tableau
29
Pointeurs et tableaux à plusieurs
dimensions
 Soit la déclaration d‟un tableau A et d‟un pointeur P :int A[M][N], *p;
 l‟instruction P = A[0] crée une liaison entre le pointeur p et la matrice A en
mettent dans p l‟adresse du premier élément de la première ligne de la matrice A ( P =
&A[0][0]).
 A partir du moment ou P = A[0], la manipulation de la matrice A peut se faire
par le biais du pointeur P. En effet :
p
p+1
p+N
p+N+1
p+i*N+j
désigne
désigne
désigne
désigne
désigne
Ou
&A[0][0]
&A[0][1]
&A[1][0]
&A[1][1]
&A[i][j]
et
et
et
et
et
*p
*(p+1)
*(p+N)
*(p+N+1)
*(p+i*N+j)
désigne A[0][0]
désigne A[0][1]
désigne A[1][0]
désigne A[1][1]
désigne A[ i ][ j ]
i є [0 , M-1] et j є [0 , N-1]
30
Pointeurs et tableaux à plusieurs dimensions
main() { //création d’une matrice de 10 lignes et 20 colonnes
int k=10, n=20; int **tab;
tab = (int**)malloc(k * sizeof(int*));
for (i = 0; i < k; i++)
tab[i] = (int*)malloc(n * sizeof(int));
....
for (i = 0; i < k; i++)
free(tab[i]);
free(tab);
}
31
Les opérateurs réalisables sur des pointeurs
Pointeur arithmétique
Nous venons de voir que le pointeur du premier élément d’un tableau doit être
incrémenté d’un nombre d’octets égal à la taille des données du tableau pour
pointer sur l’élément suivant. Pour pointer sur un élément quelconque en
utilisant une notation de type pointeur, on utilise le pointeur arithmétique.
Incrémenter les pointeurs
Incrémenter un pointeur consiste à en augmenter la valeur. Si vous
incrémentez un pointeur de 1, le pointeur arithmétique va augmenter sa valeur
pour qu’il accède à l’élément de tableau suivant.
Exemple
Si ptr_int pointe sur un élément de tableau de type int, l’instruction suivante :
ptr_int++; incrémente la valeur de ce pointeur de 4 pour qu’il pointe sur
l’élément int suivant.
De la même façon, si vous augmentez la valeur du pointeur de n, C va
incrémenter ce pointeur pour qu’il pointe sur le n-ième élément suivant :
ptr_int += 2;
Cette instruction va augmenter de 8 la valeur du pointeur, pour qu’il pointe322
éléments plus loin.
Les opérateurs réalisables sur des pointeurs
Décrémenter les pointeurs
 La décrémentation des pointeurs suit le même principe que
l’incrémentation.
 Si vous utilisez les opérateurs (– –) ou (– =) pour décrémenter un
pointeur, le pointeur arithmétique va diminuer sa valeur
automatiquement en fonction de la taille des données pointées.
Exemple
int v[6]= {1,2,3,4,5,6};
Int *p=v, *p1,*p2;
ptr_int - - ;
ptr_int - = 2;
p1++ pointe sur v[3]
p2-- pointe sur v[4]
p2-p1 donne 3
p += 4
p++
p
1000
v
1
2
3
4
5
6
1016
1000
1008
1012
1020
1004
p1
p2
33
Les opérateurs réalisables sur des pointeurs
La comparaison de pointeurs
Les opérateurs de comparaison = =, !=, >, <, >=et <= peuvent aussi être
utilisés. Les premiers éléments d’un tableau ont toujours une adresse plus
basse que les derniers. Ainsi, si ptr1et ptr2 sont deux pointeurs d’un même
tableau, la comparaison :
ptr1 < ptr2 : est vraie si ptr1 pointe sur un élément d’index plus petit que
ptr2.
Par exemple, voici, en parallèle, deux suites d’instructions réalisant la
même action : mise à 1 des 10 éléments du tableau t :
34
Les opérateurs réalisables sur des pointeurs
Autre utilisation des pointeurs
La dernière opération que l’on peut effectuer avec des pointeurs est la
différence entre deux pointeurs. Si vous avez deux pointeurs sur un même
tableau, le résultat de leur soustraction correspond au nombre d’éléments les
séparant.
Exemple :
ptr1 – ptr2 : donne le nombre d’éléments qui séparent les deux éléments
pointés par ptr1et ptr2.
Beaucoup d’opérations arithmétiques sont réservées aux variables simples,
car elles n’auraient aucun sens avec les pointeurs. Par exemple, si ptr est un
pointeur, l’instruction :
ptr *= 2; générera un message d’erreur.
35
Le Tableau vous donne la liste des six opérations possibles avec les pointeurs.
Description
Opérateur
Affectation
Vous pouvez attribuer une valeur à un pointeur. Cette valeur doit
correspondre à une adresse obtenue avec l‟opérateur d‟adresse (&,), ou à
partir d‟un pointeur constant.
Indirection
L‟opérateur indirect (*) donne la valeur stockée à l‟emplacement pointé.
Adresse de
Vous pouvez utiliser l‟opérateur d‟adresse (&)pour trouver l‟adresse d‟un
pointeur et obtenir un pointeur vers un pointeur.
Incrément
On peut ajouter un nombre entier à la valeur d‟un pointeur pour pointer
sur un emplacement mémoire différent.
Décrément
On peut soustraire un entier à la valeur d‟un pointeur pour pointer sur un
emplacement mémoire différent.
Différence
Vous pouvez soustraire un entier de la valeur d‟un pointeur pour pointer
sur un emplacement mémoire différent.
Comparaison
Ces opérateurs ne sont valides que pour deux pointeurs d‟un même
tableau.
36
Précautions d’emploi
Quand vous utilisez des pointeurs dans un programme, une grosse
erreur est à éviter : utiliser un pointeur non initialisé à gauche d’une
instruction d’affectation. Par exemple, dans la déclaration suivante :
int *ptr;
le pointeur n’est pas initialisé. Cela signifie qu’il ne pointe pas sur un
emplacement connu.
Si vous écrivez :
*ptr = 12;
la valeur 12 va être stockée à l’adresse (inconnue) pointée par ptr. Cette
adresse peut se situer n’importe où en mémoire, au milieu du code du
système d’exploitation par exemple.
La valeur 12 risque d’écraser une donnée importante, et le résultat peut
aller de simples erreurs dans un programme à l’arrêt complet du
système.
À ne pas faire
Effectuer des opérations mathématiques comme des divisions, des
multiplications ou des modulos avec des pointeurs. Les seules
opérations possibles sont l’incrémentation ou le calcul de la différence
entre deux pointeurs d’un même
37
Fonctions permettant la manipulation des chaînes
string.h ou stdlib.h
void *strcat(char *chaine1,char *chaine2)
//Concatène les 2 chaînes, résultat dans chaine1.
void *strcpy(char *chaine1,char *chaine2)
//Copie la chaine2 dans chaine1. + '\0‘
void *strncpy(char *chaine1,char *chaine2, NCmax)
// idem strcpy mais limite la copie au nombre de caractères NCmax.
int strcmp(char *chaine1,char *chaine2)
//Compare les chaînes de caractères chaine1 et chaine2,
renvoie un nombre:
- positif si la chaîne1 est supérieure à la chaine2 (au sens de l'ordre
alphabétique)
- négatif si la chaîne1 est inférieure à la chaîne2
- nul si les chaînes sont identiques.
int strlen(char *chaine)
// renvoie la longueur de la chaine ('\0' non comptabilisé).
int atoi(const char *s)
// convertit l'argument s en un int,
38
Les fonctions de concaténation de chaînes
La fonction strcat
L’appel de strcat se présente ainsi (nous placerons souvent en regard de la
présentation de l’appel d’une fonction le nom du fichier qui en contient le
prototype) :
strcat ( but, source ) (string.h)
Cette fonction recopie la seconde chaîne (source) à la suite de la première
(but), après en avoir effacé le caractère de fin.
39
Les fonctions de concaténation de chaînes
Exemple :
#include <stdio.h>
#include <string.h>
main()
{
char ch1[50] = "bonjour" ;
char * ch2 = " monsieur" ;
printf ("avant : %s\n", ch1) ;
Notez la différence entre les deux
strcat (ch1, ch2) ;
déclarations (avec initialisation) de
printf ("après : %s", ch1) ;
chacune des deux chaînes Ch1 et ch2.
}
La première permet de réserver un
emplacement plus grand que la
constante chaîne qu’on y place
initialement.
RESULTAT
avant : bonjour
après : bonjour monsieur
40
Les fonctions de concaténation de chaînes
La fonction strncat
Cette fonction dont l’appel se présente ainsi :
strncat (but, source, lgmax) (string.h)
travaille de façon semblable à strcat en offrant en outre un contrôle
sur le nombre de caractères qui seront concaténés à la chaîne
d’arrivée (but).
41
Les fonctions de concaténation de chaînes
Exemple : fonction strncat
#include <stdio.h>
#include <string.h>
main()
{
char ch1[50] = "bonjour" ;
char * ch2 = " monsieur" ;
printf ("avant : %s\n", ch1) ;
strncat (ch1, ch2, 6) ;
printf ("après : %s", ch1) ;
}
avant : bonjour
après : bonjour monsi
42
Les fonctions de copie de chaînes
La fonction strcpy
strcpy ( but, source ) (string.h)
recopie la chaîne située à l’adresse source dans l’emplacement
d’adresse destin but. Là encore, il est nécessaire que la taille du
second emplacement soit suffisante pour accueillir la chaîne à recopier,
sous peine d’écrasement intempestif.
Cette fonction fournit comme résultat l’adresse de la chaîne but.
43
Les fonctions de copie de chaînes
La fonction strncpy
strncpy ( but, source, lgmax ) (string.h)
procède de manière analogue à strcpy, en limitant la recopie au
nombre de caractères précisés par l’expression entière lgmax.
Notez bien que, si la longueur de la chaîne source est inférieure à cette
longueur maximale, son caractère de fin (\0) sera effectivement
recopié. Mais, dans le cas contraire, il ne le sera pas.
44
Les fonctions de copie de chaînes
La fonction strncpy
strncpy ( but, source, lgmax )
L’exemple suivant illustre les deux situations :
Fonctions de recopie de chaînes : strcpy et strncpy
#include <stdio.h>
#include <string.h>
main()
{
char ch1[20] = "123456789 10 11 12" ;
char ch2[20] ;
printf (« Donnez un mot : ") ;
gets (ch2) ;
strncpy (ch1, ch2, 6) ;
printf (" ch1 : %s", ch1) ;
}
Donnez un mot : Génie Informatique
ch1 : Génie 789 10 11 12
45
Les fonctions de comparaison de chaînes
Il est possible de comparer deux chaînes en utilisant l’ordre des
caractères définis par leur code.
La fonction :
strcmp ( chaîne1, chaîne2 )
compare deux chaînes dont on lui fournit l’adresse et elle fournit une
valeur entière définie comme étant :
● positive si chaîne1 > chaîne2(c’est-à-dire si chaîne1arrive après
chaîne2, au sens de l’ordre défini par le code des caractères) ;
● nulle si chaîne1 = chaîne2(c’est-à-dire si ces deux chaînes
contiennent exactement la même suite de caractères) ;
● négative si chaîne1 < chaîne2.
46
Les fonctions de comparaison de chaînes
Par exemple (quelle que soit l’implémentation) :
strcmp ("bonjour", "monsieur") est négatif
et :
strcmp ("paris2", "paris10") est positif.
#include <stdio.h>
#include <string.h>
int main () {
char password[] = "123456";
char tmp_password[80];
do {
printf ("Tapez votre mot de passe: ");
gets (tmp_password);
} while (strcmp(password,tmp_password) != 0);
printf ("Bienvenu!");
return 0;
}
47
Les fonctions de comparaison de chaînes
La fonction strncmp :
strncmp ( chaîne1, chaîne2, lgmax )
travaille comme strcmp mais elle limite la comparaison au nombre maximal
de caractères indiqués par l’entier lgmax.
Par exemple :
strncmp ("bonjour", "bon", 4) est positif
tandis que :
strncmp ("bonjour", "bon", 2) vaut zéro.
48
Les fonctions de comparaison de chaînes
Enfin, deux fonctions :
stricmp ( chaîne1, chaîne2 ) (string.h)
strnicmp ( chaîne1, chaîne2, lgmax ) (string.h)
travaillent respectivement comme strcmp et strncmp, mais sans tenir
compte de la différence entre majuscules et minuscules (pour les
seuls caractères alphabétiques)
49
Les fonctions de recherche dans une chaîne
On trouve, en langage C, des fonctions classiques de recherche
de l’occurrence dans une chaîne d’un caractère ou d’une autre
chaîne (nommée alors sous-chaîne).
Elles fournissent comme résultat un pointeur de type char * sur
l’information cherchée en cas de succès, et le pointeur nul dans le
cas contraire.
Voici les principales.
strchr ( chaîne, caractère ) (string.h)
recherche, dans chaîne, la première position où apparaît le
caractère mentionné.
50
Les fonctions de recherche dans une chaîne
 strrchr ( chaîne, caractère ) (string.h)
réalise le même traitement que strchr, mais en explorant la chaîne
concernée à partir de la fin. Elle fournit donc la dernière occurrence
du caractère mentionné.
 strstr ( chaîne, sous-chaîne ) (string.h)
recherche, dans chaîne, la première occurrence complète de la
sous-chaîne mentionnée.
51
La fonction strlen
 La fonction strlen fournit en résultat la longueur
d’une chaîne dont on lui a transmis l’adresse en
argument.
 Cette longueur correspond tout naturellement au
nombre de caractères trouvés depuis l’adresse
indiquée jusqu’au premier caractère de code nul, ce
caractère n’étant pas pris en compte dans la longueur.
Par exemple, l’expression :
strlen ("bonjour")
vaudra 7; de même, avec :
char * adr = "salut" ;
l’expression :
strlen (adr)
vaudra 5.
52
Les fonctions de conversion
Conversion d’une chaîne en valeurs numériques
Il existe trois fonctions permettant de convertir une chaîne de
caractères en une valeur numérique de type int, long ou double.
Ces fonctions ignorent les éventuels espaces de début de chaîne
et, à l’image de ce que font les codes de format %d, %ld et %f, utilisent
les caractères suivants pour fabriquer une valeur numérique.
Le premier caractère invalide arrête l’exploration.
En revanche, ici, si aucun caractère n’est exploitable, ces fonctions
fournissent un résultat nul.
atoi ( chaîne ) (stdlib.h)
fournit un résultat de type int.
atol ( chaîne ) (stdlib.h)
fournit un résultat de type long.
atof ( chaîne ) (stdlib.h)
fournit un résultat de type double.
53
Les fonctions de conversion
Notez que ces fonctions effectuent le même travail que sscanf
appliquée à une seule variable, avec le code de format approprié.
Par exemple (si n est de type int et adr de type char *) :
n = atoi (adr) ;
fait la même chose que :
sscanf (adr, "%d", &n) ;
54
Les fonctions de conversion
Conversion de valeurs numériques en chaîne
La norme ne prévoit pas de fonctions de conversion d’une valeur
numérique en chaîne, c’est-à-dire de fonctions jouant le rôle symétrique
des fonctions atoi, atol, et atof.
En revanche, elle prévoit une fonction sprintf, symétrique de sscanf.
Elle permet de convertir en chaîne une succession de valeurs
numériques, en y incorporant, le cas échéant, d’autres caractères.
Par exemple, si n, de type int, contient 15 et si p, de type float,
contient 785.35 et si tab est un tableau de caractères de taille
suffisante, l’instruction suivante :
sprintf (tab, "%d articles coutent %f F", n, p) ;
placera dans tab, la chaîne suivante (elle sera bien terminée par un
caractère \0) :
15 articles coutent 785.35 F
55
sizeof
void main()
{
int i;
char c;
float f;
double d;
printf („‟caractère : %d \n „‟, sizeof( c));
printf („‟entier : %d \n „‟, sizeof (i));
caractère : 1
entier : 2 ou 4
réel : 4
double : 8
printf („‟réel : %d \n „‟, sizeof(f));
printf („‟double : %d \n „‟, sizeof (d));
}
56
Exercice
Écrire un programme qui lit un verbe du premier groupe (se
termine avec "er") au clavier et qui affiche la conjugaison au
présent de l'indicatif de ce verbe.
Contrôlez s'il s'agit bien d'un verbe qui se termine avec "er"
avant de conjuguer.
57
Téléchargement