Programmation C
Tableaux - Chaînes de caractères |
|

|
|

|
Tableaux
- Type de données permettant d'agréger en une seule variable un ensemble de valeurs de même type
- Repérage des valeurs par la donnée de un ou plusieurs indices entiers (tableau de dimension 1 si 1 indice, tableau de dimension 2 si 2 indices...)
- Possibilité de définir des tableaux de tout type élémentaire
- Deux façons de gérer la taille des tableaux (nombre de valeurs pouvant être stockées et, incidemment, quantité de mémoire allouée pour ce stockage)
- Allocation statique : taille constante définie au moment de la compilation
- Allocation dynamique : taille définie en cours d'exécution (voir chapitre Allocation dynamique de la mémoire)
- Syntaxe en dimension 2
nomType nomVariableTableau[VALEUR_TAILLE_1][VALEUR_TAILLE_2];
- Syntaxe en dimension 3
nomType nomVariableTableau[VALEUR_TAILLE_1][VALEUR_TAILLE_2][VALEUR_TAILLE_3];
- Syntaxe en dimension n
nomType nomVariableTableau[VALEUR_TAILLE_1][VALEUR_TAILLE_2]...[VALEUR_TAILLE_N];
- Initialisation lors de la définition
- Possibilité d'indiquer des valeurs d'initialisation lors de la définition d'une variable tableau
- Syntaxe 1 : Taille de tableau explicite et initialisation avec les valeurs spécifiées entre accolades
nomType nomVariableTableau[VALEUR_TAILLE] = { v1, v2, v3, ..., vn };
Initialisation des n premières valeurs du tableau avec les n valeurs entre accolades
Maintien à la valeur indéterminée des valeurs restantes
n <= VALEUR_TAILLE sinon non compilable
- Syntaxe 2 : Taille de tableau définie implicitement avec le nombre de valeurs spécifiées entre accolades
nomType nomVariableTableau[] = { v1, v2, v3, ..., vn };
Définition d'un tableau de taille n
Initialisation du tableau avec les n valeurs entre accolades
- Connaître la taille d'un tableau
- Utilisation de l'opérateur sizeof pour calculer le nombre global d'octets du tableau et le nombre d'octets d'un élément du tableau
Exemple : sizeof(t) pour le nombre global d'octets occupés par le tableau t et sizeof(t[0]) pour le nombre d'octets de l'élement d'indice 0 du tableau
- Division de l'un par l'autre pour obtenir la taille
- Important : Repérage explicite possible de tout tableau alloué statiquement au moyen d'une variable de type pointeur sur le type élémentaire du tableau
- Possibilité de récupérer l'adresse d'un tableau (l'adresse du premier élément du tableau) via l'opérateur & appliqué à la variable tableau qui fournit un pointeur sur le type élémentaire
du tableau
Exemple : pour un tableau t défini par int t[VALEUR_TAILLE];, &t donne l'adresse du tableau sous la forme d'un pointeur sur int. Possibilité d'écrire : int *pt = &t;
- Possibilité de récupérer l'adresse de n'importe quel élément d'un tableau via l'opérateur & appliqué à l'élément désigné explicitement par son indice qui fournit un pointeur
sur le type élémentaire du tableau
Exemple : pour un tableau t défini par double t[VALEUR_TAILLE];, &t[0] donne l'adresse du double d'indice 0, &t[i] donne l'adresse du double d'indice i. Possibilité d'écrire
: double *pt = &t[0];
- Pour les tableaux de dimension 1, &t toujours égal à &t[0], Pour les tableaux de dimension 2, &t toujours égal à &t[0][0]...
- Valeurs du tableau placées linéairement dans la zone mémoire pointée par le pointeur même s'il s'agit d'un tableau de dimension supérieure à 1
-> Stockage linéarisé
-> Gestion automatique et transparente pour le développeur de l'accès au bon élément en fonction des indices fournis en dimension 1 ou supérieure
- Exemples
#include <stdio.h>
#define TAILLE_T2 8
void main(void) {
int t1[10];
for (int i = 0; i < 10; i++) {
printf("%d\n", t1[i]);
}
printf("\n");
t1[0] = 1;
for (int i = 1; i < 10; i++) {
t1[i] = i*t1[i-1];
}
for (int i = 0; i < 10; i++) {
printf("%d\n",t1[i]);
}
printf("\n");
double t2[TAILLE_T2];
for (int i = 0; i < TAILLE_T2; i++) {
t2[i] = i * 0.5;
}
for (int i = 0; i < TAILLE_T2; i++) {
printf("%lf\n", t2[i]);
}
printf("\n");
printf("%zd %zd\n", sizeof(t1), sizeof(t2));
}
|
Tableaux1.c - Tableaux1.exe |
90
0
1073741824
-1140847104
792273360
32759
792270745
32759
31
0
1
1
2
6
24
120
720
5040
40320
36288
0.000000
0.500000
1.000000
1.500000
2.000000
2.500000
3.000000
3.500000
40 64 |
|
#include <stdio.h>
#include <stdlib.h>
#define NOMBRE_LIGNES 6
#define NOMBRE_COLONNES 8
void main(void) {
srand(10);
int tab[NOMBRE_LIGNES][NOMBRE_COLONNES];
for (int l = 0; l < NOMBRE_LIGNES; l++) {
for (int c = 0; c < NOMBRE_COLONNES; c++) {
tab[l][c] = rand() % 10;
}
}
for (int l = 0; l < NOMBRE_LIGNES; l++) {
for (int c = 0; c < NOMBRE_COLONNES; c++) {
printf("%3d", tab[l][c]);
}
printf("\n");
}
printf("\n");
}
|
Tableaux2.c - Tableaux2.exe |
1 9 2 4 7 6 2 2
6 9 4 1 3 4 3 2
1 9 4 0 9 0 3 6
7 7 0 7 1 3 5 9
9 4 8 2 1 7 8 1
9 5 3 3 2 3 9 7 |
|
#include <stdio.h>
#define TAILLE_T1 12
#define NOMBRE_LIGNES 6
#define NOMBRE_COLONNES 8
void main(void) {
double t1[TAILLE_T1];
printf("%zu %zu\n", sizeof(t1), sizeof(t1[0]));
int TAILLE = sizeof(t1) / sizeof(t1[0]);
printf("%d\n", TAILLE);
printf("\n");
int t2[NOMBRE_LIGNES][NOMBRE_COLONNES];
printf("%zu %zu %zu\n", sizeof(t2), sizeof(t2[0]), sizeof(t2[0][0]));
int nl = sizeof(t2) / sizeof(t2[0]);
int nc = sizeof(t2[0]) / sizeof(t2[0][0]);
printf("%d %d\n", nl, nc);
}
|
TableauxSizeof.c - TableauxSizeof.exe |
|
- Arithmétique des pointeurs et tableaux
- Possibilité d'utiliser les pointeurs pour parcourir des tableaux en utilisant l'arithmétique des pointeurs
- Existence d'opérateurs applicables aux variables de type pointeur pour en modifier le contenu
- Opérateur + : Ajout à la variable pointeur d'un nombre d'octets égal au type sur lequel pointe le pointeur
Exemple : p = p + 1, où p est une variable de type pointeur sur int et 1 est le nombre entier 1
-> Incrémentation de l'adresse contenue dans p de 1 * le nombre d’octets du type pointé par p c'est à dire 1 * sizeof(int)
-> Après ajout, pointage par p de l'int suivant immédiatement en mémoire l'int initialement pointé par p
(même s'il ne s'agit pas d'un int !)
- Opérateur - : Equivalent à l'opérateur + mais pour retrancher au lieu d'ajouter
- Opérateur ++ : Equivalent à ajouter 1
- Opérateur -- : Equivalent à retrancher 1
- Possibilité d'ajouter tout nombre entier n ou de retrancher tout nombre entier n
- Exemple
#include <stdio.h>
#include <stdlib.h>
#define NOMBRE_VALEURS 6
#define NOMBRE_LIGNES 4
#define NOMBRE_COLONNES 3
void main(void) {
srand(10);
double tab1[NOMBRE_VALEURS];
int tab2[NOMBRE_LIGNES][NOMBRE_COLONNES];
for (int i = 0; i < NOMBRE_VALEURS; i++) {
tab1[i] = (rand() % 101) / 100.0;
}
for (int i = 0; i < NOMBRE_VALEURS; i++) {
printf("%p : %6.2lf\n", &tab1[i], tab1[i]);
}
printf("\n");
double* p1 = tab1;
printf("%p %p\n", tab1, p1);
for (int i = 0; i < NOMBRE_VALEURS; i++) {
printf("%p : %6.2lf\n", p1, *p1);
p1++;
}
for (int l = 0; l < NOMBRE_LIGNES; l++) {
for (int c = 0; c < NOMBRE_COLONNES; c++) {
tab2[l][c] = rand() % 10;
}
}
printf("\n");
for (int l = 0; l < NOMBRE_LIGNES; l++) {
for (int c = 0; c < NOMBRE_COLONNES; c++) {
printf("%p : %2d ", &tab2[l][c], tab2[l][c]);
}
printf("\n");
}
printf("\n");
int* p2 = &tab2[0][0];
printf("%p %p\n", tab2, p2);
for (int i = 0; i < NOMBRE_LIGNES * NOMBRE_COLONNES; i++) {
printf("%p : %2d\n", p2, *p2);
p2++;
}
}
|
TableauxEtPointeurs.c - TableauxEtPointeurs.exe |
0000002D028FFB10 : 0.71
0000002D028FFB18 : 0.32
0000002D028FFB20 : 0.40
0000002D028FFB28 : 0.59
0000002D028FFB30 : 0.62
0000002D028FFB38 : 0.15
0000002D028FFB10 0000002D028FFB10
0000002D028FFB10 : 0.71
0000002D028FFB18 : 0.32
0000002D028FFB20 : 0.40
0000002D028FFB28 : 0.59
0000002D028FFB30 : 0.62
0000002D028FFB38 : 0.15
0000002D028FFB40 : 2 0000002D028FFB44 : 2 0000002D028FFB48 : 6
0000002D028FFB4C : 9 0000002D028FFB50 : 4 0000002D028FFB54 : 1
0000002D028FFB58 : 3 0000002D028FFB5C : 4 0000002D028FFB60 : 3
0000002D028FFB64 : 2 0000002D028FFB68 : 1 0000002D028FFB6C : 9
0000002D028FFB40 0000002D028FFB40
0000002D028FFB40 : 2
0000002D028FFB44 : 2
0000002D028FFB48 : 6
0000002D028FFB4C : 9
0000002D028FFB50 : 4
0000002D028FFB54 : 1
0000002D028FFB58 : 3
0000002D028FFB5C : 4
0000002D028FFB60 : 3
0000002D028FFB64 : 2
0000002D028FFB68 : 1
0000002D028FFB6C : 9 |
|
- Tableaux statiques vs fonctions
- Possibilité de transmettre un tableau de dimension 1 alloué statiquement en paramètre à une fonction
- Syntaxe d'entête
nomType1 nomFonction(nomType2[] nomParametre)
- Autre syntaxe possible : transmission du tableau en tant que variable de type pointeur sur le type élémentaire du tableau selon l'entête
nomType1 nomFonction(nomType2* nomParametre)
- Dans les deux cas, réception par la fonction de l'adresse mémoire du tableau
- Problème : comment connaître la taille du tableau reçu par la fonction ?
- Impossible avec sizeof car la fonction reçoit un pointeur (une adresse) en entête donc si on utilise sizeof, on obtient la taille du pointeur et non la taille du tableau pointé par le pointeur
- Solution 1 : avoir la taille en paramètre global -> perte complète de la dynamicité concernant la taille -> pas génial
- Solution 2 : passer à la fonction la taille en paramètre supplémentaire -> assez lourd
- Possibilité de transmettre un tableau de dimension supérieure à 1 alloué statiquement en paramètre à une fonction
- Impossibilité de faire retourner à une fonction un tableau alloué statiquement dans la fonction quelle que soit son type élémentaire, sa dimension ou sa taille
- Conclusion : tableaux statiques limités fonctionnellement
- Attention
- Pas de contrôle en langage C de la validité des indices utilisés sur les tableaux
- Pas de contrôle en langage C des opérations réalisées en arithmétique des pointeurs
- Possibilité d'aller lire et écrire en dehors des tableaux
-> Corruption de la mémoire
-> dysfonctionnement voire plantage
-> Comportement erratique
|
Chaînes de caractères
- Pas explicitement de type chaîne de caractères en langage C
- Chaînes de caractères représentées par des tableaux de char contenant les caractères de la chaîne de caractères
- Fin de la chaîne repéré par le fait que le dernier caractère est suivi du char 0x00 c'est à dire le caractère de code ASCII zéro
-> Utilisation d'un tableau de n+1 char pour stocker une chaîne de caractères de n caractères
- Possibilité de déclarer une variable chaîne de caractères explicitement comme une variable pointeur sur char si on l'affecte avec l'adresse d'un tableau de char correctement défini
- Possibilité d'affecter une variable pointeur sur char avec une constante littérale de type chaîne de caractères mais attention, chaîne non modifiable par écriture directe dans sa zone mémoire
- Exemples
#include <stdio.h>
void main(void) {
char* s1 = "ABCD";
char s2[] = { 'a','b','c','d','e','\0' };
char s3[50];
for (int i = 0; i < 10; i++) {
s3[i] = '0' + i;
}
s3[10] = 0x00;
printf("%s\n", s1);
printf("%s\n", s2);
printf("%s\n", s3);
}
|
ChainesDeCaracteres1.c - ChainesDeCaracteres1.exe |
|
- Manipulation sur les chaînes de caractères
- Possibilité de développer ses propres fonctions en passant en paramètre de fonction les chaînes à traiter
- Attention, comme pour tout tableau, pas de retour sur char* d'une fonction sur un tableau
défini statiquement localement à la fonction
Exemple
#include <stdio.h>
int calculerNombreCaracteres(const char* chaine) {
int cpt = 0;
while (chaine[cpt] != 0x00) {
cpt++;
}
return cpt;
}
void copier(const char* src, char* dst) {
int i = 0;
while (src[i] != 0x00) {
dst[i] = src[i];
i++;
}
dst[i] = 0x00;
}
void substituer(char* chaine, char ancien, char nouveau) {
int i = 0;
while (chaine[i] != 0x00) {
if (chaine[i] == ancien) {
chaine[i] = nouveau;
}
i++;
}
}
void copier2(const char* src, char* dst) {
char* s = (char*)src;
char* d = dst;
while (*s != 0x00) {
*d = *s;
s++;
d++;
}
*d = 0x00;
}
void substituer2(char* chaine, char ancien, char nouveau) {
char* c = chaine;
while (*c != 0x00) {
if (*c == ancien) {
c[0] = nouveau;
}
c++;
}
}
void main(void) {
char* s = "abcdefabcdef"; // Attention! non modifiable
char t[20];
printf("Chaine : %s\n", s);
printf("Longeur : %d\n", calculerNombreCaracteres(s));
copier(s, t);
printf("Chaine copiee : %s\n", s);
printf("%s\n", t);
substituer(t, 'a', 'f');
printf("Substituion de 'a' par 'f' : %s\n", t);
substituer(t, 'f', '0');
printf("Substituion de 'f' par '0' : %s\n", t);
}
|
ChainesDeCaracteresFonctions.c - ChainesDeCaracteresFonctions.exe |
|
- Possibilité d'utiliser les fonctions de la librairie standard string.h si elles correspondent aux besoins
- Calcul du nombre de caractères d'une chaîne de caractères : strlen
- Concaténation d'une chaîne de caractères au bout d'une autre chaîne de caractères : strcat
- Copie d'une chaîne de caractères vers une autre chaîne de caractères : strcpy
- Recherche d'un caractère dans une chaîne de caractères : strchr
- Recherche d'une chaîne de caractères dans une chaîne de caractères : strstr
- Comparaison entre deux chaînes de caractères : strcmp
- ...
Exemple
#include <stdio.h>
#include <string.h>
void main(void) {
char* s0 = "abcd";
char* s1 = "ABCD";
char s2[] = { '0','1','2','3','4','\0' };
char s3[50];
printf("%s\n", s0);
printf("%s\n", s1);
printf("%s\n", s2);
printf("\n");
strcpy(s3, s1);
printf("%s possede %zu caracteres\n", s3, strlen(s3));
strcat(s3, s2);
printf("%s possede %zu caracteres\n", s3, strlen(s3));
printf("Comparaison de %s et %s : %d\n", s0, s0, strcmp(s0, s0));
printf("Comparaison de %s et %s : %d\n", s0, s1, strcmp(s0, s1));
printf("Comparaison de %s et %s : %d\n", s1, s0, strcmp(s1, s0));
char c = 'z';
char* pc = strchr(s0, c);
if (pc != NULL) {
printf("%c existe dans %s et est en position %d\n", c, s0,(int) (pc-s0) );
}
else {
printf("%c n'existe pas dans %s\n", c, s0);
}
}
|
ChainesDeCaracteresAPIString.c - ChainesDeCaracteresAPIString.exe |
abcd
ABCD
01234
ABCD possede 4 caracteres
ABCD01234 possede 9 caracteres
Comparaison de abcd et abcd : 0
Comparaison de abcd et ABCD : 1
Comparaison de ABCD et abcd : -1
z n'existe pas dans abcd
c existe dans abcd et est en position 2
|
|
|