Je viens à vous aujourd'hui pour vous exposer mon problème sur lequel je sèche depuis plusieurs jours maintenant.
En effet, j'essaie de récupérer un nombre de caractère dans un fichier. Sachant que je ne connais pas ce nombre exacte (car il peut changer) j'utilise l'allocation dynamique de mémoire à l'aide de malloc et de realloc.
Le problème est que l'orsque j'arrive au 3 ème caractère, le programme stop et j'ai le droit à un erreur de segmentation. (Segmentation fault (Core dumped)). J'imagine que c'est mon utilisation de realloc qui doit m'échapper et donc que je fais un dépassement de mémoire. Mais malgrés tous mes tests et recherche, je n'arrive pas à trouver ou.
Je vous met mon code (seulement la boucle charger de récupérer les caractères du fichier).
// Get all objects in map file
while (car != EOF)
{
if ((car != '\n') && (car != '\r'))
{
nbObjects++;
if (nbObjects == 1)
{
*objects = (char*) malloc(nbObjects);
}
else
{
*objects = (char*) realloc(*objects, nbObjects);
}
if (*objects == NULL)
{
error("The memory allocation has failed", false);
}
*objects[nbObjects - 1] = car;
}
}
Les variables sont bien évidemment déclarer plus haut.
Je vous remercie de l'attention que vous porterez à mon problème.
PS: A savoir que j'utilise l'étoile devant le pointeur objects car je l'ai récupérer dans les paramètres d'une fonction. Et donc, j'ai utiliser le double pointeur.
Bonne soirée !
- Edité par peridot69 9 août 2017 à 21:27:59
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
Nous sommes donc d'accord qu'objet est un pointeur sur pointeur ? Déjà que les pointeurs sont des nids à erreurs alors les pointeurs sur pointeurs.... Je veux dire que si tu peux éviter de les utiliser, il faut le faire. Après il y a certainement des cas où on a pas le choix, mais autant que possible il faut éviter.
Par exemple faire comme la fonction realloc, tu prends en paramètre un simple pointeur et tu renvois sa nouvelle adresse. Voici la méthode que j'utilise, certainement pas la meilleure mais elle me suffit.
//Fonction qui augmente la taille d'un bloc mémoire de 6, complètement inutile mais c'est pour l'exemple
char* monAlloc(char* ptr, size_t size, int* erreur)
{
ptr=(char*) realloc(ptr, size+6);
if(ptr)
{
*erreur=0;
return ptr;
}
else
{
*erreur = TON_CODE_ERREUR;
return NULL;
}
}
Via cette fonction tu peux renvoyer toute une variété de codes d'erreur grâce au pointeur "int* erreur" (même si ici c'est complètement inutile, mais tu vois ainsi comment faire si ça te prive du retour d'un int par exemple), et en même temps je n'ai pas de pointeurs sur pointeurs.
En effet, j'avais étudier cette solution, le problème est que le pointeur en lui même j'ai besoin de le créer et possiblement d'utiliser avant l'utilisation de la fonction qui cherche les caractère.
De plus, j'aurais besoin à tous moment de la taille du tableau (le nbObjects renvoyer). Ce qui fait que je ne peux pas me passer de cette information et je doit donc la renvoyer (je pourrais utiliser le passage par adresse mais ce serais utiliser un bazooka pour tuer une mouche à mon sens).
Aussi la le double pointeur est très utile et presque nécessaire. Néanmoins je te remercie, mais à savoir que le code n'est pas parfait, j'ai pas encore sécurisé le pointeur.
Par exemple. Si le realloc échoue (ou le malloc, mais j'ai réussis à m'en passer depuis quelques minutes) je n'aurais pas libérer l'espace mémoire et j'aurais perdu le seul accès qui me permettais de le faire.
Juste ce que je ne comprend pas c'est la dépassement mémoire. Pourtant je m'assure bien d'agrandir petit à petit l'espace allouer et encore plus étrange, c'est que c'est au moment du stockage du troisième caractère (à la ligne 22) que le dépassement intervient. Pourtant le test pour savoir si le realloc a fonctionner passe. Ducoup je ne comprend vraiment plus...
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
Ah oui tu as tout à fait raison pour realloc (il faut dire que j'avais oublié cette subtilité, je n'ai jamais vraiment utilisé realloc jusque là), il faut utiliser un autre pointeur déclarer localement pour récupérer le retour le temps de verifier si ça a marché ou pas, et en cas d'échec gérer l'erreur (par exemple avec free).
Non je pense que le retour par adresse de nbObjets est la meilleure solution, plus une tapette à mouche qu'un bazooca pour le coup, les pointeurs sur pointeurs c'est vraiment le bordel, un simple passage par adresse pour les éviter est un bien moindre prix à payer.
Concernant ton problème toute de même, je me demande si tu ne devrais pas remplacer :
*objects[nbObjects - 1] = car;
Par :
(*objects)[nbObjects - 1] = car;
Quand je dis que les pointeurs sur pointeurs c'est vraiment la peste c'est aussi parce que parfois une parenthes en moins et pouf, plus rien.
Dans les faits ça se gère, mais VRAIMENT, il vaut mieux éviter quand on peut pour diminuer le risque de crash.
Malheureusement j'ai essayer ta solution et rien n'y fais...
Pour des raison pratique je choisis de renvoyer le nombre d'objets plutôt que le pointeur. Puisque de toute manière, je ne pourrais pas y échapper éternellement et je dois donc essayer de trouve la solution maintenant.
Mais l'idée est bonne je la garde en tête.
En revanche je vais remettre mon code, car il a bien changer depuis :
// Get all objects in map file
while (car != EOF)
{
if ((car != '\n') && (car != '\r'))
{
nbObjects++;
inter_objects = (char*) realloc(*objects, nbObjects);
// Securing the pointer
if ((inter_objects == NULL))
{
free(*objects);
error("The memory allocation has failed", false);
}
*objects = inter_objects;
**(objects + (nbObjects - 1)) = car;
}
car = fgetc(map_file);
}
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
Déjà, on ne cast pas les malloc() / realloc() / calloc() en C.
Ensuite, ligne 14: realloc() prend le pointeur à réallouer comme premier argument, pas le contenu de ce pointeur
Enfin, pour peu qu'on ait convenablement lu le manuel au sujet de realloc(), on peut se passer du test ligne 8: si le pointeur passé à realloc() est NULL, alors realloc() se comporte comme malloc()...
- Edité par edgarjacobs 9 août 2017 à 23:46:03
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Déjà, on ne cast pas les malloc() / realloc() / calloc() en C.
Ensuite, ligne 14: realloc prend le pointeur à réallouer comme premier argument, pas l' adresse de ce pointeur
Enfin, pour peu qu'on ait convenablement lu le manuel au sujet de realloc(), on peut se passer du test ligne 8: si le pointeur passé à realloc() est NULL, alors realloc() se comporte comme malloc()...
Pour les malloc / realloc / calloc en effet j'ai vu que malgrés tous sa fonctionnais mais si tu me dis qu'on ne le fais en C je me pencherais pour savoir si oui ou non c'est accepter dans une des normes C (je compile en C11).
En revanche pour le realloc et le premier argument, c'est ce que je fais, je passe le pointeur (d’où la première étoile) et non l'adresse (alors il ne faudrait pas mettre d'étoile). Et dans le cas ou je ne mettrais pas d'étoile j'ai le droit à un : "Invalid pointer". Et avec les 2 étoiles, sa équivaut à lui donner un char en argument.
Du coup si je ne vois pas la solution, serais-t'il possible de me l'indiquer s'il te plaît ?
Pour le dernier point, le code a été modifié dans mon dernier post. Donc plus de soucis à ce niveau la.
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
Object a-t'il été correctement initialisé avant l'appel à realloc ? Soit à NULL soit à une adresse allouée.
nbObjects a-t'il une valeur initiale cohérente ? À savoir le nombre d'éléments du tableau et non pas la position du dernier.
Je dirais plutôt :
*(*objects + (nbObjects-1))=car ;
objects a la valeur NULL avant d'entrer dans la fonction et n'est modifier que dans cette boucle.
Pour ce qui est de nbObjects il a une valeur initiale de 0. Et est incrémenter à chaque tour de boucle (si le caractère n'est ni un retour ligne/retour chariot ni 'EOF'). Donc de toute évidence je dirais que oui. Pour un fichier contenant '0123456789' il fait respectivement : '1, 2, 3, 4, 5, 6, 7, 8, 9, 10'.
En théorie (je dis bien en théorie) tous va bien jusqu'au troisième caractère.
Voici l'appel de la fonction :
// Appel de la fonction :
// ----------------------
char *objects = NULL;
nbObj = loadMap("Test", &objects);
Et voici la fonction au complet :
/*
* Charge tous les objets de la scène à partir d'un fichier et renvoie le nombre de ceux-ci.
*
* @param char filename[] Nom du fichier
* @param char **objects Liste d'objets pour le stockage
*
* @return int Nombre d'objets charger
*/
int loadMap(char filename[], char **objects)
{
char *inter_objects = NULL;
FILE* map_file = NULL;
int nbObjects = 0;
char car;
map_file = fopen(filename, "r");
if (map_file == NULL)
{
fprintf(stderr, "The file '%s' was not found\n", filename);
exit(EXIT_FAILURE);
}
car = fgetc(map_file);
// Get all objects in map file
while (car != EOF)
{
if ((car != '\n') && (car != '\r'))
{
nbObjects++;
inter_objects = (char*) realloc(*objects, nbObjects);
if ((inter_objects == NULL))
{
free(*objects);
error("The memory allocation has failed", false);
}
*objects = inter_objects;
**(objects + (nbObjects - 1)) = car;
}
car = fgetc(map_file);
}
fclose(map_file);
if (*objects == NULL && (nbObjects != 0))
{
error("The memory allocation has failed", false);
}
return nbObjects;
}
- Edité par peridot69 9 août 2017 à 23:56:59
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
Eeeeg ! C'est ce que j'ai proposé plus haut, tu as mal recopié (pourquoi tu as ajouté des parenthèses ?) ! Boulet va ! Je me tue à t'aider et tu te trompes quand je donne la solution !
On ne cast plus les retours des fonctions de memory management.
nbObjects++;
inter_objects = (char*) realloc(*objects, nbObjects);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
^ for christ's sake ! It's so slow... allow several byte in one go and resize if you want to O_o.
Allouer byte par byte, c'est lent.
- Edité par DiscorverYourboringlife 10 août 2017 à 0:41:56
On ne cast plus les retours des fonctions de memory management.
nbObjects++;
inter_objects = (char*) realloc(*objects, nbObjects);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
^ for christ's sake ! It's so slow... allow several byte in one go and resize if you want to O_o.
Allouer byte par byte, c'est lent.
- Edité par DiscorverYourboringlife il y a environ 22 heures
En effet je suis d'accord sur tous les points (Sauf sur certains mais c'est plutôt une question de goût, notamment l'endroit ou l'on choisis de déclarer les variables).
En revanche, je ne comprend pas pourquoi allouer byte par byte est lent. Je veux dire, dans mon cas, je ne sais pas à l'avance combien de byte je vais devoir allouer. Ce serais à tester, mais parcourir tous le fichier pour compter le nombre de caractère, allouer tous les bytes d'un coup, puis ensuite revenir au début du fichier, le reparcourir puis les stocker. Je pense que en terme de performance on gagne pas des masses (pour peu qu'on en gagne). Surtout si le fichier fais des milliers voir des centaines de milliers de caractères.
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
En revanche, je ne comprend pas pourquoi allouer byte par byte est lent. Je veux dire, dans mon cas, je ne sais pas à l'avance combien de byte je vais devoir allouer. Ce serais à tester, mais parcourir tous le fichier pour compter le nombre de caractère, allouer tous les bytes d'un coup, puis ensuite revenir au début du fichier, le reparcourir puis les stocker. Je pense que en terme de performance on gagne pas des masses (pour peu qu'on en gagne). Surtout si le fichier fais des milliers voir des centaines de milliers de caractères.
Non, je ne fais pas référence à cette méthode. Mais à une autre qui serait ( toujours en ne parcourant qu'une fois le fichier ) d'allouer plusieurs bytes d'un coup ( admettons 16 bytes ), jusqu'à atteindre la fin puis de redimensionner si nécessaire. Tiens, un code source illustrant mes propos. ( Surement pas parfait, mais fonctionnel )
On ne cast plus les retours des fonctions de memory management.
nbObjects++;
inter_objects = (char*) realloc(*objects, nbObjects);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
^ for christ's sake ! It's so slow... allow several byte in one go and resize if you want to O_o.
Allouer byte par byte, c'est lent.
- Edité par DiscorverYourboringlife hier à 0:41
Etant en C11, je ne penses pas qu'il puisse déclarer ses variables au moment où il en a besoin, mais forcément au début. D'expérience je dirais que c'est à partir de C99 que l'on peut faire ça.
Pour les fonctions d'allocation mémoire, je suis obligé de caster les fonctions sous peine d'avoir des erreurs du style: impossible de convertir de "void*" en ...
Du coup, comment tu fais pour ne pas faire de cast. Ca m'intéresse parcque c'est bête d'utiliser une fonction standart pas correctement ???
Plus j'apprends et plus je me rends compte que je ne sais rien.
Etant en C11, je ne penses pas qu'il puisse déclarer ses variables au moment où il en a besoin, mais forcément au début. D'expérience je dirais que c'est à partir de C99 que l'on peut faire ça.
Mais au bout d'un moment, il faut se mettre à jour. Un tant soit peu ton compilateur est à jour, il devrait compiler automatiquement en c11 ou gnu11.
quent34 a écrit:
Pour les fonctions d'allocation mémoire, je suis obligé de caster les fonctions sous peine d'avoir des erreurs du style: impossible de convertir de "void*" en ...
Du coup, comment tu fais pour ne pas faire de cast. Ca m'intéresse parcque c'est bête d'utiliser une fonction standart pas correctement ???
Il nous faudrait plus d'information, du style : le compilateur utilisé, la version de ce compilateur, ta ligne de commande pour compiler, et éventuellement un code source démontrant l'erreur émisse, avec celle-ci.
- Edité par DiscoverThenewhorizon 11 août 2017 à 11:55:59
Pour les fonctions d'allocation mémoire, je suis obligé de caster les fonctions sous peine d'avoir des erreurs du style: impossible de convertir de "void*" en ...
A mon avis, tu compiles en C++: l'extension de tes fichiers sources n'est pas .C, mais .CPP
- Edité par edgarjacobs 11 août 2017 à 11:59:51
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le cast de la fonction malloc/realloc n'est pas nécessaire car elle renvoie un pointeur sur void. Et en C la conversion est faite automatiquement vers le type de variables qui récupère ce pointeur void.
// Pas besoin de cast, C comprend qu'il doit renvoyer vers un type int
int test = malloc(4);
En revanche, bien qu'il ne soit pas obligatoire de caster, il peut être utile de le faire pour au moins 2 raison :
1 - Pour garder une compatibilité avec les compilateurs plus anciens (qui eux n'autorisais pas la conversion implicite?)
2 - Car cela rend le code compatible avec C++
Ce sont les 2 principaux arguments que j'ai trouver.
J'ai aussi entendu parler du fait que le cast peut cacher une erreur au compilateur. Notament si on inclut pas stdlib.h ou malloc.h. Mais c'est assez flou. Certains considère qu'ici la véritable erreur c'est l'oublie de l'inclusion de ses fichiers et donc qu'on devrais caster malgré tous si on le désire, d'autres sont contre.
Ce qui est sur c'est que cette question a des partisans des 2 côtés. Et je pense que on ne peut reprocher catégoriquement à quelqu'un d'utiliser l'une ou l'autre méthode.
Personnellement je compile toujours autant que possible en version c11.
- Edité par peridot69 11 août 2017 à 12:39:20
printf("Les rudiments de la programmation ? Nan mais Hello quoi !");
Oui en fait j'ai compris. Comme je travaille sur visual studio je suis en C++. J'avais oublié ce détail :).
J'ai forcé la compilation en C sur visual et effectivement pas besoin de cast.
Par la même occasion si y en a qui savent comment connaître la version du compilateur C que j'utilise avec visual merci d'avance (il me semble que c'est C90 ???).
- Edité par Sylabio 15 août 2017 à 10:39:29
Plus j'apprends et plus je me rends compte que je ne sais rien.
Par la même occasion si y en a qui savent comment connaître la version du compilateur C que j'utilise avec visual merci d'avance (il me semble que c'est C90 ???).
La version du compilateur et la version du standard n'ont rien à voir.
Sinon pour savoir avec quelle version du standard compile par défaut ton compilateur, il faut lire sa doc. ( Bien sûr dans la version du compilateur utilisé. )
Par exemple gcc 7.x compile par défaut avec le standard gnu11.
Par la même occasion si y en a qui savent comment connaître la version du compilateur C que j'utilise avec visual merci d'avance (il me semble que c'est C90 ???).
La version du compilateur et la version du standard n'ont rien à voir.
Sinon pour savoir avec quelle version du standard compile par défaut ton compilateur, il faut lire sa doc. ( Bien sûr dans la version du compilateur utilisé. )
Par exemple gcc 7.x compile par défaut avec le standard gnu11.
- Edité par GnuAs__ il y a environ 1 heure
Oui effectivement. C'était du standard dont je voulais parler et non du compilateur. Bon du coup j'ai cherché une doc pour visual studio 2015 visual C++ mais j'ai pas trouvé de doc pdf, c'est bizarre.
En tout cas sur le site MSDN (que je déteste car je trouve qu'il est mal fait) on trouve pas grand chose sur le standard C utilisé puisque l'on trouve surtout des infos pour le C++.
"Conformité à la bibliothèque C99 Visual Studio 2015 Implémentation totale de la bibliothèque standard C99, à l'exception de toutes les fonctionnalités de la bibliothèque qui dépendent de fonctionnalités du compilateur non encore prises en charge par le compilateur Visual C++ (par exemple, <tgmath.h> n'est pas implémentée)."
Bon il semblerait que du coup le compilateur de visual n'est pas conforme au standard C11.
Je fais ma petite pub au passage en conseillant de vous procurer visual studio community (100% gratuit) qui reste quoi que l'on puisse dire un très bon IDE, le meilleur pour ce qui est du debug je pense (l'interface du logiciel est quand même mieux que Code:Blocks ).
Plus j'apprends et plus je me rends compte que je ne sais rien.
Personnellement je préfére mes outils séparés, éditeur, compilateur, débogueurs tous ça en ligne de commande. Les IDE's je trouve qu'ils font trop usine à gaz.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Plus j'apprends et plus je me rends compte que je ne sais rien.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Plus j'apprends et plus je me rends compte que je ne sais rien.
Plus j'apprends et plus je me rends compte que je ne sais rien.