C - Tableau à taille variable
Bonsoir,
Comme indiqué dans le titre, je souhaite implémenter en C des tableaux de taille variable. Ils fonctionneraient de cette manière : on déclare une variable de type Liste (qui n'est pas vraiment une liste chaînée mais je n'avais pas d'autre nom), qui est en réalité une structure contenant : la taille du tableau (int) et les données (char *).
Code:
1 2 3 4
| typedef struct{
int longueur; //La taille de la liste
char *valeur; //Les données dans la liste
}Liste; |
On l'initialise via la fonction init, qui se contente de mettre la taille à 0 et d'allouer une case de mémoire de la taille d'un char.
Code:
1 2 3 4 5 6 7
| void init(Liste *l)
{
l->longueur = 0; //On met la taille (= nombre de caractères) à 0
l->valeur = calloc(1, sizeof(char)); //On utilise calloc pour allouer dynamiquement et mettre à 0
if (l->valeur == NULL)
exit(EXIT_FAILURE); //En cas d'erreur, on quitte le programme
} |
Ensuite, je souhaite modifier la chaîne de caractères via une fonction qui en appelle deux autres :
Code:
1 2 3 4 5 6 7 8 9 10 11 12
| void scanListe(Liste *l)
{
char c[2] = {0}; //Chaine qui servira a stocker ce qui est entré par l'utilisateur
changerValeur(l, ""); //On vide la chaine (voir après)
while(c[0] != '\n')
{
c[0] = getchar(); //On récupère le dernier caractère
if(c[0] != '\n') //Si ce n'est pas un retour à la ligne
concatener(l, c); //On met à la fin de la chaine
}
} |
Voici l'implémentation de la fonction changerValeur :
Code:
1 2 3 4 5 6 7 8 9 10
| void changerValeur(Liste *l, char* str)
{
realloc(l->valeur, (strlen(str) + 1) * sizeof(char));
/*On réalloue le tableau avec la taille de la nouvelle chaine. Pas besoin de réallouer à la fin puisque
on veut écraser l'ancienne valeur de la chaine */
if (l->valeur == NULL)
exit(EXIT_FAILURE); //On quitte en cas d'erreur
strcpy(l->valeur, str); //Enfin on copie la nouvelle chaine dans l'ancienne...
l->longueur = strlen(str); //... et on met à jour la taille du tableau
} |
Et enfin la fonction concatener
Code:
1 2 3 4 5 6 7 8 9
| void concatener(Liste *l, char* str)
{
realloc(l->valeur, (l->longueur + (strlen(str) + 1)) * sizeof(char));
/*On réalloue, mais cette fois avec la taille initiale en plus*/
if (l->valeur + l->longueur + 1 == NULL)
exit(EXIT_FAILURE); //On quitte en cas d'erreur
strcpy((l->valeur + l->longueur), str); //On copie la chaine à partir de la fin
l->longueur += strlen(str); //Et on met à jour la taille de la chaine
} |
J'ai en plus fait une fonction qui renvoie seulement la chaine de caractère contenue dans une structure Liste :
Code:
1 2 3 4
| char* chaine(Liste *l)
{
return l->valeur;
} |
Enfin, voici ma fonction qui libère la mémoire :
Code:
1 2 3 4 5
| void liberer(Liste *l)
{
free(l->valeur);
l->valeur = NULL;
} |
La totalité du code est donc :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #include <stdio.h>
#include <stdlib.h>
typedef struct{
int longueur; //La taille de la liste
char *valeur; //Les données dans la liste
}Liste;
void init(Liste *l);
void changerValeur(Liste *l, char* str);
void concatener(Liste *l, char* str);
char* chaine(Liste *l);
void liberer(Liste *l);
void scanListe(Liste *l);
int main()
{
Liste Chaine;
init(&Chaine);
while(strcmp(chaine(&Chaine),"exit") != 0)
{
printf("La liste chainee vaut \"%s\".\nModifiez sa valeur : ", chaine(&Chaine));
scanListe(&Chaine);
}
liberer(&Chaine);
return 0;
}
void init(Liste *l)
{
l->longueur = 0; //On met la taille (= nombre de caractères) à 0
l->valeur = malloc(sizeof(char)); //On utilise calloc pour allouer dynamiquement et mettre à 0
*(l->valeur) = '\0';
if (l->valeur == NULL)
exit(EXIT_FAILURE); //En cas d'erreur, on quitte le programme
}
void scanListe(Liste *l)
{
char c[2] = {0}; //Chaine qui servira a stocker ce qui est entré par l'utilisateur
changerValeur(l, ""); //On vide la chaine (voir après)
while(c[0] != '\n')
{
c[0] = getchar(); //On récupère le dernier caractère
if(c[0] != '\n') //Si ce n'est pas un retour à la ligne
concatener(l, c); //On met à la fin de la chaine
}
}
void changerValeur(Liste *l, char* str)
{
realloc(l->valeur, (strlen(str) + 1) * sizeof(char));
/*On réalloue le tableau avec la taille de la nouvelle chaine. Pas besoin de réallouer à la fin puisque
on veut écraser l'ancienne valeur de la chaine */
if (l->valeur == NULL)
exit(EXIT_FAILURE); //On quitte en cas d'erreur
strcpy(l->valeur, str); //Enfin on copie la nouvelle chaine dans l'ancienne...
l->longueur = strlen(str); //... et on met à jour la taille du tableau
}
void concatener(Liste *l, char* str)
{
realloc(l->valeur, (l->longueur + (strlen(str) + 1)) * sizeof(char));
/*On réalloue, mais cette fois avec la taille initiale en plus*/
if (l->valeur + l->longueur + 1 == NULL)
exit(EXIT_FAILURE); //On quitte en cas d'erreur
strcpy((l->valeur + l->longueur), str); //On copie la chaine à partir de la fin
l->longueur += strlen(str); //Et on met à jour la taille de la chaine
}
void liberer(Liste *l)
{
free(l->valeur);
l->valeur = NULL;
}
char* chaine(Liste *l)
{
return l->valeur;
} |
Ce programme est une boucle qui permet de changer la valeur de Chaine jusqu'a ce qu'on entre "exit", auquel cas l'application se ferme.
Il réside plusieurs bugs lors de l'éxecution de ce programme que je ne parviens pas à résoudre :
- Une et une seule fois, lorsque ce que je rentre dépasse les 7 caractères, l'affichage me sors une chose du type "h>ù" (3 caractères spéciaux généralement)
- Lorsque la liste est de longue taille (plus de deux lignes environ), le programme plante (SEGMENTATION FAULT) lors de la fermeture, pas avant, et ce même si je remodifie la chaîne en quelque chose de plus court.
- Le problème précédant est observé après le return 0; lorsque j'utilise le debugger de Code::Blocks. Je suppose donc que c'est que la chaîne a mal été libérée de la mémoire, mais la fonction free() ne renvoyant rien, je ne peux pas tester son succès avec la valeur de retour.
J'ai bien sûr plusieurs fois vérifié et modifié mon code mais sans succès et sans même trouver d'où peuvent venir ces problèmes, surtout que lorsqu'il est dans la boucle principale, ce code semble fonctionner (hormis le 1er bug qui n’apparaît qu'une fois lors de l’exécution).
Je remercie par avance toute aide et j'espère que vous ferez preuve d'indulgence, je ne suis pas encore très expérimenté.