Un tableau de char est une suite de caractères en mémoire :
chat tab1[10]
Est donc constitué de 10 octets consécutifs en mémoire, 1 par caractère.
tab1[0] est le premier caractère et il est de type char
tab1[9] est le dernier caractère et il est de type char aussi
Ne pas oublier qu'une chaîne de caractères est une suite de caractères se terminant par le caractère invisible '\0' (code ASCII = 0, ne pas confondre avec le caractère représentant le chiffre zéro '0' dont le code ASCII est 48 ou 0x30 en hexa)
Donc pour stocker la chaine de caractère "abcdefhij" de 9 caractères, il faut un char tab[10];
Un tableau ne peut pas changer de taille dynamiquement (contrairement à une String .... quand ca ne plante pas) donc il faut toujours dimensionner les tableaux de manière suffisamment large pour couvrir tous les cas.
Inconvénient : il faut réfléchir car si on se trompe on déborde du tableau et le code crashe.
Avantage : si ton programme compile et se charge, tu es sur qu'il ne te manqueras pas de mémoire durant l'exécution (du moment que tu as bien calculé la taille).
Dit comme cela, on a l'impression qu'on a rien gagné par rapport au String car si on s'est gouré, le programme crashe quand même.
Moi je dis que tu as gagné que le programme ne crashe que si TU t'es trompé dans ton calcul.
Avec les String, le programme crashe sans que tu saches pourquoi.....
Noter aussi que puisque un tableau est une suite de case mémoires, les pointeurs s'appliquent aussi
Et le nom du tableau tab1 est équivalent syntaxiquement à un pointeur sur char pointant sur le 1er élément du tableau
Ainsi
char tab1[10];
char *ptr2;
peuvent être confondus en tant que type.
Mais il y a une grosse différence :
tab1 est un tableau donc de la mémoire a été allouée. Les 10 cases mémoires existes. Mais à partir de la 11eme ca appartient à une autre variable.
ptr2 est un pointeur non initialisé qui pointe n'importe où.
Si tu écris
ptr2 = tab1;
Alors ptr2 devient un pointeur initialisé qui pointe sur la première case du tableau de caractère tab1[]
Et comme on a le droit d'utiliser des crochets avec des pointeurs
tab1 et ptr2 représentent tous les 2 le même élément de type char du tableau
Et si tu fait ++ptr2
Alors tu crée un décalage et ptr2 est équivalent à tab1[i+1]
Par contre il est interdit d'écrire ++tab1 car tab est un tableau et pas un pointeur
Maintenant si tu regarde le code de
*_</em> <em><em>*void strcpy( char *dest, const char *srce ) { while( *srce != '\0' ) // tant que l'on n'est pas sur le '\0' terminal *dest++=*srce++; *dest=*srce; // copie le '\0' terminal }*</em></em> <em>_*
et que tu l'applique à:
```
*char tab1[20] = "abcdef"; // tableau de 20 char initialisé avec une chaine de 6 caractères suivie d'un '\0'
char tab2[10]; // tableau de 10 char non initialisé;
strcpy( tab2, tab1 );
_*_</em> <em>_*- Lors de l'appel de la fonction, tab1 et tab2 sont considérés comme des pointeurs par la fonction.*_</em> <em>_*Mais ce sont des tableaux ce qui assure qu'il y a bien de la mémoire derrière*_</em> <em>_*- La boucle copie les 6 caractères et se termine lorsque srce pointe sur le '\0' final de la chaine. Ce '\0' n'est pas copié*_</em> <em>_*- La dernière ligne de la fonction assure que le '\0' est copié*_</em> <em>_*Tout semble ok.*_</em> <em>_*Mais que ce passe t'il si on a :*_</em> <em>_*
char tab1[20] = "abcdefABCDEF"; // tableau de 20 char initialisé avec une chaine de 12 caractères suivie d'un '\0'
*_</em> <em>_*La boucle va recopier les 12 caractères plus le '\0' (13eme caractère caché) dans une destination qui ne comporte que 10 places.*_</em> <em>_*Résultat on va déborder (on appelle cela en jargon le jardinage mémoire) et écraser les cases mémoires situées après tab2 et qui appartiennent probablement a d'autres variables.*_</em> <em>_*C'est pourquoi il vaut mieux utiliser strncpy() qui compte les caractères :*_</em> <em>_*
*void strncpy( char *dest, const char *srce, int sizedest )
{
while( (sizedest > 0) && (*srce != '\0') ) // tant que l'on n'est pas sur le '\0' terminal
{
*dest++=*srce++;
sizedest--;
}
if ( sizedest > 0 )
*dest=srce; // copie le '\0' terminal
}
*_</em> <em>_*On ajoute un test sur la taille (compteur qui est décrémenté à chaque copie)*_</em> <em>_*Résultat si le code devient :*_</em> <em>_*
strncpy( tab2, tab1, 10 );
*_</em> <em>_*On est sur que l'on ne déborde pas.*_</em> <em>_*MAIS QUE CONTIENT EXACTEMENT LES 10 caractères de tab2 ?*_</em> <em>_*on a tab2[0] = 'a' jusqu'à tab2[9] = 'D'*_</em> <em>_*Est-ce qu'il ne manque pas quelque chose ?*_</em> <em>_*Si : il manque le '\0' final.*_</em> <em>_*strncpy() s’arrête au dernier caractère qui rentre dans le tableau de destination mais si la source est plus grande, la chaîne n'est pas terminée par un '\0'*_</em> <em>_*Une solution est de coder la copie ainsi :*_</em> <em>_*
strncpy( tab2, tab1, 10 );
tab2[9] = '\0';
*_</em> <em>_*Ainsi on est sur que si la chaine dépasse on s'assure quel tab2 se termine par un '\0' propre*_</em> <em>_*Pour éviter les erreurs de codage, on utilisera systématiquement des macros pour définir les tailles de tableau :*_</em> <em>_*_
#define KEYNAME_LEN 12 // Un nom de clé ne fait pas plus de 12 caractères utiles
char NomClef[KEYNAME_LEN+1]
strncpy( NomClef, xxxx, KEYNAME_LEN );
NomClef[KEYNAME_LEN] = '\0';
_*_</em> <em>_*On peut aussi choisir de d'inclure le caractère terminal dans la macro :*_</em> <em>_*_
#define KEYNAME_LEN (12+1) // Un nom de clé ne fait pas plus de 12 caractères utiles + '\0' final
char NomClef[KEYNAME_LEN]
strncpy( NomClef, xxxx, KEYNAME_LEN );
NomClef[KEYNAME_LEN-1] = '\0';
_```
Un petit mot pour finir sur les tableaux à 2 dimensions. Si tu veut mémoriser plusieurs chaines de caractères :_
#define KEYNAME_LEN 12*
#define NBKEYS 5
char MesClefs[NBKEYS][KEYNAME_LEN+1];
MesClefs[0][0] est donc le premier caractère de la 1ère clef
MesClefs[0][12] est donc le 13eme caractère de la 2ère clef (réservé pour le '\0' terminal si le nom de clef fait effectivement 12 caractères)
MesClefs[1][0] est le 1er caractère de la 2eme clef
etc ...
MesClefs[0] est équivalent à un pointeur sur le 1er caractère de la 1ere clef (type char *)
MesClefs est équivalent à un pointeur sur .... char [KEYNAME_LEN+1]
Effectivement MesClefs n'est pas tout a fait équivalent à un char ** mais bien à un pointeur sur tableau car il "conserve" l'information de taille du tableau pointé.
Ca commence à être un peu compliqué mais si besoin je pourrais compléter.
Juste pour terminé une petite question pour voir si j'ai été clair : A quoi est équivalent MesClefs[0][13] ?
(si j'écrit MesClefs[0][13] = 'A', où va s'écrire le caractère 'A' ?)