π
<-
Chat plein-écran
[^]

Bug incompréhensible dans un programme C compilé en .EXE

Assembleur, Axe, C/C++, ICE...

Bug incompréhensible dans un programme C compilé en .EXE

Unread postby Bobb » 25 May 2021, 10:47

Bonjour à tous,
Je programme en ce moment un peu en C et j'ai depuis quelques temps un problème que je voudrais vous présenter :
Voici Herbert (non je rigole)

Vous pouvez tester, j'ai mis le code source et le programme compilé en pièces jointes.

Tout d'abord, je suis sur Windows 7 et mon compilateur est MinGW.

Mais quel est ce bug ?

En fait, lorsque je programme sur un IDE en ligne (replit), tout se passe normalement quand j'exécute le programme (vous pouvez tester ici).
Alors que quand j'essaie de compiler exactement le même code source avec MinGW pour windows, le programme ne fait pas la même chose lors de l'exécution. (vous pouvez voir sur la capture d'écran la différence).

Image

Je précise encore une chose : Sur windows 10, le bug n'arrive presque jamais.

Quelqu'un pourrait-il m'expliquer d'où vient ce bug et/ou comment le résoudre ?

Merci,
Bobb
Attachments
fichiers-source et compilés.zip
(23.32 KiB) Downloaded 65 times

Tous mes programmes sont disponibles ici

↳ Testez mon simulateur Android sur Ti-83 Premium CE et / ou Édition Python
Jetez un coup d'oeil à mon langage de programmation interprété Neon.

Image
User avatar
BobbProgrammeur
Niveau 10: GR (Guide de Référence)
Niveau 10: GR (Guide de Référence)
Level up: 94.5%
 
Posts: 300
Joined: 19 Apr 2020, 12:37
Location: Morbihan
Gender: Male
Calculator(s):
MyCalcs profile
Class: CPGE MPI

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby SlyVTT » 25 May 2021, 12:29

Salut Bobb,

j'ai juste survolé ton code, et pour être franc, sans que tu le prennes mal bien sûr, celui-ci me semble éminemment complexe compte tenu de ce que tu cherches à faire.

Je note plusieurs points qui de mon humble point de vue posent problème.

1) il y a de multiples appels à des fonctions "malloc" pour allouer dynamiquement de la mémoire aux chaines de caractères que tu manipules, mais je n'ai vu (sauf erreur) aucune libération de mémoire via des appels correspondants à "free". En gros, cela signifie fuites de mémoires à chaque appel. Je te rappelle que le C n'a pas de garbage collector, donc ce qui est alloué à un moment et non restitué est perdu jusqu'au prochain reboot (ou plantage).
Par exemple, ta fonction input( )
Code: Select all
char* input(char *text) {// fonction pour demander le calcul
  char *var=malloc(100*sizeof(char));
  printf("%s",text);
  scanf("%s",var);
  char* newVar = malloc(sizeof(char)*strlen(var));
  strcpy(newVar,var);
  return newVar;
}

à chaque utilisation de la fonction, la variable var se voit allouer 100*char qui ne sont jamais libérés.
De meme ta fonction retourne newVar dont la mémoire a été allouée, mais qu'il te faudra à un moment ou à un autre libérer.

2) je note aussi des trucs bizarres, dont par exemple cette ligne :
Code: Select all
char** operateur=malloc(sizeof(char*)*6);
qui ne me semble vraiment pas très à propos (nous sommes à deux niveaux de pointeurs). Tout cela par la suite pour "remplir" la chaine par
Code: Select all
operateur[0]="**";
D'une manière générale, un niveau de pointeur est suffisant pour le commun des mortels et au-delà, il faut passer en mode alerte, c'est qu'il y a un truc à simplifier. (je te passe le fait que assigner de force une chaine de 2 caractères dans un char me fait tousser ;-) et que en plus operateur[0] ne correspond pas à ce que tu penses que cela correspond, en fait operateur[0] correspond à un (char*), pas à un (char)). Bref : simplifie et vérifie.

3) évite les imbrications de fonctions qui rendent le code illisible et intraçable : par exemple
Code: Select all
calcul=calc(input("Votre calcul : "));
serait nettement plus maintenable en décomposant les actions : on fait la lecture de l'entrée utilisateur, on stocke (comme ça si besoin on peut vérifier que c'est Ok avec un débugger), et ensuite on fait des opérations sur cette entrée utilisateur.

Donc pour répondre à ta question et sans trop m'avancer, ton bug est typiquement lié à un problème de gestion/mauvaise utilisation mémoire. Comme tu travailles avec des chaines, selon la tolérance du système/compilateur, ca passe ou ca ne passe pas. Mais à mon sens ton code est beaucoup trop complexe.

Je ne peux que te conseiller de le reprendre complètement en le décomposant et en vérifiant avec un débugger si c'est Ok ou pas via des watches/breakpoints. Code::Blocks avec MinGW devrait t'être d'une grande aide.

A plus

Sly
Last edited by SlyVTT on 25 May 2021, 19:16, edited 1 time in total.
Some works in progress :
The GUI Toolkit NF for nSpireMyShmup for fxCG-50Magic Light for Casio Graph 90+E
and
Magic Light for nSpire CX/CX-II
Simple Text Editor for nSpireOutRun for Casio Graph 90+E
95%
50%
100%
75%
100%
And more to come ... stay tuned
User avatar
SlyVTTPremium
Niveau 12: CP (Calculatrice sur Pattes)
Niveau 12: CP (Calculatrice sur Pattes)
Level up: 43.1%
 
Posts: 484
Images: 31
Joined: 19 Jan 2021, 09:41
Location: France
Gender: Male
Calculator(s):
MyCalcs profile
GitHub: SlyVTT

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby SlyVTT » 25 May 2021, 12:42

Bobb,

j'oubliais le plus important, le C est vraiment complexe et demande beaucoup de rigueur, il ne faut surtout pas de décourager, il est normal de galérer au début.

Un autre petit conseil, mais peut être fais tu déjà : fait tes algo sur papier avant pour bien réfléchir a comment ton code doit se décomposer.

Dans ton cas :

1) lire l'opération demandée par l'utilisateur et stocker dans "Entrée"
2) décomposer "Entrée" pour trouver l'operation demandée (opérateur) et les opérandes ("nombre1" et "nombre2")
3) pour trouver l'opérateur : scanner "entrée" pour chercher les symboles + - * / ** ...
4) pour trouver les opérandes : scanner "entrée" pour chercher les chiffres et eventuellement le signe - (attention aux cas particuliers par exemple si nombre 2 est négatif ...)
5) convertir les chaines "nombre1" et "nombre2" en valeurs numérique correspondantes
6) conduire l'operation demandée en focntion de l'opérateur trouvé
7) afficher le résultat

Bon, j'ai fait rapidos, c'est peut être à raffiner un peu, mais c'est le principe et ensuite il faut habiller chacune des fonctions

Ciao

Sly
Some works in progress :
The GUI Toolkit NF for nSpireMyShmup for fxCG-50Magic Light for Casio Graph 90+E
and
Magic Light for nSpire CX/CX-II
Simple Text Editor for nSpireOutRun for Casio Graph 90+E
95%
50%
100%
75%
100%
And more to come ... stay tuned
User avatar
SlyVTTPremium
Niveau 12: CP (Calculatrice sur Pattes)
Niveau 12: CP (Calculatrice sur Pattes)
Level up: 43.1%
 
Posts: 484
Images: 31
Joined: 19 Jan 2021, 09:41
Location: France
Gender: Male
Calculator(s):
MyCalcs profile
GitHub: SlyVTT

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby Bobb » 25 May 2021, 13:24

Merci pour toutes ces précisions éclairantes, je comprends bien que mon code est "sale".
Je dois le nettoyer et régler TOUS mes bugs avant d'aller plus loin.
Reprendre mon code de zéro pourrait être aussi une idée. Et là je pourrais le faire proprement avec découpage en lexèmes, arbre syntaxique etc.

Sinon pour le tableau de pointeurs de char*, c'est parce que la fonction qui prend ça en paramètres peut prendre plusieurs opérateurs qui sont composés de plusieurs caractères.

Par rapport au fait que j'utilise plein de mémoire et que je ne la libère pas, j'avais une question.
Normalement, tous les pointeurs créés dans une fonction se suppriment à la fin de la fonction. Donc je ne suis pas obligé d'utiliser free juste avant la fin d'une fonction ?

Bobb

Tous mes programmes sont disponibles ici

↳ Testez mon simulateur Android sur Ti-83 Premium CE et / ou Édition Python
Jetez un coup d'oeil à mon langage de programmation interprété Neon.

Image
User avatar
BobbProgrammeur
Niveau 10: GR (Guide de Référence)
Niveau 10: GR (Guide de Référence)
Level up: 94.5%
 
Posts: 300
Joined: 19 Apr 2020, 12:37
Location: Morbihan
Gender: Male
Calculator(s):
MyCalcs profile
Class: CPGE MPI

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby SlyVTT » 25 May 2021, 14:59

Salut Bobb,

pour le char**, ok, j'ai survolé, mais je te confirme que dans ce cas, si tu veux avoir un code propre, il faut dans l'ordre :
1/ dire que ton tableau fait X entrée (le nombre d'opérateur à considérer (par exemple si tu as 6 opérateurs alors malloc(size_of(char*)*6);
2/ à ce stade, tu as réservé 6 emplacements pour des (char*) que tu sais pointer avec *operateur[0] à [5]
3/ désormais, il faut pour chacune de 6 chaines, allouer la mémoire pour le nombre de caractères via un malloc(size_of(char) * nbcaractere ); sinon tu inscris dans une mémoire non allouée et c'est le risque de corruption mémoire associée et plantage.

Ensuite pour entrer l'opérateur, soit tu as un seul caractère, par exemple '+' et là tu peux faire un operateur[0][0]='+'; par exemple pour le premier opérateur, ou operateur[2][0]='-' pour le 3eme operateur (car il s'agit bien d'un double tableau de char).
Si c'est un operateur avec plusieurs caractères, par exemple "**" ou bien par exemple plus tard "sin" ou "cos", alors il faut passer par un strcpy(operateur[0], "**" );

Pour la liberation de la mémoire, la portée des variable telle que tu décris est vraie seulement pour les variables statiques. Pour les variables dynamiques avec allocation via un pointeur, la mémoire allouée via un malloc reste allouée=occupée, par contre le pointeur lui est détruit, tu te retrouves donc avec impossibilité de dés-allouer cette mémoire, d'où une fuite. C'est un peu l'image d'un ballon de baudruche. Le pointeur correspond à la ficelle que tu tiens dans la main. Le Malloc sert à gonfler le ballon pour le remplir et free sert à le dégonfler pour récupérer l'air. Si tu coupes la ficelle, le ballon s'envole et tu ne peux plus récupérer l'air, donc il est perdu. Donc oui la portée du pointeur est la fonction, par contre c'est faux pour la mémoire allouée et c'est pour cela qu'il faut toujours bien garder une référence (=un pointeur) sur la mémoire allouée pour pouvoir la libérer ensuite.

J'espère avec été clair dans mes explications.

A plus

Sylvain
Last edited by SlyVTT on 25 May 2021, 16:02, edited 1 time in total.
Some works in progress :
The GUI Toolkit NF for nSpireMyShmup for fxCG-50Magic Light for Casio Graph 90+E
and
Magic Light for nSpire CX/CX-II
Simple Text Editor for nSpireOutRun for Casio Graph 90+E
95%
50%
100%
75%
100%
And more to come ... stay tuned
User avatar
SlyVTTPremium
Niveau 12: CP (Calculatrice sur Pattes)
Niveau 12: CP (Calculatrice sur Pattes)
Level up: 43.1%
 
Posts: 484
Images: 31
Joined: 19 Jan 2021, 09:41
Location: France
Gender: Male
Calculator(s):
MyCalcs profile
GitHub: SlyVTT

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby Bobb » 25 May 2021, 15:21

Merci, je comprends mieux les raisons possibles de mes bugs, et je corrigerai tout ça.

Tous mes programmes sont disponibles ici

↳ Testez mon simulateur Android sur Ti-83 Premium CE et / ou Édition Python
Jetez un coup d'oeil à mon langage de programmation interprété Neon.

Image
User avatar
BobbProgrammeur
Niveau 10: GR (Guide de Référence)
Niveau 10: GR (Guide de Référence)
Level up: 94.5%
 
Posts: 300
Joined: 19 Apr 2020, 12:37
Location: Morbihan
Gender: Male
Calculator(s):
MyCalcs profile
Class: CPGE MPI

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby SlyVTT » 25 May 2021, 16:06

Juste pour te confirmer que c'est un truc pas forcément évident, dans le GUI Toolkit, j'ai eu récemment des problèmes de perte de pointeurs et donc pour libérer la mémoire, j'ai donc du récrire tout un pan de mon code (quelques milliers de ligne pour le passer en vrai C++ et virer mes (char*) pour les remplacer par des std::string). L'avantage est que ces objets sont nettement plus évolués et contiennent un destructeur qui simplifie les choses.

Au moins je ne referai plus cette bétise. C'est en forgeant qu'on devient forgeron comme dit l'adage ...

Donc tu vois, même quand on à l'habitude, parfois on fait des âneries et on galère ;-)

A plus

Sly
Some works in progress :
The GUI Toolkit NF for nSpireMyShmup for fxCG-50Magic Light for Casio Graph 90+E
and
Magic Light for nSpire CX/CX-II
Simple Text Editor for nSpireOutRun for Casio Graph 90+E
95%
50%
100%
75%
100%
And more to come ... stay tuned
User avatar
SlyVTTPremium
Niveau 12: CP (Calculatrice sur Pattes)
Niveau 12: CP (Calculatrice sur Pattes)
Level up: 43.1%
 
Posts: 484
Images: 31
Joined: 19 Jan 2021, 09:41
Location: France
Gender: Male
Calculator(s):
MyCalcs profile
GitHub: SlyVTT

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby Lionel Debroux » 25 May 2021, 19:04

Suggestions basées sur l'extrait posté dans le premier post de SlyVTT:
* la première étape est de mettre une longueur dans la chaîne de formattage des fonctions de la famille scanf, c'est à dire utiliser "%100s" avec une chaîne de 101 caractères, sinon des buffer overflows sont possibles - c'est une vulnérabilité;
* la seconde étape est d'utiliser une forme ou une autre de constante, de préférence non matérialisée (#define va bien) pour la taille du buffer temporaire, et d'utiliser l'astuce de stringification basée sur le préprocesseur, utilisant une paire de macros dont la macro supérieure qu'on appelle est souvent nommée "xstr", permettant d'écrire une construction de la forme
Code: Select all
scanf("%" xstr(MA_JOLIE_LONGUEUR_TABLEAU_TEMPORAIRE) "%s", var);
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
User avatar
Lionel DebrouxSuper Modo
Niveau 14: CI (Calculateur de l'Infini)
Niveau 14: CI (Calculateur de l'Infini)
Level up: 11.2%
 
Posts: 6859
Joined: 23 Dec 2009, 00:00
Location: France
Gender: Male
Calculator(s):
MyCalcs profile
Class: -
GitHub: debrouxl

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby Bobb » 02 Jun 2021, 08:01

Je te remercie de ta réponse, j'essaierai de la comprendre et je l'utiliserais.

J'ai commencé à mettre des free() pour vider mes pointeurs, et je me posais une question. Dans ma fonction, j'alloue un pointeur avec malloc, et je le retourne. Comment puis-je faire pour vider le pointeur après l'avoir retourné ?

Tous mes programmes sont disponibles ici

↳ Testez mon simulateur Android sur Ti-83 Premium CE et / ou Édition Python
Jetez un coup d'oeil à mon langage de programmation interprété Neon.

Image
User avatar
BobbProgrammeur
Niveau 10: GR (Guide de Référence)
Niveau 10: GR (Guide de Référence)
Level up: 94.5%
 
Posts: 300
Joined: 19 Apr 2020, 12:37
Location: Morbihan
Gender: Male
Calculator(s):
MyCalcs profile
Class: CPGE MPI

Re: Bug incompréhensible dans un programme C compilé en .EXE

Unread postby Lionel Debroux » 02 Jun 2021, 08:20

Pour vider le pointeur, il faut le mettre à nullptr.
Pour vider la mémoire pointée, memset() avant free(). Sur gros ordinateur, il y a des allocateurs mémoire qui font ce memset en interne dans les fonctions type malloc() et les fonctions type free() si on le leur demande, par exemple celui de la glibc quand la variable d'environnement MALLOC_PERTURB_ est définie, ou sous MSVC, en mode debug.

Code: Select all
void * truc = malloc(SIZE);
return truc;

// nullptr est C++11 et ultérieurs; en C et en C++ vieux style, c'est NULL.
// A mon sens, mieux vaut arrêter d'utiliser du C pour du nouveau code. Même si on n'utilise pas les nombreuses et très importantes features C++ dont le C ne bénéficiera jamais, rien que le typage plus fort est un avantage pour réduire les bêtises qu'on peut écrire.
if (nullptr != truc) {
    // faire quelque chose avec truc, et quand on a fini...
    free(truc); truc = nullptr; // Toujours faire les deux, sauf si on réutilise la variable plus loin pour pointer vers autre chose.
}
else {
    // On arrête car on n'a pas pu allouer la mémoire.
}


La paire
Code: Select all
free(truc); truc = nullptr;
peut être wrappée sous forme de fonction inline globale à mettre dans un header commun, du genre
Code: Select all
inline void myfree(void ** ptr) {
    free(*ptr);
    *ptr = nullptr;
}

// Utilisation: myfree(&truc);

A toi de voir si c'est plus clair ainsi.
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
User avatar
Lionel DebrouxSuper Modo
Niveau 14: CI (Calculateur de l'Infini)
Niveau 14: CI (Calculateur de l'Infini)
Level up: 11.2%
 
Posts: 6859
Joined: 23 Dec 2009, 00:00
Location: France
Gender: Male
Calculator(s):
MyCalcs profile
Class: -
GitHub: debrouxl

Next

Return to Langages alternatifs

Who is online

Users browsing this forum: No registered users and 5 guests

-
Search
-
Social TI-Planet
-
Featured topics
Comparaisons des meilleurs prix pour acheter sa calculatrice !
Aidez la communauté à documenter les révisions matérielles en listant vos calculatrices graphiques !
Phi NumWorks jailbreak
123
-
Donations / Premium
For more contests, prizes, reviews, helping us pay the server and domains...
Donate
Discover the the advantages of a donor account !
JoinRejoignez the donors and/or premium!les donateurs et/ou premium !


Partner and ad
Notre partenaire Jarrety Calculatrices à acheter chez Calcuso
-
Stats.
855 utilisateurs:
>811 invités
>39 membres
>5 robots
Record simultané (sur 6 mois):
6892 utilisateurs (le 07/06/2017)
-
Other interesting websites
Texas Instruments Education
Global | France
 (English / Français)
Banque de programmes TI
ticalc.org
 (English)
La communauté TI-82
tout82.free.fr
 (Français)