Mes amis les erreurs de segmentation ;)

Discutez d'informatique ici !
Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

Mes amis les erreurs de segmentation ;)

par Rockleader » 09 Fév 2014, 13:15

Hello; pouvez vous m'éclairer sur ce qui peut causer ce genre d'erreur; j'avoue que je vois pas --'


Code: Tout sélectionner
f1.c

struct cas {
int val1;
int val2
};


struct tab{
cas t*
int val3
}

/*cas et tab sont défini dans le .c et renommé par des pointeurs avec typedef dans le .h*/



Lorsque dans une fonction je veux faire une toute bête affectation je prend une erreur de segmentation

Par exemple si je fais

Code: Tout sélectionner
tab Tableau=NULL;

Tableau->val3=0; //prend une erreur de segmentation


J'ai fait des traces et l'erreur se produit bien sur un problème similaire; en ce qui concerne le t* je fais un calloc; mais là pour val3 pourquoi ça devrait coincer ? Le compilo ne détecte rien à la compilation
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !



Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 09 Fév 2014, 13:46

hello,

code incomplet.
tu peux pas utiliser tab tel quel, il manque un typedef, sinon on aurait struct tab.
Par ailleurs,
Tableau-> montre l'utilisation d'un pointeur (écriture équivalente à (*Tableau).val3) et si tu utilises un pointeur avec une zone mémoire non allouée, c'est normal que ca clash.
Il te manque un calloc sur Tableau de fait.
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 09 Fév 2014, 14:03

fatal_error a écrit:hello,

code incomplet.
tu peux pas utiliser tab tel quel, il manque un typedef, sinon on aurait struct tab.
Par ailleurs,
Tableau-> montre l'utilisation d'un pointeur (écriture équivalente à (*Tableau).val3) et si tu utilises un pointeur avec une zone mémoire non allouée, c'est normal que ca clash.
Il te manque un calloc sur Tableau de fait.


Non; le typedef je l'ai mis dans le .h il est donc bien présent.

En revanche tu me dis que je dois quand même utiliser un malloc par dessus.

Ce qui veut dire que je dois faire un malloc pour la variable de type struct tab et un autre également pour mon tableau de struct cas; ou bien en faisant un malloc pour tab je n'ai pas besoin de faire sur cas ?

Désolé si c'est pas très clair; je ne peux pas mettre le code pour le moment; je ne suis pas sur le même pc ;)
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 09 Fév 2014, 14:24

Quand tu utilises un pointeur.
Si tu utilises la mémoire pointée par le pointeur, il faut que cette mémoire ait été allouée.

Si tu as
Code: Tout sélectionner
struct C{
 int *tab;
}
struct B{
 struct C* c;
}
struct A{
 struct B* b;
}


alors pour pouvoir écrire
Code: Tout sélectionner
A* a;
...
a->b->c->tab[3]=2;

il faut que tu aies fait
Code: Tout sélectionner
A* a=malloc(sizeof(A));
a->b=malloc(sizeof(B));
a->b->c=malloc(sizeof(C));
a->b->c->tab=malloc(sizeof(int)*3);
a->b->c->tab[3]=2;
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 09 Fév 2014, 14:56

D'accord, je le voyais pas comme ça; je ferais des tests dès que possible.

Merci !
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

joel76
Membre Relatif
Messages: 230
Enregistré le: 11 Fév 2013, 17:31

par joel76 » 09 Fév 2014, 18:43

Rockleader a écrit:J'ai fait des traces et l'erreur se produit bien sur un problème similaire; en ce qui concerne le t* je fais un calloc; mais là pour val3 pourquoi ça devrait coincer ? Le compilo ne détecte rien à la compilation
Le programmeur C est supposé savoir ce qu'il fait, la compilation ne s'intéresse qu'à la syntaxe du langage, pas à la sémantique du code.
Le compilo accepte n'importe quoi pourvu que ce soit bien écrit dans le langage considéré.

sylvainc2
Membre Naturel
Messages: 69
Enregistré le: 12 Aoû 2012, 20:22

par sylvainc2 » 09 Fév 2014, 22:39

Ce qui me surprend c'est que le compilateur ne donne pas d'erreur à:
Tableau->val3=0;

La raison est que "Tableau->" veut dire que Tableau est un pointeur, mais en fait c'est une structure de type tab, pas un pointeur à une structure de type tab. Ce n'est pas la même chose.

D'ailleurs j'ai essayé ton code avec mon compilateur et il ne le permet pas. Il faut plutôt écrire: Tableau.val3=0; et pour moi ca marche (pas d'erreur de segmentation).

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 09 Fév 2014, 23:09

sylvainc2 a écrit:Ce qui me surprend c'est que le compilateur ne donne pas d'erreur à:
Tableau->val3=0;

La raison est que "Tableau->" veut dire que Tableau est un pointeur, mais en fait c'est une structure de type tab, pas un pointeur à une structure de type tab. Ce n'est pas la même chose.

D'ailleurs j'ai essayé ton code avec mon compilateur et il ne le permet pas. Il faut plutôt écrire: Tableau.val3=0; et pour moi ca marche (pas d'erreur de segmentation).



Non car c'est bien un pointeur ;)

Voici une partie de mon .c

Code: Tout sélectionner
struct uneCase {
   int val; //valeur de la case
   int vide; //case vide ou pleine
};
struct TableauDynamique {
   Case *t; //tableau de case (valeur numerique + booleene) taille indéfini
   int nbVal; // nb valeur dans le tableau = taille effective
   int size_max; //taille max tableau courant
   int size_init; //taille à la création
};

TableauDynamique tabDynamiqueCreer(int N)
{
   TableauDynamique tab=NULL;
   tab=calloc(N, sizeof(tab));
   if (tab == NULL)
   {
       fprintf(stderr, "Erreur calloc: échec de l'allocation mémoire de la structure\n");
       exit(1); //on quitte si le calloc plante
   }
   tab->nbVal=0;
   tab->size_max=N;//taille max courante
   tab->size_init=N; //taille à la création
   tab->t = calloc(N, sizeof(Case));
   if (tab->t == NULL)
   {
       fprintf(stderr, "Erreur calloc: échec de l'allocation mémoire du tableau\n");
       exit(1); //on quitte si le calloc plante
   }
   //calloc gère l'initialisation à 0
   return tab;
}

void tableauDynamiqueAjouter(TableauDynamique tab, int v)
{
   if(tab->nbVal==tab->size_max) //le cas nbVal >size_max n'est pas possible
   {
      int N=tab->size_init+tab->size_max; //nouvelle taille tableau
      tab->t = realloc(tab->t, N*sizeof(Case)); //on augmente la taille du tableau de la taille initiale + taille courante
      if (tab->t == NULL)
      {
          fprintf(stderr, "Erreur realloc: lors de la réallocation mémoire du tableau\n");
          exit(2); //on quitte si le realloc plante
      }
   }
   int k=0;
   while(tab->t[k]->vide != VIDE) //on s'arrête lorsque l'on trouve la première case vide
   {
      k++;
      printf("test\n");//core dumped test ne s'affiche pas
   }
   tab->t[k]->val=v; //On ajoute v dans la case vide
   tab->t[k]->vide=PLEIN; //On indique case occupé
   tab->nbVal++;
}


Ainsi que le headdeur qui va avec

Code: Tout sélectionner
#define VIDE 0
#define PLEIN 1

typedef struct uneCase* Case;
typedef struct TableauDynamique* TableauDynamique;



TableauDynamique tabDynamiqueCreer(int N);//crée et retourne un tableau de taille N
void tableauDynamiqueAjouter(TableauDynamique tab, int v);//ajouter l'entier v dans la première case vide


Après la remarque de fatal_error j'ai pu corriger mon erreur et désormais ma fonction créatrice de tableau se déroule sans accro.
En revanche j'ai encore une erreur de segmentation sur la fonction d’ajout à l'endroit indiqué en commentaire.
Pourtant dans la fonction créatrice du tableau j'ai bien fait l'allocation de la structure d'une part et du tableau de structure d'autre part. Je ne comprends donc pas d'où peut venir cette nouvelle erreur de segmentation.



Pour Joel: Heureusement alors que je ne suis pas un programmeur et que c'est en faisant des tests et des erreurs que je pourrais prétendre à ce titre. C'est en forgeant que l'on devient forgeron. Je ne peux pas prétendre tout comprendre au C au bout de 4 mois.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 09 Fév 2014, 23:17

tes typedef case* case, très franchement, c'est nulachier.
Appeles les ptrCase ou ne fais pas de typedef, mais la tu masques carrément le type que tu manipules.

D'ailleurs, on croit qu'on store dans le tableau des cases, mais en fait c'est des pointeurs sur case.
Qui dit pointeur dit allocation.

serves you right!
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 09 Fév 2014, 23:34

fatal_error a écrit:tes typedef case* case, très franchement, c'est nulachier.
Appeles les ptrCase ou ne fais pas de typedef, mais la tu masques carrément le type que tu manipules.

D'ailleurs, on croit qu'on store dans le tableau des cases, mais en fait c'est des pointeurs sur case.
Qui dit pointeur dit allocation.

serves you right!


Justement; le but est de protéger le code en masquant le type utilisé à l'utilisateur c'est ce que l'on nous demande de faire. Après c’est vrai que j'ai peut être pas besoin de faire un typedef sur case.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 09 Fév 2014, 23:40

On ne protège pas un code en masquant un type.
On protège un code en masquant les internals.

Les internals, c'est les attributs de ta structure.
On utilise des pointeurs dans la déclaration parce que (détails à part) ca marche.

Quant à la façon dont tu utilises ces typedefs, c'est super crade.
Ca peut se comprendre si tu veux enlever le mot clé struct, mais de feindre un plein objet pour un pointeur... c'est très discutable.
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 09 Fév 2014, 23:52

J'ai du mal avec cette notion de protection de code; je pensais que le fait de séparer les structures dans le .c de leur typedef avec un pointeur dans le .h suffisait à protéger les données; mais apparemment c'est pas ça.

Comment déclarerais tu ses deux structures pour qu'elles soit "protégées" ?
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 10 Fév 2014, 00:18

1: les typedefs n'ont rien à voir avec la protection de code. Les typedefs sont justes des alias.
2: les .h n'ont rien à voir avec la protection de code non plus. Les .h sont justes des fichiers pour les feignants/pour faire joli.
3: la protection de code consiste à utiliser un pointeur.
Code: Tout sélectionner
A.cpp
struct B;
void doSomethingWithB(struct B*);
void test(){
  struct B* b;
  doSomethingWithB(b);
}

gcc -c A.cpp peut compiler.


Code: Tout sélectionner
B.cpp
struct B{
  int b1,b2,...;
}
void doSomethingWithB(struct B* b){

}

gcc -c B.cpp contient le code exécutable de B, B a évidemment accès à tous les internals de ...B

Lorsque tu es dans, A, tu n'as pas besoin de connaitre ce que contient struct B, tu as juste besoin de connaitre le nom des méthodes (uniquement leur déclaration).
Si tu utilises struct B (le type plein) alors le compilateur lit la définition de B, et tu as donc besoin d'avoir défini B dans A.
Mais si tu utilises struct B*, alors le compilateur trouves juste un pointeur et n'a pas besoin de la définition de B.

C'est seulement quand tu compileras ton programme que gcc appèleras le linker, et le linker dira pour A.o: utilises le code de doSomethingWithB qui se trouve dans B.

Conclusion:
Pour protéger tes internals.
Tu utilises un pointeur.

Dans ton fichier qui propose toutes les méthodes, tu as accès a ton type plein (et uniquement dans ce fichier).
Eventuellement, tu écris le prototype des méthodes avec une déclaration de ta structure dans un .h, par exemple
Code: Tout sélectionner
B.h
struct B; //forward declaration
void doSomethingWithB(struct B* b);

A.cpp
#include "B.h"
void test(){
//...
}

Tu remarques que c'est exactement le même code qu'avec les déclration dans le cpp. (tu te rappèles qu'un include ne fait que copier le texte du .h à l'endroit ou ya écrit include)
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 10 Fév 2014, 00:29

Code: Tout sélectionner
B.h
struct B; //forward declaration
void doSomethingWithB(struct B* b);

A.cpp
#include "B.h"
void test(){
//...
}



Je vois que tu déclares la structure dans B.h selon ce modèle; hors en cours on nous as dit que pour protéger on devait déclarer la structure dans le .c

ce que moi j'ai fait au final c'est:

Code: Tout sélectionner
B.h
typedef struct B* Sb; // Je redéfini struct B*
void doSomethingWithB(Sb b);

A.cpp
#include "B.h"
struct B; //forward declaration

void test(){
//...
}
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 10 Fév 2014, 00:42

ben ton cours est faux.
Ou tu l'as mal lu.

passer par un typedef n'est pas nécessaire.
quant à la déclaration tu la fais (au moins) dans le cpp qui utilises le type ou le type pointé.

Ps: un forward declaration dans un cpp n'a que très peu de sens, et certainement aucun dans le cas courant.
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 10 Fév 2014, 01:04

fatal_error a écrit:ben ton cours est faux.
Ou tu l'as mal lu.

passer par un typedef n'est pas nécessaire.
quant à la déclaration tu la fais (au moins) dans le cpp qui utilises le type ou le type pointé.

Ps: un forward declaration dans un cpp n'a que très peu de sens, et certainement aucun dans le cas courant.



Je peux t'assurer que c'est bien ce que l'on nous fait faire.

La dernière fois lorsque l'on faisait une implémentation de pile dynamique; le prof m'a fait mettre la déclaration de structure dans le .c et un typedef sur un pointeur sur la structure dans le .h

J'avoue ne pas avoir compris en quoi cela pouvait protéger les données...

Je demanderais au prof pour avoir des informations complémentaires.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

sylvainc2
Membre Naturel
Messages: 69
Enregistré le: 12 Aoû 2012, 20:22

par sylvainc2 » 10 Fév 2014, 01:43

Rockleader a écrit:...

En revanche j'ai encore une erreur de segmentation sur la fonction d’ajout à l'endroit indiqué en commentaire.
Pourtant dans la fonction créatrice du tableau j'ai bien fait l'allocation de la structure d'une part et du tableau de structure d'autre part. Je ne comprends donc pas d'où peut venir cette nouvelle erreur de segmentation.
....


Le probleme est que tu alloues de l'espace pour les pointeurs avec l'instruction:

tab->t = calloc(N, sizeof(Case));

dans la fonction tabDynamiqueCreer(int N), mais tu n'alloues pas l'espace de la structure a laquelle chacun de ses pointeurs pointe.

Donc quand tu référes à :

while (tab->t[k]->vide != VIDE)

chaque tab->t[k] (pour K allant de 0 à N-1) n'a pas été alloué et ca plante.

Il faut ajouter un calloc dans une boucle de 0 à N-1 avant d'arriver là.

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 20:42

par Rockleader » 10 Fév 2014, 02:20

Je comprends ce que tu veux dire; mais je ne comprends pas pourquoi le calloc sur tab->t ne suffit pas vu qu'on lui donne justement une taille N


A priori je fais un calloc sur tab qui est donc le pointeur de structure
Puis je fais un calloc sur tab->t qui pointe sur un tableau de structure. Je pensais que cette étape était suffisante et nous permettait d'éviter de boucler sur chaque k de 0 à N-1. A tord d'après ce que tu viens de me dire.


Sa commence à faire beaucoup de free à enchaîner tout ça *-*
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 14:00

par fatal_error » 10 Fév 2014, 10:19

poste de 21h17, deuxième paragraphe.
la vie est une fête :)

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 09:03

par ampholyte » 10 Fév 2014, 11:05

Bonjour,

Un petit conseil pour bien organiser ton code.

Je te conseille de créer deux fonctions, une te permettant d'allouer ta structure et une pour libérer la totalité de cette structure.

Comme il a été dit précédement, dès que tu utilises un pointeur, il doit forcément pointer vers une emplacement mémoire qui a été allouée.

Il y a plusieurs possibilités :

1) Depuis une variable déja déclarée
Code: Tout sélectionner
 
int a = 3;
int *b = &a; /* a est déjà alloué lors de la déclaration donc pas besoin de free*/


Voici un autre exemple plus intéressant :

Code: Tout sélectionner
char chaine[] = "J'aime les pommes";
char *ptr = NULL;
int cpt = 0;

ptr = chaine[0]; /* On pointe sur le premier caractère de chaine */

/* On déplace le pointeur tant que le caractère pointé est différent de \0 */
while (*ptr != '\0') {
    if (*ptr == 'm') { /* On compte le nombre de m dans la chaine */
            cpt++;
    }
    ptr++;
}
printf("%d\n", cpt);


Ici pas besoin de free puisque chaine est déjà déclarée depuis le tableau de char chaine.

2) Depuis une fonction disponible depuis une lib (c'est pour cela que la lecture du man est importante !)
Code: Tout sélectionner
char chaine[] = "Mange une pomme";
char *toto = NULL;
toto = strdup("chaine"); /* nécessite un free */
free(chaine);
chaine = NULL;


3) A la main
Code: Tout sélectionner
int *tab = NULL;
tab = calloc(5, sizeof(int)); /* Allocation manuelle nécessite un free */
free(tab);
tab = NULL;

 

Retourner vers ϟ Informatique

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 4 invités

Tu pars déja ?



Fais toi aider gratuitement sur Maths-forum !

Créé un compte en 1 minute et pose ta question dans le forum ;-)
Inscription gratuite

Identification

Pas encore inscrit ?

Ou identifiez-vous :

Inscription gratuite