IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Gestion des char : pourquoi ça crash ?


Sujet :

C

  1. #1
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 211
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 211
    Points : 8 316
    Points
    8 316
    Billets dans le blog
    52
    Par défaut Gestion des char : pourquoi ça crash ?
    Bonjour,

    Je viens de débugé une DLL écrite en C pour une application Java. Cette dll provoquait des crash (non systématique) de l'application Java lors de l'initialisation :
    #
    # An unexpected error has been detected by HotSpot Virtual Machine:
    #
    # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x7706e3be, pid=476, tid=4880
    #
    # Java VM: Java HotSpot(TM) Client VM (1.5.0_13-b05 mixed mode)
    # Problematic frame:
    # C [ntdll.dll+0x2e3be]
    #
    Après un peu de temps, la cause du crash s'est rapprochée des lignes suivantes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char * sChemin = NULL;
    char nomIni[250]="\0"; 
    sChemin = sC_GEN_AddSepToDirectory(nomFichier);
    strcat(sChemin,"MONFICHIER.INI");
    En sachant que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    FCT_EXP char * sC_GEN_AddSepToDirectory(char * _sChemin) {
    // L'implémentation de la méthode ...
    }
    En comparant avec le reste du code C à ma disposition dans cette DLL, j'ai mis le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char sChemin[250] = "\0";
    strcpy(sChemin,sC_GEN_AddSepToDirectory(nomFichier));
    printf(sChemin);
    strcat(sChemin,"MONFICHIER.INI");
    Après une série d'essai, je n'ai plus de crash de l'application avec la DLL corrigé.

    Ma question est pourquoi la DLL provoquait un crash ?

    Cordialement,
    Patrick Kolodziejczyk.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  2. #2
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 291
    Points : 4 941
    Points
    4 941
    Billets dans le blog
    5
    Par défaut
    Bonjour.

    La fonction FCT_EXP char * sC_GEN_AddSepToDirectory(char * _sChemin) allouait-elle correctement l'espace nécessaire pour la chaîne retournée ?

  3. #3
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 211
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 211
    Points : 8 316
    Points
    8 316
    Billets dans le blog
    52
    Par défaut
    Que veux tu dire par "correctement " ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    FCT_EXP char * sC_GEN_AddSepToDirectory(char * _sChemin) 
    {
    	char * sChemin;
    	int taille = 0;
     
    	if(_sChemin == NULL) 	
    	{
    		_sChemin = malloc(sizeof(char));	 
    		_sChemin="\0";
    	}
     
    	sChemin = strrchr(  _sChemin, XGN_ANTISLASH); 
    	/* Il n'y a pas de sép à la fin du chemin donc on le met */
    	if(!sChemin || strlen(sChemin) > 1)
    	{
    		taille = strlen(_sChemin);
    		sChemin = malloc((taille+2)*sizeof(char));	 
    		strncpy(sChemin, _sChemin, taille);
    		sChemin[taille] = XGN_ANTISLASH;
    		sChemin[taille+1] = '\0';
    	}
    	/* Il y a un sép à la fin du chemin c'est bon */
    	else
    	{
    		sChemin =  _sChemin;
    	}
     
     	return sChemin;
     }
    J'ai bien le malloc pour la chaine de retour, mais je ne pense pas que c'est cela dont tu parle...

    Cordialement,
    Patrick Kolodziejczyk.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 370
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 370
    Points : 23 625
    Points
    23 625
    Par défaut
    Bonsoir,

    Citation Envoyé par kolodz Voir le message
    Que veux tu dire par "correctement " ?
    Il y a une énorme erreur dans le passage suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	if(_sChemin == NULL) 	
    	{
    		_sChemin = malloc(sizeof(char));	 
    		_sChemin="\0";
    	}
    Tu ne déposes pas un caractère nul dans l'espace fraîchement alloué mais tu détournes le pointeur pour le faire pointer une autre chaîne, en lecture seule. Ce faisant, tu perds la valeur de retour renvoyée par malloc() et tu provoques une fuite de mémoire.

    Par la suite, si ta condition est fausse, tu renvoies directement cette adresse à l'utilisateur qui, lui, pense qu'elle a été allouée. S'il essaie d'y écrire, cela plantera. S'il essaie de la libérer avec free(), cela plantera aussi.

  5. #5
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 211
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 211
    Points : 8 316
    Points
    8 316
    Billets dans le blog
    52
    Par défaut
    Je n'ai pas bien compris ton explication. Mais globalement, tout les appels à cette méthodes peuvent provoquer un crash.
    La modification que j'ai fait n'est donc qu'une rustine...

    Si je reprends ton explication :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    _sChemin = malloc(sizeof(char));
    On réalisé une allocation mémoire et place un mon pointeur de char dans la variable.
    On place le pointeur du caractère null dans la variable.
    Ce qui fait qu'on perd la référence de ce qu'on vient d'alloué en mémoire. (Fuite mémoire de la taille d'un char)

    Cependant, pour cette partie :
    Par la suite, si ta condition est fausse, tu renvoies directement cette adresse à l'utilisateur qui, lui, pense qu'elle a été allouée.
    Si _sChemin a été initialé avec une caractère nul, alors sChemin sera forcément un pointeur nul. Car strrchr, il ne trouvera pas occurrence cherché.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sChemin = strrchr(  _sChemin, XGN_ANTISLASH);
    Ce qui fait que je passe dans ce morceau de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    taille = strlen(_sChemin);
    sChemin = malloc((taille+2)*sizeof(char));	 
    strncpy(sChemin, _sChemin, taille);
    sChemin[taille] = XGN_ANTISLASH;
    sChemin[taille+1] = '\0';
    Dans ce cas là, strlen(_sChemin) retournera 0.
    On aura donc sChemin ayant le contenu suivant {["\"],["\0"]} (ma synthase ressemble plus à du json que du c non ?)

    Par contre, si on donne un pointeur de char non null. qui finit par un XGN_ANTISLASH et qui est en plus en read only. Là ça va crashé, non ?

    Cordialement,
    Patrick Kolodziejczyk.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  6. #6
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 291
    Points : 4 941
    Points
    4 941
    Billets dans le blog
    5
    Par défaut
    Je ferais plutôt :
    Enfin je crois...

  7. #7
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 211
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 211
    Points : 8 316
    Points
    8 316
    Billets dans le blog
    52
    Par défaut
    Juste pour info : le code des poste précédent c'est du "Legacy code".
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  8. #8
    Membre chevronné
    Homme Profil pro
    Inscrit en
    Août 2009
    Messages
    508
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Août 2009
    Messages : 508
    Points : 2 155
    Points
    2 155
    Par défaut
    C'est du "Legacy code" mais c'est bien pourri si je puis me permettre.

    Remarque diverses :
    a - dans la fonction, sChemin n'est pas initialisé (pour le principe)
    b - comme indiqué par Gérald3d l'init n'est pas correcte si chemin = NULL

    Après le souci, c'est le mode d'appel depuis Java qui peut poser problème (JNI ?).

    Quand au code que tu a modifié, sChemin est il alloué ?

    Les ressources allouées pour sChemin sont elles libérées quelque part ?

  9. #9
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 211
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 211
    Points : 8 316
    Points
    8 316
    Billets dans le blog
    52
    Par défaut
    C'est du "Legacy code" mais c'est bien pourri si je puis me permettre.
    Et encore... tu n'as pas vue comment ça gère les pointeurs...
    Après le souci, c'est le mode d'appel depuis Java qui peut poser problème (JNI ?).
    J'en doute, la partie qui pose problème, c'est l'initialisation de la DLL. Java ne fait que la déclenché. Aucun paramètre fournit et aucune résultat retourné.
    Les ressources allouées pour sChemin sont elles libérées quelque part ?
    Il y a une erreur dans le code que j'ai modifié (oublie) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char sChemin[250] = "\0";
    Dans tout les cas, il me semble que j'ai m'a réponse à ma question initial. Je tag résolu, mais si vous avez d'autres commentaire, je prends !

    Cordialement,
    Patrick Kolodziejczyk.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par kolodz Voir le message
    Il y a une erreur dans le code que j'ai modifié (oublie) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * sChemin[250] = "\0";
    Bonjour

    Tu ne peux pas placer l'adresse d'une chaine ("\0" est bien une chaine puisqu'il y a des quotes doubles) dans un tableau de 250 pointeurs (250 adresses) !!!

    Sois tu écris
    • char sChemin[250] = "\0"; => tu crées une chaine de 250 caractères et tu lui copies dès le départ une autre chaine
    • char *sChemin = "\0"; => tu crées un pointeur et tu lui affectes l'adresse de la chaine statique "\0"
    • char *sChemin[250] = {"\0"}; => tu crées un tableau de 250 pointeur et tu affectes l'adresse de la chaine statique "\0" dans le premier pointeur du tableau
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  11. #11
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 211
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 211
    Points : 8 316
    Points
    8 316
    Billets dans le blog
    52
    Par défaut
    D'après le peu de documentation que j'ai lu, la déclaration suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * sChemin[250] = "\0";
    Revient à cette affectation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * sChemin[250] = "";
    Car :
    Actually, you do not place the null character at the end of a string constant. The C compiler automatically places the '\0' at the end of the string when it initializes the array.
    Or le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char label[10] = "Single";
    Revient à cette initialisation mémoire :
    {[S][i][n][g][l][e][\0][][][]}
    Ce qui fait que dans mon cas, ce la revient à :
    {[\0][][][][][][][][][][][][]....}
    Même si je trouve la notation particulièrement stupide ! Mais, du peu que je comprends du C cela reste valide...
    Et contenu de la règle sacré concernant le Legacy Code (perso) :
    Tu ne touchera point ce qui fonctionne !
    Source :
    https://www.cs.bu.edu/teaching/cpp/string/array-vs-ptr/
    http://www.tutorialspoint.com/cprogr.../c_strings.htm
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par kolodz Voir le message
    D'après le peu de documentation que j'ai lu, la déclaration suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * sChemin[250] = "\0";
    Revient à cette affectation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * sChemin[250] = "";
    Pas tout à fait. Tu vois bien que dans le premier cas tu lui affectes un caractère et pas dans le second (détails un poil plus bas)

    Citation Envoyé par kolodz Voir le message
    Or le code suivant : char label[10] = "Single"; revient à cette initialisation mémoire {[S][i][n][g][l][e][\0][][][]}
    Si tu veux bien je vais réécrire ça avec la notation propre du C, tu verras que ça aide: char label[10] = "Single" revient à cette initialisation mémoire: char label[10] = {'s', 'i', 'n', 'g', 'l', 'e', '\0', ..., ..., ...} (les "..." signifiant "valeurs aléatoires qui étaient dans la mémoire à ce moment là)

    Donc pour en revenir à ton premier exemple, écrire char label[5] = "\0" équivaut à char label[5] = {'\0', '\0', ..., ..., ...} alors qu'écrire char label[5] = "" équivaut à char label[5] = {'\0', ..., ..., ..., ...}.
    Ainsi, dans les faits, il y a une énorme différence (dans un des cas tu ne peux pas garantir la valeur du second élément du tableau). Toutefois dans l'utilisation conventionnelle d'une chaine, c'est à dire via des fonctions de manipulation classiques comme strlen(), strcpy(), printf(), toutes ces fonctions s'arrêtant au premier '\0' rencontré, il se trouve que ça ne change rien au résultat. Donc effectivement tu as le droit de simplifier en disant que c'est pareil à condition que tu gardes quand-même ce détail à l'esprit. Parce qu'ici ça ne porte pas à conséquence mais ça pourrait poser un problème dans ce cas là: char label[1] = "\0"!!!
    Fort heureusement ce cas de figure ne peut pas arriver parce que l'écriture correcte char label[1] = "" ne peut pas exister (qui irait créer une chaine condamnée à être toujours vide ???). Mais puisque les deux écritures, sans être identiques, produisent un résultat qui, lui, l'est, autant rester simple et écrire char label[5] = ""...

    Citation Envoyé par kolodz Voir le message
    Ce qui fait que dans mon cas, ce la revient à {[\0][][][][][][][][][][][][]....}
    En théorie oui. Dans la réalité, le compilo voyant des quotes traduira ça par une possible erreur de ta part et te renverra une erreur (enfin un compilo digne de ce nom). Ainsi, chez-moi, char *x[250]={'\0'} compile mais pas char *x[250]="". Mais même la première écriture n'est pas "politiquement" correcte car on ne met pas de caractère (même égal à 0) dans un pointeur. La bonne syntaxe (c'est à dire la syntaxe qui montre à tout le monde que tu sais ce que tu fais) est celle-là: char *x[250]={NULL} ou au pire char *x[250]={0}. La preuve, dans ton premier post j'ai mal compris ce que tu cherchais à faire et pensais que tu voulais affecter une chaine

    N'oublie jamais qu'un code est toujours 100 fois plus lu qu'écrit...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  13. #13
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 823
    Points : 7 119
    Points
    7 119
    Par défaut
    Dans le cas formateur, c'est intéressant comme débat. Si on veut éviter cela, l'utilisation de strdup est intéressante.
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 23/06/2006, 10h23
  2. [reseaux] Gestion des threads en perl
    Par totox17 dans le forum Programmation et administration système
    Réponses: 2
    Dernier message: 28/11/2002, 09h40
  3. Gestion des variables - mémoire ?
    Par RIVOLLET dans le forum Langage
    Réponses: 4
    Dernier message: 26/10/2002, 12h44
  4. Réponses: 4
    Dernier message: 04/07/2002, 12h31
  5. c: gestion des exceptions
    Par vince_lille dans le forum C
    Réponses: 7
    Dernier message: 05/06/2002, 14h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo