Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1091 connectés 

  FORUM HardWare.fr
  Programmation
  C++

  prob C étrange : programme qui marche et personne ne comprend pourquoi

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

prob C étrange : programme qui marche et personne ne comprend pourquoi

n°215330
zluman
Posté le 16-09-2002 à 22:44:26  profilanswer
 
mood
Publicité
Posté le 16-09-2002 à 22:44:26  profilanswer
 

n°215331
antp
Super Administrateur
Champion des excuses bidons
Posté le 16-09-2002 à 22:46:42  profilanswer
 

Tu pourrais au moins retaper le sujet ici :sarcastic:


---------------
mes programmes ·· les voitures dans les films ·· apprenez à écrire
n°215332
zluman
Posté le 16-09-2002 à 22:47:53  profilanswer
 

bonsoir tout le monde,  
 
voilà j'ai un petit problème avec le simple code suivant. Le programme marche parfaitement bien mais beaucoup de dév trouvent le code incorrect et ne comprennent pourquoi ça marche ! un avis ? le code est-il bon ? sinon que faudrait-il modifier pour le "purifier" ?  
 
merci :  
 
 
mention.h :  
 
 
 
# include <stdio.h>  
 
void laMention(void) ;  
 
float demanderNote(char commentaire[]) ;  
 
char* definirMention(float note) ;  
 
void afficherResultat(float note, char mention[]) ;  
 
 
 

Code :
  1. -----------------------------------------------------------
  2. mention.c :
  3. # include "mention.h"
  4. void laMention(void)
  5. {
  6. float note ;
  7. char *mention ;
  8. note = demanderNote("Entrez la note" ) ;
  9. if (note < 0 || note > 20)
  10. fprintf(stdout, "Erreur :\n\nNote invalide, merci d'entrer une note comprise dans l'intervalle [0, 20]." ) ;
  11. else
  12. {
  13. mention = definirMention(note) ;
  14. afficherResultat(note, mention) ;
  15. }
  16. }
  17. float demanderNote(char commentaire[])
  18. {
  19. float noteEntree ;
  20. fprintf(stdout, "%s : ", commentaire) ;
  21. fscanf(stdin, "%f", ¬eEntree) ;
  22. return noteEntree ;
  23. }
  24. char* definirMention(float note)
  25. {
  26. char *mentionCorrespondante;
  27. if (note >= 16)
  28. mentionCorrespondante = "Tres bien" ;
  29. else if (note >= 14)
  30. mentionCorrespondante = "Bien" ;
  31. else if (note >= 12)
  32. mentionCorrespondante = "Assez-bien" ;
  33. else if (note >= 10)
  34. mentionCorrespondante = "Passable" ;
  35. else
  36. mentionCorrespondante = "Echec" ;
  37. return mentionCorrespondante ;
  38. }
  39. void afficherResultat(float note, char mention[])
  40. {
  41. fprintf(stdout, "note : %5.2f\nmention : %s", note, mention);
  42. }
  43. --------------------------------------------------------------------------
  44. mentionTest.c :
  45. # include "mention.c"
  46. void main(void)
  47. {
  48. laMention() ;
  49. }


Message édité par zluman le 17-09-2002 à 13:41:48
n°215336
joce
Architecte / Développeur principal
&#034;BugHunter&#034;
Posté le 16-09-2002 à 22:51:22  profilanswer
 

je vois pas de blème


---------------
Protèges carnets personnalisés & accessoires pour bébé
n°215340
zluman
Posté le 16-09-2002 à 22:52:36  profilanswer
 

des dév me disent que ça coince au niveau des pointeurs

n°215341
joce
Architecte / Développeur principal
&#034;BugHunter&#034;
Posté le 16-09-2002 à 22:54:49  profilanswer
 

ba non
à la limite tu pourrais déclarer en const char*


---------------
Protèges carnets personnalisés & accessoires pour bébé
n°215344
zluman
Posté le 16-09-2002 à 22:58:42  profilanswer
 

ah ok ben cool alors si le code est correct :)
 
qques propos de dév :
 
<< erreur dans cette fonction :)  
char* definirMention(float note)
 
quoi que ça doit passer vue que c'est un char* mais j'ai un doute
car tu déclares dans ta fonction et donc à la sortie de la fonction la zone de pile est vidée
mais bon un char * c'est comme un int donc il doit renvoyer son adresse et du coup ça marche  
par contre si c'etait un char[2] par exemple, ça marcherait plus
>>
 
 
 
<< un char* c'est un int qui pointe vers une zone mémoire  
comme tu ne l'as pas initialisé ça pointe n'importe où
donc ton programme tombe en marche
un char * = une chaine statique  
ça marche pas c'est clair >>
 
 
et pourtant ...

n°215373
joce
Architecte / Développeur principal
&#034;BugHunter&#034;
Posté le 17-09-2002 à 00:35:08  profilanswer
 

pour moi ca renvoie l'adresse mémoire contenant la chaine de caractère donc c'est correct (l'espace mémoire necessaire pour les chaines étant alloué à la compil)


Message édité par joce le 17-09-2002 à 00:36:10

---------------
Protèges carnets personnalisés & accessoires pour bébé
n°215419
darkoli
Le Petit Dinosaure Bleu
Posté le 17-09-2002 à 09:05:29  profilanswer
 

zluman a écrit a écrit :

 
[...]
char* definirMention(float note)  
{  
 
char *mentionCorrespondante;  
 
if (note >= 16)  
mentionCorrespondante = "Tres bien" ;  
else if (note >= 14)  
mentionCorrespondante = "Bien" ;  
else if (note >= 12)  
mentionCorrespondante = "Assez-bien" ;  
else if (note >= 10)  
mentionCorrespondante = "Passable" ;  
else  
mentionCorrespondante = "Echec" ;  
 
return mentionCorrespondante ;  
 
}  
[...]




 
Ben y'a pas de truc louche ...
Par contre je trouve que la presentation du code est bof ...

Code :
  1. -----------------------------------------------------------
  2. mention.c :
  3. # include "mention.h"
  4. void laMention(void)
  5. {
  6. float  note=0.0;
  7. char*  mention=NULL;
  8. note = demanderNote("Entrez la note" );
  9. if ( (note < 0) || (note > 20) )
  10.   {
  11.    fprintf(stdout, "Erreur :\n\nNote invalide, merci d'entrer une note comprise dans l'intervalle [0, 20]." );
  12.   }
  13. else
  14.   {
  15.    mention = definirMention(note);
  16.    afficherResultat(note, mention);
  17.   }
  18. }
  19. float demanderNote(char commentaire[])
  20. {
  21. float noteEntree=0.0;
  22. fprintf(stdout, "%s : ", commentaire);
  23. fscanf(stdin, "%f", ¬eEntree);
  24. return noteEntree;
  25. }
  26. char* definirMention(float note)
  27. {
  28. if (note >= 16) return "Tres bien";
  29. if (note >= 14) return "Bien";
  30. if (note >= 12) return "Assez-bien";
  31. if (note >= 10) return "Passable";
  32. return "Echec";
  33. }
  34. void afficherResultat(float note, char mention[])
  35. {
  36. fprintf(stdout, "note : %5.2f\nmention : %s", note, mention);
  37. }
  38. --------------------------------------------------------------------------
  39. mentionTest.c :
  40. # include "mention.c"
  41. void main(void)
  42. {
  43. laMention();
  44. }


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
n°215420
darkoli
Le Petit Dinosaure Bleu
Posté le 17-09-2002 à 09:07:17  profilanswer
 

joce a écrit a écrit :

pour moi ca renvoie l'adresse mémoire contenant la chaine de caractère donc c'est correct (l'espace mémoire necessaire pour les chaines étant alloué à la compil)




+1 (Comme Joce il a dit ...)


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
mood
Publicité
Posté le 17-09-2002 à 09:07:17  profilanswer
 

n°215435
kenshiro18​2
Posté le 17-09-2002 à 09:39:16  profilanswer
 

Peut-etre que tes devs pensaient que les chaines "Tres bien" et compagnie etaient des variables locales.
Hors ce sont grosso-modo des variables globales, donc ca marche

n°215440
bb138
La vie est belle ...
Posté le 17-09-2002 à 10:00:30  profilanswer
 

Justement sur cette question, pourrais-je avoir plus de détails ?
Personellement j'aurais dit que cela aurait pu poser un problème...

n°215450
BifaceMcLe​OD
The HighGlandeur
Posté le 17-09-2002 à 10:32:57  profilanswer
 

Le seul problème que je vois à ce code, outre sa présentation déplorable, bien sûr -- mais je ne vais pas m'apesentir là-dessus, darkoli en a déjà parlé -- ce sont les affectations de chaînes de caractères :

Code :
  1. char *mentionCorrespondante; 
  2. mentionCorrespondante = "Tres bien" ;


En C strict, cette ligne ne fonctionne pas. Cependant, elle fonctionne correctement avec la plupart des compilateurs C++ actuels. Mais sache que ce n'est pas 100 % portable.
 
Qu'est-ce qui cloche ici ?
En théorie, en exécutant cette ligne, le compilateur va créer une chaîne de caractères temporaire pour stocker la chaîne constante "Tres bien", puis, il va faire pointer mentionCorrespondante vers le début de la zone mémoire qui contient cette chaîne de caractères. Enfin, la chaîne temporaire étant, justement temporaire, le compilateur pourra désallouer la mémoire qui a été réservée pour cette chaîne temporaire, et mentionCorrespondante va se retrouver à pointer vers une zone mémoire non allouée.
 
Comment résoudre le problème ? Faire toujours appel à strcpy() pour copier une chaine constante dans une variable de type chaine de caractères. Ce qui signifie également qu'il faut préalablement et explicitement allouer de la mémoire pour mentionCorrespondante avec un malloc().
Note : quand le code que tu as donné marche, c'est en fait le compilateur a transformé l'affectation en un strcpy() en douce.
 
Autre solution, pour ton cas précis, qui est la conséquence d'une règle totalement différente : éviter les chaînes de caractères "codées en dur" disséminées dans le code. Il est grandement préférable de les rassembler à un endroit précis du code. Par exemple, en définissant une table des chaînes constantes plus ou moins globales (cela peut-être global à un module plutôt qu'au programme entier). Ainsi, on aurait, dans le fichier mention.c :

Code :
  1. // Pour la lisibilité.
  2. #define  ARRAY_LENGTH(array)  (sizeof(array) / sizeof((array)[0]))
  3. // Ainsi les chaînes de caractères constantes sont allouées une
  4. // fois pour toutes par le compilateur, dès le début du  
  5. // programme, et elles existent jusqu'à sa fin.
  6. const char* mentions[]       = { "Echec", "Passable", "Assez bien", "Bien", "Tres bien" };
  7. int         seuil_mentions[] = { 0, 10, 12, 14, 16 };
  8. // Noter le nom de fonction plus approprié
  9. const char* donneMention(float note)
  10. {
  11.   int  index;
  12.   for (index = ARRAY_LENGTH(mentions) - 1; index >= 0; index--) {
  13.     if (note >= seuil_mentions[index]) {
  14.       return mentions[index];
  15.     }
  16.   }
  17.   return mentions[0];
  18. }


Message édité par BifaceMcLeOD le 17-09-2002 à 11:59:27
n°215610
joce
Architecte / Développeur principal
&#034;BugHunter&#034;
Posté le 17-09-2002 à 13:41:30  profilanswer
 

DarkOli a écrit a écrit :

 
 
Ben y'a pas de truc louche ...
Par contre je trouve que la presentation du code est bof ...

Code :
  1. -----------------------------------------------------------
  2. mention.c :
  3. # include "mention.h"
  4. void laMention(void)
  5. {
  6. float  note=0.0;
  7. char*  mention=NULL;
  8. note = demanderNote("Entrez la note" );
  9. if ( (note < 0) || (note > 20) )
  10.   {
  11.    fprintf(stdout, "Erreur :\n\nNote invalide, merci d'entrer une note comprise dans l'intervalle [0, 20]." );
  12.   }
  13. else
  14.   {
  15.    mention = definirMention(note);
  16.    afficherResultat(note, mention);
  17.   }
  18. }
  19. float demanderNote(char commentaire[])
  20. {
  21. float noteEntree=0.0;
  22. fprintf(stdout, "%s : ", commentaire);
  23. fscanf(stdin, "%f", ¬eEntree);
  24. return noteEntree;
  25. }
  26. char* definirMention(float note)
  27. {
  28. if (note >= 16) return "Tres bien";
  29. if (note >= 14) return "Bien";
  30. if (note >= 12) return "Assez-bien";
  31. if (note >= 10) return "Passable";
  32. return "Echec";
  33. }
  34. void afficherResultat(float note, char mention[])
  35. {
  36. fprintf(stdout, "note : %5.2f\nmention : %s", note, mention);
  37. }
  38. --------------------------------------------------------------------------
  39. mentionTest.c :
  40. # include "mention.c"
  41. void main(void)
  42. {
  43. laMention();
  44. }






T'avais pas besoin de te faire chier à faire la tabulation la balise cpp la fait toute seule


---------------
Protèges carnets personnalisés & accessoires pour bébé
n°215612
joce
Architecte / Développeur principal
&#034;BugHunter&#034;
Posté le 17-09-2002 à 13:45:07  profilanswer
 

BifaceMcLeOD a écrit a écrit :

Le seul problème que je vois à ce code, outre sa présentation déplorable, bien sûr -- mais je ne vais pas m'apesentir là-dessus, darkoli en a déjà parlé -- ce sont les affectations de chaînes de caractères :

Code :
  1. char *mentionCorrespondante; 
  2. mentionCorrespondante = "Tres bien" ;


En C strict, cette ligne ne fonctionne pas. Cependant, elle fonctionne correctement avec la plupart des compilateurs C++ actuels. Mais sache que ce n'est pas 100 % portable.
 
Qu'est-ce qui cloche ici ?
En théorie, en exécutant cette ligne, le compilateur va créer une chaîne de caractères temporaire pour stocker la chaîne constante "Tres bien", puis, il va faire pointer mentionCorrespondante vers le début de la zone mémoire qui contient cette chaîne de caractères. Enfin, la chaîne temporaire étant, justement temporaire, le compilateur pourra désallouer la mémoire qui a été réservée pour cette chaîne temporaire, et mentionCorrespondante va se retrouver à pointer vers une zone mémoire non allouée.
 
Comment résoudre le problème ? Faire toujours appel à strcpy() pour copier une chaine constante dans une variable de type chaine de caractères. Ce qui signifie également qu'il faut préalablement et explicitement allouer de la mémoire pour mentionCorrespondante avec un malloc().
Note : quand le code que tu as donné marche, c'est en fait le compilateur a transformé l'affectation en un strcpy() en douce.
 
Autre solution, pour ton cas précis, qui est la conséquence d'une règle totalement différente : éviter les chaînes de caractères "codées en dur" disséminées dans le code. Il est grandement préférable de les rassembler à un endroit précis du code. Par exemple, en définissant une table des chaînes constantes plus ou moins globales (cela peut-être global à un module plutôt qu'au programme entier). Ainsi, on aurait, dans le fichier mention.c :

Code :
  1. // Pour la lisibilité.
  2. #define  ARRAY_LENGTH(array)  (sizeof(array) / sizeof((array)[0]))
  3. // Ainsi les chaînes de caractères constantes sont allouées une
  4. // fois pour toutes par le compilateur, dès le début du  
  5. // programme, et elles existent jusqu'à sa fin.
  6. const char* mentions[]       = { "Echec", "Passable", "Assez bien", "Bien", "Tres bien" };
  7. int         seuil_mentions[] = { 0, 10, 12, 14, 16 };
  8. // Noter le nom de fonction plus approprié
  9. const char* donneMention(float note)
  10. {
  11.   int  index;
  12.   for (index = ARRAY_LENGTH(mentions) - 1; index >= 0; index--) {
  13.     if (note >= seuil_mentions[index]) {
  14.       return mentions[index];
  15.     }
  16.   }
  17.   return mentions[0];
  18. }






tout à fait d'accord :jap:


---------------
Protèges carnets personnalisés & accessoires pour bébé
n°215613
bb138
La vie est belle ...
Posté le 17-09-2002 à 13:46:21  profilanswer
 

Là OK, je comprend mieux ! ;)

n°215645
darkoli
Le Petit Dinosaure Bleu
Posté le 17-09-2002 à 14:27:48  profilanswer
 

joce a écrit a écrit :

 
T'avais pas besoin de te faire chier à faire la tabulation la balise cpp la fait toute seule




D'ailleurs quand la tabulation est faite, la balise CPP merde !!! (Et comme en général mon code est indenté comme je le poste je ne vais pas le dé-indenter avant !!!).


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
n°215653
darkoli
Le Petit Dinosaure Bleu
Posté le 17-09-2002 à 14:30:43  profilanswer
 

BifaceMcLeOD a écrit a écrit :

Le seul problème que je vois à ce code, outre sa présentation déplorable, bien sûr -- mais je ne vais pas m'apesentir là-dessus, darkoli en a déjà parlé -- ce sont les affectations de chaînes de caractères :

Code :
  1. char *mentionCorrespondante; 
  2. mentionCorrespondante = "Tres bien" ;


En C strict, cette ligne ne fonctionne pas. Cependant, elle fonctionne correctement avec la plupart des compilateurs C++ actuels. Mais sache que ce n'est pas 100 % portable.
 
[...]
 
Comment résoudre le problème ? Faire toujours appel à strcpy() pour copier une chaine constante dans une variable de type chaine de caractères. Ce qui signifie également qu'il faut préalablement et explicitement allouer de la mémoire pour mentionCorrespondante avec un malloc().
Note : quand le code que tu as donné marche, c'est en fait le compilateur a transformé l'affectation en un strcpy() en douce.
 
[...]
 




Mais dans ce cas ça ne fonctionne pas car la chaine destinatrice de la copie n'existe pas puisque que c'est un pointeur. Non ? Ou je dis n'importe quoi ?


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
n°215658
LetoII
Le dormeur doit se réveiller
Posté le 17-09-2002 à 14:35:05  profilanswer
 

DarkOli a écrit a écrit :

 
Mais dans ce cas ça ne fonctionne pas car la chaine destinatrice de la copie n'existe pas puisque que c'est un pointeur. Non ? Ou je dis n'importe quoi ?




 
j'avais pas vu la note, c nimportequoi. Si ça marche c par ce que le compilo ne crée pas de chaine temporaire. La chaîne exite en dehors du cadre de la fonction et l'adresse passée en retour est donct tout à fait valide.


---------------
Le Tyran
n°215704
darkoli
Le Petit Dinosaure Bleu
Posté le 17-09-2002 à 15:34:44  profilanswer
 

LetoII a écrit a écrit :

 
 
j'avais pas vu la note, c nimportequoi. Si ça marche c par ce que le compilo ne crée pas de chaine temporaire. La chaîne exite en dehors du cadre de la fonction et l'adresse passée en retour est donct tout à fait valide.




 
Voilà c'est ça. En gros les chaînes en dur dans le code sont comme des variables globales donc le programme se contente de filer l'adresse memoire de la chaine. :D C'est mieux comme ça.


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
n°215823
kenshiro18​2
Posté le 17-09-2002 à 17:42:38  profilanswer
 

BifaceMcLeOD a écrit a écrit :

 
En C strict, cette ligne ne fonctionne pas. Cependant, elle fonctionne correctement avec la plupart des compilateurs C++ actuels. Mais sache que ce n'est pas 100 % portable.




 
Je ne suis pas un pro des standards, mais dans un de mes bouquins (Exceptional C++), l'auteur dit qu'il y a plusieurs zones memoires:
- stack
- variables globales
- heap
- free store
- et... donnees constantes
 
Si j'y pense, je jetterai un coup d'oeil dans ce bouquin chez moi ce soir.

n°216034
Musaran
Cerveaulté
Posté le 18-09-2002 à 03:22:54  profilanswer
 

joce a écrit a écrit :

T'avais pas besoin de te faire chier à faire la tabulation la balise cpp la fait toute seule


Tiens justement, je voulais t'en parler.
J'aime pas ça.
1) Elle est boguée pour certains trucs.
2) J'indente/alligne toujours correctement mon code, à MA façon.
 
Il pourrait pas y avoir au moins une option pour désactiver ça ?


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°216035
Musaran
Cerveaulté
Posté le 18-09-2002 à 03:24:55  profilanswer
 

Il est presque normal que ce programme marche.
 
Pas forcément compilable:

Code :
  1. void main() /*Pas standard*/
  2. int main(void){ /*standard*/
  3. /*...*/
  4. return 0;
  5. }


Les chaînes littérales sont constantes, il vaudrait avoir des 'const char*', mais cela oblige à changer le code:

Code :
  1. const char* definirMention(float note)
  2. {
  3. const char* mentions[]= {
  4.  "Tres bien",
  5.  "Bien",
  6.  "Assez-bien",
  7.  "Passable",
  8.  "Echec"
  9. }
  10. int iMention;
  11.      if (note >= 16) iMention= 0 ;
  12. else if (note >= 14) iMention= 1 ;
  13. else if (note >= 12) iMention= 2 ;
  14. else if (note >= 10) iMention= 3 ;
  15. else                 iMention= 4 ;
  16. return mentions[iMention] ;
  17. }

Ou alors le tableau est plus global, et on renvoie juste l'indice.
Je ne suis pas du tout partisan de la copie inutile dans tous les sens: lenteur + bogues.
Je confirme que les chaînes littérales sont des tableaux globaux/statiques.
Pas de problème d'allocation donc.
 
Compilable, mais super moche:

Code :
  1. # include "mention.c"

On inclus des headers.
inclure des sources, ça ne se fait pas. Ou alors on a de *TRES* bonnes raisons.
 
Correct, mais inutile:

Code :
  1. fprintf(stdout... //on apelles ça printf
  2. fscanf(stdin... ///on apelles ça scanf


Conception
Ça est bien compliqué et long pour pas grand-chose.
Le header devrait être protégé comme indiqué. Sauf qu'il me semble que les noms commençant par "__" ou  "_" sont réservés au système.
C'est demanderNote qui devrait tester la validité, et si elle est invalide, au choix:
-redemmander
-renvoyer une valeur d'erreur à tester


Message édité par Musaran le 18-09-2002 à 03:26:04

---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°216082
antp
Super Administrateur
Champion des excuses bidons
Posté le 18-09-2002 à 09:38:11  profilanswer
 

Musaran a écrit a écrit :

 
1) Elle est boguée pour certains trucs.
2) J'indente/alligne toujours correctement mon code, à MA façon.
 
Il pourrait pas y avoir au moins une option pour désactiver ça ?




 
1) ouais :/
2) y a fixed au lieu de code alors...
 


if machin = truc then
  bidule
else  
begin
  a := 5;
  if test then
    chose(a);
end;


 
PS: Joce, on attend tj la coloration / mise en gras des mots clé pour le Pascal :D vu que t'as viré la balise [pascal] j'imagine que c'est foutu pour ça ? Et qu'en est-il de [php] qui marchait pas trop mal ? (à part un immense bug :D)


Message édité par antp le 18-09-2002 à 09:42:09

---------------
mes programmes ·· les voitures dans les films ·· apprenez à écrire
n°216598
BENB
100% Lux.
Posté le 18-09-2002 à 17:10:06  profilanswer
 

BifaceMcLeOD a écrit a écrit :

Le seul problème que je vois à ce code, outre sa présentation déplorable, bien sûr -- mais je ne vais pas m'apesentir là-dessus, darkoli en a déjà parlé -- ce sont les affectations de chaînes de caractères :

Code :
  1. char *mentionCorrespondante; 
  2. mentionCorrespondante = "Tres bien" ;


En C strict, cette ligne ne fonctionne pas. Cependant, elle fonctionne correctement avec la plupart des compilateurs C++ actuels. Mais sache que ce n'est pas 100 % portable.
 
Qu'est-ce qui cloche ici ?
En théorie, en exécutant cette ligne, le compilateur va créer une chaîne de caractères temporaire pour stocker la chaîne constante "Tres bien", puis, il va faire pointer mentionCorrespondante vers le début de la zone mémoire qui contient cette chaîne de caractères. Enfin, la chaîne temporaire étant, justement temporaire, le compilateur pourra désallouer la mémoire qui a été réservée pour cette chaîne temporaire, et mentionCorrespondante va se retrouver à pointer vers une zone mémoire non allouée.

 
Comment résoudre le problème ? Faire toujours appel à strcpy() pour copier une chaine constante dans une variable de type chaine de caractères. Ce qui signifie également qu'il faut préalablement et explicitement allouer de la mémoire pour mentionCorrespondante avec un malloc().
Note : quand le code que tu as donné marche, c'est en fait le compilateur a transformé l'affectation en un strcpy() en douce.
 
Autre solution, pour ton cas précis, qui est la conséquence d'une règle totalement différente : éviter les chaînes de caractères "codées en dur" disséminées dans le code. Il est grandement préférable de les rassembler à un endroit précis du code. Par exemple, en définissant une table des chaînes constantes plus ou moins globales (cela peut-être global à un module plutôt qu'au programme entier). Ainsi, on aurait, dans le fichier mention.c :

Code :
  1. // Pour la lisibilité.
  2. #define  ARRAY_LENGTH(array)  (sizeof(array) / sizeof((array)[0]))
  3. // Ainsi les chaînes de caractères constantes sont allouées une
  4. // fois pour toutes par le compilateur, dès le début du  
  5. // programme, et elles existent jusqu'à sa fin.
  6. const char* mentions[]       = { "Echec", "Passable", "Assez bien", "Bien", "Tres bien" };
  7. int         seuil_mentions[] = { 0, 10, 12, 14, 16 };
  8. // Noter le nom de fonction plus approprié
  9. const char* donneMention(float note)
  10. {
  11.   int  index;
  12.   for (index = ARRAY_LENGTH(mentions) - 1; index >= 0; index--) {
  13.     if (note >= seuil_mentions[index]) {
  14.       return mentions[index];
  15.     }
  16.   }
  17.   return mentions[0];
  18. }






 
Je suis assez surprise par ce que tu dis...
 
En effet le Kernighan & Ritchie n'est pas du tout du meme avis que toi.
 
D'apres ce livre (2nd edition C ansi A2.6 & 5.5) une constante chaine de caracteres à une classe de stockage static (donc loin d'etre temporaire) et l'affecter à un pointeur fait que ce pointeur pointe sur cette chaine static.
 
Il n'est absolument pas fait metion d'une quelconque chaine temporaire...
 
Par contre modifier une telle chaine est annoncé comme indefini. Ce qui infirme encore la possibilité d'une chaine temporaire.
 
A propos du strcpy, le strdup permet de faire en un étape l'allocation et la copie.


Message édité par BENB le 18-09-2002 à 17:12:33
n°216602
youdontcar​e
Posté le 18-09-2002 à 17:14:09  profilanswer
 

BENB a écrit a écrit :

Par contre modifier une telle chaine est annoncé comme indefini.


haaaa, le mot magique de toute spécification qui se respecte ! :D

n°216608
BENB
100% Lux.
Posté le 18-09-2002 à 17:23:02  profilanswer
 

youdontcare a écrit a écrit :

haaaa, le mot magique de toute spécification qui se respecte ! :D




 
Le plus probable est qu'elle soit modifiée definitivement jusqu'à la fin de l'execution du programme, mais sachant que le compilo à le droit de reunir plusieurs chaines identiques modifier une chaine peut en modifier une autre ailleurs (identique), les conséquenses sont donc bien indéfinies...


Message édité par BENB le 18-09-2002 à 17:23:40
n°216610
LetoII
Le dormeur doit se réveiller
Posté le 18-09-2002 à 17:26:16  profilanswer
 

BENB a écrit a écrit :

 
 
Le plus probable est qu'elle soit modifiée definitivement jusqu'à la fin de l'execution du programme, mais sachant que le compilo à le droit de reunir plusieurs chaines identiques modifier une chaine peut en modifier une autre ailleurs (identique), les conséquenses sont donc bien indéfinies...




 
Ben non, puis que ça dépend du compilo :D


---------------
Le Tyran
n°216617
youdontcar​e
Posté le 18-09-2002 à 17:37:25  profilanswer
 

BENB a écrit a écrit :

les conséquenses sont donc bien indéfinies...


vi, bien que l'approche que tu cites est la plus logique d'un point de vue implémentation. mais voir 'indéfini' me donne toujours l'impression d'un "on n'est pas allé au bout des choses" (impression certainement erronée, mais qui me titille toujours).

n°216627
BENB
100% Lux.
Posté le 18-09-2002 à 17:49:12  profilanswer
 

Leto II > Non indefini n'est pas forcement "implementation dependant". Ca peut l'etre, mais pas forcement.
 
J'ai bien commencé mon post par "Le plus probable", et je montre que pour un implementation définie, le resultat est indéfini, c'est à dire que la modification d'une chaine a un endroit donné peut provoquer de modifications de comportement du programme ailleurs.  
 
Après rien n'empeche l'implémentation d'etre différente que celle que je suppose, et donc que les modifications aient des conséquences différentes que celles que je suppose...
 
Bien souvent le terme indefini laisse supposer (comme le confirme youdontcare) qu'on ne c'est pas posé la question. Bien souvent au contraire cette affirmation est forte est sous-entends que les conséquences peuvent affecter le fonctionnement de l'application au-dela de ce qui était prévisible par des effets de bord que l'utilisateur n'est pas sensé connaitre.
 
C'est bien plus grave qu'un resultat "implementation dependant" qui lui est supposé etre constant pour une implementation donnée, et qui peut donc etre vérifé par un test...

n°216647
BifaceMcLe​OD
The HighGlandeur
Posté le 18-09-2002 à 18:17:01  profilanswer
 

BENB a écrit a écrit :

 
En effet le Kernighan & Ritchie n'est pas du tout du meme avis que toi.




Le seul hic, c'est que bien peu de compilateurs respectent ce standard historique : la plupart respectent la norme ANSI, qui comportent pas mal de différences assez subtiles avec le standard K&R (mais on est en plein dans la subtilité, là, n'est-ce pas ?  ;) ).
 
A part ça, je peux te garantir que ce que je décris, c'est du vécu. Avant, moi aussi, je faisais des affectations directes. Jusqu'au jour où j'ai eu des bugs méchamment subtils et méchamment balèzes à trouver (qui datent d'il y a une petite dizaine d'années, je reconnais). Et depuis, je fais des strcpy(), ou mieux, des tables de caractères statiques.
 
PS: J'avoue que je n'aime pas trop les strdup(). C'est une fonction qui fait 2 choses en une, et je préfère faire une allocation explicite (comme ça, c'est plus facile de compter le nombre d'allocations et de désallocations de manière automatique, aussi ; donc de faire des vérifications dans la gestion mémoire). Et puis une fois sur 2, tu es en C++, et il n'est jamais très bon de mélanger les blocs mémoires alloués avec new et malloc() (et clairement, c'est très mauvais de désallouer par delete un bloc alloué par malloc() ; or strdup() fait un malloc() implicite)

n°216653
BENB
100% Lux.
Posté le 18-09-2002 à 18:22:30  profilanswer
 

BifaceMcLeOD a écrit a écrit :

 
Le seul hic, c'est que bien peu de compilateurs respectent ce standard historique : la plupart respectent la norme ANSI, qui comportent pas mal de différences assez subtiles avec le standard K&R (mais on est en plein dans la subtilité, là, n'est-ce pas ?  ;) ).
 
A part ça, je peux te garantir que ce que je décris, c'est du vécu. Avant, moi aussi, je faisais des affectations directes. Jusqu'au jour où j'ai eu des bugs méchamment subtils et méchamment balèzes à trouver (qui datent d'il y a une petite dizaine d'années, je reconnais). Et depuis, je fais des strcpy(), ou mieux, des tables de caractères statiques.
 
PS: J'avoue que je n'aime pas trop les strdup(). C'est une fonction qui fait 2 choses en une, et je préfère faire une allocation explicite (comme ça, c'est plus facile de compter le nombre d'allocations et de désallocations de manière automatique, aussi ; donc de faire des vérifications dans la gestion mémoire). Et puis une fois sur 2, tu es en C++, et il n'est jamais très bon de mélanger les blocs mémoires alloués avec new et malloc() (et clairement, c'est très mauvais de désallouer par delete un bloc alloué par malloc() ; or strdup() fait un malloc() implicite)




 
Je parlais bien de l'édition consacrée au C ansi...  ;)  
Je te donne d'ailleurs les references des chapitres concernés
 
Par contre les différences entre C ansi et C K&R ne tiennent pas de la subtilité à mon avis...


Message édité par BENB le 18-09-2002 à 18:23:37
n°216689
LeGreg
Posté le 18-09-2002 à 19:39:00  profilanswer
 

le coup du litteral instancie temporairement je trouve ca un peu tire par les cheveux et tres certainement vicieux
 
Et le surcout du strcpy aurait lieu a chaque appel de la fonction??
 
LeGreg

n°216849
LeGreg
Posté le 18-09-2002 à 23:03:26  profilanswer
 

youdontcare a écrit a écrit :

vi, bien que l'approche que tu cites est la plus logique d'un point de vue implémentation. mais voir 'indéfini' me donne toujours l'impression d'un "on n'est pas allé au bout des choses" (impression certainement erronée, mais qui me titille toujours).




 
bien sur que non, s'il est ecrit dans le standard que le comportement d'un code est indefini c'est qu'on a etudie ce cas et qu'on en a deduit qu'on pouvait ne pas le definir (il est le plus souvent incorrect d'ecrire du code qui a un comportement indefini, meme si ca compile). Si l'on n'etait vraiment pas alle "au bout des choses", on n'en aurait pas fait mention, ce qui n'est pas le cas.
 
Exemple: delete sur un pointeur non alloue. Le compilateur va te laisser faire, mais le comportement du programme ensuite va etre 'indefini': soit il ne fait rien de particulier (le cas le pire), soit il crashe sur l'instruction (le meilleur cas), soit il va provoquer une corruption de la memoire qui peut avoir n'importe quel effet.
Si on avait affaire a un autre langage que le C++, on aurait pu definir dans le standard qu'appeler delete sur un pointeur non alloue ne fasse rien ou leve une exception par exemple mais cela aurait necessite un travail supplementaire et la simplicite du langage en aurait souffert.
(en java toute reference pointe toujours sur un objet valide, sauf si elle est mise explicitement a nil)
 
LeGreg

n°216855
antp
Super Administrateur
Champion des excuses bidons
Posté le 18-09-2002 à 23:09:42  profilanswer
 

un autre exemple de comportement indéfini : un goto qui saute à l'intérieur d'une boucle (venant de l'extérieur de la boucle)
du moins c'est comme ça en Pascal :

Citation :

Never jump into a loop or other structured statement, since this can have unpredictable effects.


---------------
mes programmes ·· les voitures dans les films ·· apprenez à écrire
n°216882
Musaran
Cerveaulté
Posté le 18-09-2002 à 23:44:05  profilanswer
 

Quand quelque chose est spécifié comme indéfini, ça veut dire qu'on y a bien pensé, qu'on a bien examiné les possibilités, mais qu'on préfères pour l'instant n'imposer aucune règle.
 
Je me rends compte qu'il y a moyen de faire beaucoup plus simple que ma proposition:

Code :
  1. const char* definirMention(float note)
  2. {
  3. if (note >= 16) return "Tres bien";
  4. if (note >= 14) return "Bien";
  5. if (note >= 12) return "Assez bien";
  6. if (note >= 10) return "Passable";
  7. /*noalign*/     return "Echec";
  8. }

L'apparence n'est pas ordinaire, mais c'est du code absolument standard, qui est en plus court et lisible.
 
De mon point de vue, copier ces chaînes serait une source de bogues (dépassement, non-libération, identité), et une complication inutile.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°216985
darkoli
Le Petit Dinosaure Bleu
Posté le 19-09-2002 à 09:43:21  profilanswer
 

Musaran a écrit a écrit :

Quand quelque chose est spécifié comme indéfini, ça veut dire qu'on y a bien pensé, qu'on a bien examiné les possibilités, mais qu'on préfères pour l'instant n'imposer aucune règle.
 
Je me rends compte qu'il y a moyen de faire beaucoup plus simple que ma proposition:

Code :
  1. const char* definirMention(float note)
  2. {
  3. if (note >= 16) return "Tres bien";
  4. if (note >= 14) return "Bien";
  5. if (note >= 12) return "Assez bien";
  6. if (note >= 10) return "Passable";
  7. /*noalign*/     return "Echec";
  8. }

L'apparence n'est pas ordinaire, mais c'est du code absolument standard, qui est en plus court et lisible.
 
De mon point de vue, copier ces chaînes serait une source de bogues (dépassement, non-libération, identité), et une complication inutile.




+1 (Comme ce que j'ai dit tout en haut :D)


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
n°217001
BifaceMcLe​OD
The HighGlandeur
Posté le 19-09-2002 à 10:09:27  profilanswer
 

Musaran a écrit a écrit :

Quand quelque chose est spécifié comme indéfini, ça veut dire qu'on y a bien pensé, qu'on a bien examiné les possibilités, mais qu'on préfères pour l'instant n'imposer aucune règle.
 
Je me rends compte qu'il y a moyen de faire beaucoup plus simple que ma proposition:
(...)
L'apparence n'est pas ordinaire, mais c'est du code absolument standard, qui est en plus court et lisible.
 
De mon point de vue, copier ces chaînes serait une source de bogues (dépassement, non-libération, identité), et une complication inutile.




Vous noterez quand même que ma proposition ne comportait aucune copie de chaîne, puisqu'elle utilisait une table de chaînes de caractères allouées statiquement...  ;)

mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C++

  prob C étrange : programme qui marche et personne ne comprend pourquoi

 

Sujets relatifs
[BORLAND c++] utilisation de TRichedit ENCORE des prob[RESOLU NA!!][PHP][Mysql] Update d'un champ qui marche po
programme en C++ : filtre médianprogrammationworld.com ne marche plus ??
comment changer un nom au demarage d'un programme fé en c++ visual basProb avec la bibliotheque BGI ( graphics.h ) [prob résolu]
pb lors de l excecution d un programmeTableau d'objet : comment çà marche
je cherche un serveur smtp qui marche pour mettre dans le forum phpBB2Quel est le meilleur programme de programmation en c++
Plus de sujets relatifs à : prob C étrange : programme qui marche et personne ne comprend pourquoi


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR