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 :

Pointeurs et segmentation fault


Sujet :

C

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2016
    Messages
    52
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2016
    Messages : 52
    Points : 35
    Points
    35
    Par défaut Pointeurs et segmentation fault
    Bonjour à tous,

    Dans le cadre de mes études, je dois faire un programme qui reproduit les comportements de base d'un shell bach. J'ai quasiment terminé, mais je bloque sur un point : implémenter l'équivalent de argv[] après avoir récupérer une saisie de l'utilisateur.

    Je poste ici seulement les bouts de code concerné, le main et la fonction qui pose problème. C'est sûrement un problème trivial sur des pointeurs (ma mémoire semble ne pasêtre alloué) mais comme j'utilise la référence à un pointeur sur un pointeur, je m'embrouille. Peut-être me suis je compliquer la vie pour rien...

    main.c
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    int main(int argc, char *argv[], char *arge[]) {
     
    	char *commande = NULL;
    	char **arguments = NULL;
     
    	printf("Type Q to quit the shell\n>>");
     
    	lireCommande(&commande, &arguments);
    	printf("Valeur de commande dans le main : %s\n", commande);
     
    	for(int i=0; arguments[i]!=NULL; i++){
    		printf("Valeur de argument[%d] dans le main : %s\n", i, arguments[i]);
    	}
     
    	while(strcmp("Q", commande) != 0){
    		printf("Valeur de strcmp : %d\n", strcmp("Q", commande));
    	/*
    		if(strcmp("setenv", commande) == 0){
    			setenv2(commande, arguments, arge);
    		}
    		// Traitement des commandes externes
    		else if(typeCommande(commande) == 0){
    			commandeExterne(commande, arguments);
    		}		
    		// Traitement des commandes internes
    		else {
    			commandeInterne(commande, arguments);
    		}
    	*/	
    		printf(">>");
    		lireCommande(&commande, &arguments);
    	}
     
     
    	free(commande);
    	free(arguments);
    	return 0;
    }
    et ma fonction
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    void lireCommande(char **commande, char **arguments[]){
    	char buffer[256];
    	char *abuffer[10];
    	char* delim = " ";
    	int i = 0;
    	char *token;
     
    	if(fgets(buffer, sizeof buffer, stdin)==NULL){
    		printf("Erreur de lecture\n");
    		exit(-1);
    	}
    	else{
    		buffer[strcspn(buffer, "\n")] = '\0';    // Remplace le \n à la fin du fgets par \0
     
    		*commande = calloc(strlen(buffer)+1, sizeof(char));
    		strcpy(*commande, buffer);
     
    		token = strtok(buffer, delim);
    		printf("Valeur de token : %s\n", token);
     
    		while(token!=NULL){
    			abuffer[i]=token;
    			token = strtok(NULL, delim);
    			i++;	
    		}
    		printf("Valeur de i : %d\n", i);
     
    		*arguments = calloc(i, sizeof(char*));//marche pas, pourquoi ?
     
    		for(int j=0; j<i; j++){
    			printf("Valeur de abuffer[%d] dans lireCommande : %s\n", j, abuffer[j]);
    			*arguments[j]=abuffer[j]; //segfault pour j>0
    			printf("Valeur de arguments[%d] dans lireCommande : %s\n", j, *arguments[j]);
    		}
     
    		printf("Valeur de commande dans lireCommande : %s\n", *commande);
    	}
    }
    Le problème survient lorsque je passe arguments par référence dans ma fonction lireCommande, sinon ça fonctionne correctement (mais je ne peux donc pas l'utiliser dans le main).

    J'ai essayé de débuger avec valgrind mais je n'ai pas compris grand chose d'autre que ''ma mémoire est pas allouée".

    Je vous remercie grandement par avance.

  2. #2
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Ligne 33 : *arguments[j] .

    L'opérateur [] a une priorité supérieure à l'opérateur * (source). Plutôt que d'écrire à l'adresse contenue dans *arguments, plus j * sizeof(char *), tu écris à l'adresse contenue dans [ arguments plus j * sizeof(char **) ].

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2016
    Messages
    52
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2016
    Messages : 52
    Points : 35
    Points
    35
    Par défaut
    Je t'avoue ne pastout comprendre, en faisant ça résout le problème ?

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Jichaels Voir le message
    Je t'avoue ne pastout comprendre, en faisant ça résout le problème ?
    Bonjour

    C'est exactement ça. Mais il faut y penser aussi dans le printf() de la ligne 34. Mais ça ne résout "qu'un seul" des problèmes de ce code...

    Bon déjà dans ton main, tu appelles ta fonction une fois puis tu la rappelles dans ta boucle. Ca, ca sent le bouquin de théorie de la méthode Warnier appliqué à la lettre. Alors ok, c'était super en 1970 quand les langages savaient faire que dalle mais aujourd'hui ils permettent quand-même des constructions plus élégantes...

    Code c : 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
    fputs("Type Q to quit the shell\n", stdout);		// En effet, pourquoi utiiiser un truc aussi lourd que printf() pour une simple chaine ???
    while(1) {
    	fputs(">>", stdout);			// Idem -  En plus le printf() bufferise ses infos donc il peut y avoir décalage temporel entre "appel de printf()" et "affichage à l'écran"
    	lireCommande(&commande, &arguments);
     
    	printf("Valeur de strcmp : %d\n", strcmp("Q", commande));  // Debug (ce qui explique qu'on appelle ici aussi strcmp() au lieu d'utiliser une variable
    	if (strcmp("Q", commande) == 0) break;
     
    	printf("Valeur de commande dans le main : %s\n", commande);
     
    	for(i=0; arguments[i]!=NULL; i++){
    		printf("Valeur de argument[%d] dans le main : %s\n", i, arguments[i]);
    	}
    /*
    	if(strcmp("setenv", commande) == 0){
    		setenv2(commande, arguments, arge);
    	}
    	// Traitement des commandes externes
    	else if(typeCommande(commande) == 0){
    		commandeExterne(commande, arguments);
    	}		
    	// Traitement des commandes internes
    	else {
    		commandeInterne(commande, arguments);
    	}
    */	
    }

    Cela mis à part, il y a un détail qui me choque: en ligne 32, tu appelles lireCommande(&commande, &arguments). Alors pour "commande" il n'y a pas de souci: c'est un char * donc il sera reçu dans un char **. Mais pour arguments c'est le même mécanisme de pensée qui doit s'appliquer: c'est un char ** donc il doit être reçu dans un char *** et non un char **[] !!! Quand on passe l'adresse de "truc" à une fonction, celle-ci la stocke dans un "truc *" !!!
    En C, l'équivalence "tableau=pointeur" ben déjà ça n'en est pas une et d'autre part ça ne fonctionne que dans le sens "je peux convertir un tableau en pointeur" et pas dans le sens "je peux convertir un pointeur en tableau".
    Donc ta fonction doit s'écrire ainsi: void lireCommande(char **commande, char ***arguments).

    Autre chose: c'est bien pensé ton buffer[strcspn(buffer, "\n")] = '\0' mais il y a plus simple et plus rapide: *(strchr(buffer, '\n'))='\0'. Plus rapide car c'est plus rapide de chercher un caractère qu'une chaine et tu évites un appel à buffer[x] (qui se traduit par un placement en buffer[0] puis un décalage de x éléments).
    Ceci dit, il y a un risque (dans ton écriture comme dans la mienne) que ça plante si la chaine n'a pas de '\n' (ce qui arrive si la saisie est plus longue que le nb de caractères demandés à fgets()).

    Solution
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt;
    if ((pt=strchr(buffer, '\n')) != NULL) *pt='\0';
    Autre petit détail: ce exit() dans ta fonction "lireCommande()" car c'est pas super propre de quitter brutalement une sous-fonction (on ne doit quitter que par le main()). Mais en plus le "else" du dessous ne sert absolument à rien et décale inutilement ton code.

    Ensuite un peu plus gros dans ta ligne 12 de ton main() !!! Parce que dans cette ligne, tu cherches un "NULL" dans le tableau "arguments". Or nulle part, dans ta fonction "lireCommande()" tu ne positionnes ce NULL !!!
    La solution est heureusement simple: dans ton calloc (que tu dis ne pas fonctionner alors que tu n'as même pas vérifié son résultat) tu alloues (i+1) au lieu de (i).

    On arrive au croustillant: dans ta fonction "lireCommande()" où tu recopies dans (*argument)[i] le contenu de abuffer[i] ben en fait tu ne fais que recopier des pointeurs locaux ; pointeurs dont le contenu sera réaffecté à autre chose dès que tu auras quitté la fonction !!!!!!!!

    Il faut, à chaque fois que tu veut récupérer abuffer[i], que tu fasses d'abord un malloc suffisant puis que tu passes par strcpy(). Ou alors tu utilises strdup() qui se charge de tout ça. Faudra aussi penser à écrire une fonction de désallocation à appeler depuis le main().
    Ca ne change rien au pb du NULL exposé un peu avant et à sa solution.

    Et enfin le final: ben chaque fois que tu appelles "lireCommande()", la fonction fait du malloc/calloc à tire larigot sans que ça ne te préoccupe vraiment. C'est vrai que la mémoire, tant qu'on en a... et quand ça plante ben suffit juste de racheter des barrettes hein ?
    => Faut que tu fasses tes free() dans le while !!!

    Une fois tout ça réglé, là ça sera ok.
    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]

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2016
    Messages
    52
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2016
    Messages : 52
    Points : 35
    Points
    35
    Par défaut
    Bonjour,

    Tout d'abords je te remercie beaucoup pour cette réponse plus que complète. J'avais d'ici mon post précédent résolu mon problème (qui était bien de copier des variables locales) mais j'ai appris pas mal de choses grâce à toi

    J'étais surement fatigué quand j'ai écris le code que j'ai posté ici, même moi ça me fait peur quand je relis certaines choses

    Merci encore !

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 24/02/2010, 10h38
  2. segmentation.fault pointeur ->de.*m.
    Par PhilippeHen dans le forum Débuter
    Réponses: 5
    Dernier message: 05/08/2009, 16h51
  3. [REDHAT] Segmentation fault systematique
    Par mela dans le forum RedHat / CentOS / Fedora
    Réponses: 2
    Dernier message: 21/09/2004, 06h05
  4. Réponses: 13
    Dernier message: 13/07/2004, 15h41
  5. Comment contrer la "segmentation fault" ?
    Par guillaume_pfr dans le forum C
    Réponses: 15
    Dernier message: 08/08/2003, 13h43

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