PDA

Voir la version complète : [AIDE] Projet langage C



PolluXxX
15/06/2009, 19h31
Bonsoir les canards!

Voilà, j'ai un projet scolaire en C sur le feu en ce moment, et il y a une partie que je n'arrive pas à coder. En très gros, je dois créer un programme qui prend en entrée une expression logique (genre A&B|C) et qui affiche une table de vérité par après.
En fait, pour vous expliquer, le mieux est de vous mettre le lien vers le .pdf expliquant le sujet: http://dl.free.fr/qk7fj8NeC.

J'ai déjà fini le premier module, le module variable, plutôt facilement, mais je bloque complètement sur le second module, le module expression, vu que j'ai du mal avec les arbres.

Je l'ai déjà bien commencé, mais je bloque sur l'affichage (la fonction display_expression). Je ne vois pas du tout comment placer les parenthèses et autre.

Voilà mon début de code pour cette partie:

#include <stdio.h>
#include <stdlib.h>



Typedef struct item
{
char label;
*item gauche;
*item droit;
} item;


Void inventorier_variables(item *expr)
{
if (expr!=NULL)
{
inventorier_variables(expr->gauche);
ajouter_variable(expr->label);
inventorier_variables(expr->droit);
}
}

Void display_expression(item *expr)
{
if (expr!=NULL)
{
printf("(");
display_expression(expr->gauche);
display_expression(expr->droit);
printf(")");
}
}

*item creer_variable(char v){
item var;
var.label = v;
var.gauche = NULL;
var.droit = NULL;

return *var;
}

*item creer_diaddique(char o, item *op1, item *op2) ;
item var;
if(o == "&" || o == "|" || o == "^"){
var.label = o;
gauche = op1;
droit = op2;
return var;
}else{
return NULL;
}
}

*item creer_monaddique(char o, item *op1){
item var;
if(o=="!"){
var.label = o;
gauche = op1;
return var;
}else{
return NULL;
}
} Donc voilà, pour ceux qui ont envie de se prendre un peu la tête, et éventuellement m'aider, ça serait nickel! Ou me conseiller, me corriger, fin tout ce qui pourrait me faire avancer pour ce projet!

Un grand merci d'avance! :lol:

DaP
15/06/2009, 23h31
*item creer_variable(char v){
item var;
var.label = v;
var.gauche = NULL;
var.droit = NULL;

return *var;
}

L'adresse de var n'est plus valide après l'appel de la fonction vu qu'elle est locale. En plus c'est & qui renvoie l'adresse, pas *. Utilise l'allocation dynamique ou initialise un item existant qui est passé par pointeur.

rOut
15/06/2009, 23h51
Bin déjà tu vas avoir des problèmes avec le retour de tes fonctions, dans leur déclaration, tu renvoies un pointeur vers un item (*item, pour creer_monaddique par exemple), et tu fais un "return var", var étant un item.

La déclaration de ta variable var étant locale à ta fonction, lorsque la fonction se terminera, la mémoire utilisée pour stocker cette variable sera libérée. Et le pointeur vers cette zone de mémoire, que tu viens de renvoyer en retour, ne sera donc pas valide du tout, et tu vas avoir de belles segmentation fault (au mieux, au pire ca plantera pas mais ca fera n'importe quoi) lorsque tu vas tenter de l'utiliser...

Vu le pdf, tu n'as pas trop le choix sur le prototype de tes fonctions, du coup je dirais que tu est obligé de gérer l'allocation de la mémoire pour tes item. Tu as le choix entre faire un gros tableau de taille fixe avec tout un tas d'items non initialisés, près à être utilisés, mais donc avec un maximum d'items utilisables en même temps. Ou alors d'allouer la mémoire dynamiquement pour tes item, à coup de malloc (dans tes fonctions creer_*). Tout en pensant bien à libérer la mémoire à la fin, ou lorsque tu n'as plus besoin de tes items, avec des free.

Pour l'inventaire des variables, je pense qu'il faut que tu vérifie que le label de ton item courant correspond bien à une variable et non à un monadique / diaddique avant d'appeler ajouter_variable.

Même chose pour display_expression, il faut que tu vérifie la nature de l'item courant, si c'est une variable, tu affiches juste "[" label "]", si c'est un diaddique, "(" gauche label droite ")", si c'est monadique label gauche.

Pour info, ton arbre syntaxique aura certainement la forme suivante pour l'expression donnée en exemple. Chaque noeud étant un item :

&
/ \
A |
/ \
B !
/
C
Et tu commences le parcours par la racine, c'est à dire un item ayant pour label "&", pour fils gauche "A" et fils droit "|", etc...

PS : Quelques points de syntaxe, lorsque tu as une variable de type "item" et que tu veux obtenir un pointeur dessus, il faut que tu utilises l'opérateur & (qui correspond à l'adresse mémoire de ta variable, et donc à la valeur d'un pointeur). L'opérateur * est dans le sens inverse, lorsque tu as un pointeur et que tu veux obtenir la valeur pointée.
Deuxième point, les char sont entre simple quote ', lorsque tu fais la comparaison. Les double quote " sont pour des chaines de caractères, ta comparaison va foirer à tous les coups comme ça :p.

Edit: Tu oublies de préciser var., devant l'assignation de gauche et droite aussi :).
Edit²: Tiens, le forum fout des majuscules tout seul après un "." :tired:.

PolluXxX
16/06/2009, 17h01
Bon, apparemment, je n'ai pas bien saisi le concept de pointeur, qui semble plus ou moins important...

En fait, une utilisation correcte de creer_diaddique, ça serait de ce genre?


*item creer_diaddique(char o, item *op1, item *op2) ;
item var;
if(o == '&' || o == '|' || o == '^'){
var.label = o;
gauche = op1;
droit = op2;
return &var;
}else{
return NULL;
}
}

Et je dois aussi ici utiliser le malloc?
Il faut que je regarde le malloc, je me souviens que le prof en avait parlé, je dois avoir ça qui traîne.
Bon, je vais déjà essayer de corriger le peu de code que j'ai avant de me lancer dans l'affichage, je vous poste la suite dans la soirée!
Merci à vous.

la_bosse
16/06/2009, 17h49
Oui il faut utiliser le malloc => item var ne sera valable que le temps de ta fonction.
Il faut que tu fasses une allocation dynamique => item* var = malloc(sizeof(item));

Une astuce connue des developpeurs => un pointeur, appelle le pVar plutot que var.
Tu sais pQqch est un pointeur et *pQqch est une variable (mentalement *p = rien donc *pQqch = Qqch... C'est une variable... Tu vois ce que je veux dire....).


En gros, tu declares :

{
MonType var;
...
}
Si tu as besoin de var le temps du scope de { }. La memoire est prise sur la pile d'execution du programme. Par contre, tu as besoin de garder ta variable plus longtemps, tu fais une allocation dynamique => malloc. Tu reserves une zone memoire qqpart, tu recuperes donc un pointeur (l'addresse de ta zone memoire). C'est ta zone a toi... Elle reste allouee (et "t'appartient" donc) tant que tu n'as pas fait un free dessus.

Sp1d3r
16/06/2009, 20h24
*item creer_diaddique(char o, item *op1, item *op2) ;
item var; #
if(o == '&' || o == '|' || o == '^'){
var.label = o;
gauche = op1;
droit = op2;
return &var; #
}else{
return NULL;
}
}

1) Pourquoi il y a un problème avec ta variable "var" ?

Tu la crées dans une fonction sans allouer de la mémoire (via malloc par exemple), donc elle est dans la pile. Quand tu récupères l'adresses de var, c'est une adresse situé dans la pile que tu obtiendras.

Or, même si par miracle "&var" peut toujours correspondre à ce que tu souhaites un temps donné, cet espace mémoire va finir par se faire écraser par autre chose, vu qu'il est alloué à la pile.

2) si tu fais un "malloc", tu alloues de la mémoire dans un autre espace, le tas. Et là l'espace est alloué et réservé jusqu'à ce que tu le libère ("free") explicitement. Malloc renvoit un pointeur tout le temps, qui vaut NULL si tu es maudit (:ninja:) ou l'adresse de l'espace réservé. Pour des infos plus concrètes et exactes, "man malloc". :rolleyes:

Concrètement ta fonction doit ressembler à ça :



*item creer_diaddique(char o, item *op1, item *op2) ;
*item var=malloc(sizeof(item));
if(o == '&' || o == '|' || o == '^'){
var->label = o;
var->gauche = op1;
var->droit = op2;
return var; #
}else{
return NULL;
}
}


Pointeur->champ est l'équivalent de *(pointeur).champ

rOut
16/06/2009, 21h00
En même temps je ne sais pas si la "pile" parle beaucoup à Polluxxx, tout dépends de ce qu'on lui a expliqué en cours :p

olih
16/06/2009, 21h07
Il n'y a que moi que ça choque le *item en valeur de retour de fonction ? (et pas item*)

la_bosse
16/06/2009, 21h45
En fait, non... Moi aussi ca m'a choque...
Un type pointeur s'ecrit item*.

Pour la pile, c'est la memoire que t'alloue ton OS pour derouler ton prog. En gros, tu empiles des fonctions, qui chacune ont un contexte qui occupe une zone memoire (dont les variables locales, les parametres d'appels...). Je simplifie pas mal.

D'ou le fameux stack overflow, si tu consomes trop de memoire dans ta pile. Par exemple :
Toto() {
Printf("J'empile");
Toto();
}
Stack overflow garanti ! C'est pourquoi les appels recursifs sont interdit dans certains prog sensible...

Sans vouloir faire le prof... Prendre un peu de temps pour comprendre ces concepts, et tu as compris 50 % du C.

PolluXxX
16/06/2009, 21h54
Ok! Je commence à comprendre mieux certaines choses vis-à-vis des pointeurs.
Pour le *item à la place de item*, c'est encore moi. J'ai voulu changer et j'me suis emmêlé les pinceaux dans les pointeurs.

Pour la pile, je vois ce que c'est. J'ai fait de l'assembleur, et là, on l'a beaucoup utilisé, donc je vois ce que ça représente.

On aurait donc ce genre de truc:



Item* creer_diaddique(char o, item *op1, item *op2) ;
item* var=malloc(sizeof(item));
if(o == '&' || o == '|' || o == '^'){
var->label = o;
var->gauche = op1;
var->droit = op2;
return var; #
}else{
return NULL;
}
}


Ce qui m'amène donc à changer certaines choses dans mon programme. Je m'en vais réécrire les fonctions de base, et je reviendrais sûrement pour ma procédure d'affichage avec laquelle j'ai encore un peu de mal pour passer de sa visualisation à son code.
En tout cas, merci!

Au fait, au passage, vous auriez un éditeur de C sympathique (Linux/Windows)? Disons qu'Emacs m'énerve un peu parfois.

rOut
16/06/2009, 21h58
Eclipse CDT ? :ninja:

PolluXxX
16/06/2009, 22h02
Voilà mon premier jet après correction:


#include <stdio.h>
#include <stdlib.h>

Typedef struct item
{
char label;
item* gauche;
item* droit;
} item;


Void inventorier_variables(item *expr)
{
if (expr!=NULL)
{
inventorier_variables(expr->gauche);
ajouter_variable(expr->label);
inventorier_variables(expr->droit);
}
}


Item* creer_variable(char v){
item* var=malloc(sizeof(item));
var->label = v;
var->gauche = NULL;
var->droit = NULL;

return var;
}

Item* creer_diaddique(char o, item *op1, item *op2) ;
item* var=malloc(sizeof(item));
if(o == '&' || o == '|' || o == '^'){
var->label = o;
var->gauche = op1;
var->droit = op2;
return var; #
}else{
return NULL;
}
}

Item* creer_monaddique(char o, item *op1){
item* var=malloc(sizeof(item));
if(o=='!'){
var->label = o;
var->gauche = op1;
return var;
}else{
return NULL;
}
}

Il ne me reste plus qu'à me pencher sérieusement sur l'affichage.
En tout cas, n'hésitez pas à faire le prof, ça me fera du bien d'en avoir un...(Disons que mon prof de C est bien sympathique, mais niveau pédagogie, c'est proche du néant)

rOut
16/06/2009, 22h05
En fait, Eclipse CDT, sous Linux ca marche nickel, par contre sous windows je ne sais pas comment ça tourne au niveau du compilateur / linker.

Si t'es sous Windows, j'aurais tendance à suggérer Visual Studio 2008 Express, qui est gratuit et qui marchera sans doute "mieux".

---------- Post ajouté à 22h05 ----------

Ta fonction inventorier_variables appelle ajouter_variable, quel que soit la valeur de label, tu vas donc faire cet appel aussi bien pour des variables que pour des monadiques et diaddiques. Si la vérification se fait dans ajouter_variable, pas de problème, sinon ça risque de coincer.

Sinon, t'as des variations entre Item et item dans ton code -- ha, c'est le forum qui fout des majuscules, peut être --, et t'as aussi un # qui traine.

olih
16/06/2009, 22h10
Voilà mon premier jet après correction:


#include <stdio.h>
#include <stdlib.h>

Typedef struct item
{
char label;
*item gauche;
*item droit;
} item;


Void inventorier_variables(item *expr)
{
if (expr!=NULL)
{
inventorier_variables(expr->gauche);
ajouter_variable(expr->label);
inventorier_variables(expr->droit);
}
}


Item* creer_variable(char v){
item* var=malloc(sizeof(item));
var->label = v;
var->gauche = NULL;
var->droit = NULL;

return var;
}

Item* creer_diaddique(char o, item *op1, item *op2) ;
item* var=malloc(sizeof(item));
if(o == '&' || o == '|' || o == '^'){
var->label = o;
var->gauche = op1;
var->droit = op2;
return var; #
}else{
return NULL;
}
}

Item* creer_monaddique(char o, item *op1){
item* var=malloc(sizeof(item));
if(o=='!'){
var->label = o;
var->gauche = op1;
return var;
}else{
return NULL;
}
}Il ne me reste plus qu'à me pencher sérieusement sur l'affichage.
En tout cas, n'hésitez pas à faire le prof, ça me fera du bien d'en avoir un...(Disons que mon prof de C est bien sympathique, mais niveau pédagogie, c'est proche du néant)
Je sais pas si c'est dans ton code (ou une étrangeté du forum :ninja:) mais le C est sensible à la casse. Du coup autant éviter des Void ou Item (qui sont différent de void et item).
Pareil dans ta structure, une variable pointeur sur item s'écrit item* monpointeur et non *item monpointeur.
Enfin tu alloues la mémoire pendant la définition de la variable (pour être rigoureux, il faudrait d'ailleurs tester le retour du malloc, on est en C que diable ^_^), il ne faut pas oublier de la libérer dans le cas ou tu ne t'en sers pas (dans la clause else).

rOut
16/06/2009, 22h12
Je sais pas si c'est dans ton code (ou une étrangeté du forum :ninja:) mais le C est sensible à la casse. Du coup autant éviter des Void ou Item (qui sont différent de void et item).
C'est ce que je disais aussi, mais j'ai remarqué hier que le forum rajoutait des majuscules tout seul après un. Donc je pense que c'est lui aussi qui fout toutes les premières lettres d'une ligne avec une majuscule...

Un test pour voir. :o

Edit: Bah ouais, si on saute une ligne ça ajoute aussi tout seul la majuscule... Spa top pour le code ça.

PolluXxX
16/06/2009, 22h14
Alors, un coup d'oeil à ajouter_variable me montre que je ne vérifie pas ça.
Du coup, je dois l'inclure ici.

Est-ce que quelque chose comme ça

void inventorier_variables(item *expr)
{
if (expr!=NULL)
{
inventorier_variables(expr->gauche);
if(expr->label > 64 && expr->label < 91){
ajouter_variable(expr->label);
}
inventorier_variables(expr->droit);
}
}
Ca peut fonctionner? Parce qu'il paraît que les caractères ne sont qu'un code ASCII.
Et pour l'alphabet majuscule, A= 65 et Z = 90...

Et j'ai édité mon code juste au dessus pour la déclaration de la structure!

rOut
16/06/2009, 22h17
Ouais, ça devrait rouler.

newbie06
16/06/2009, 22h38
Y'a un problème ici :


Item* creer_monaddique(char o, item *op1){
item* var=malloc(sizeof(item));
if(o=='!'){
var->label = o;
var->gauche = op1;
return var;
}else{
return NULL;
}
}

En fait y'en a même trois...

1. Il se passe quoi avec la mémoire allouée si on n'a pas de '!' ?
2. Un item contient 3 champs ; malloc n'initialise rien
3. Et s'il n'y a plus de mémore ?

EDIT : y'en a un 4ème, mais là je pinaille, on écrit "monadique" (un seul 'd') et "dyadique" (un 'y') :p

rOut
16/06/2009, 22h47
EDIT : y'en a un 4ème, mais là je pinaille, on écrit "monadique" (un seul 'd') et "dyadique" (un 'y') :p
Y'a une erreur dans le pdf de l'énoncé alors, laisse tomber cet exo... :rolleyes:

PolluXxX
16/06/2009, 22h49
Y'a un problème ici :


Item* creer_monaddique(char o, item *op1){
item* var=malloc(sizeof(item));
if(o=='!'){
var->label = o;
var->gauche = op1;
return var;
}else{
return NULL;
}
} En fait y'en a même trois...

1. Il se passe quoi avec la mémoire allouée si on n'a pas de '!' ?
2. Un item contient 3 champs ; malloc n'initialise rien
3. Et s'il n'y a plus de mémore ?

EDIT : y'en a un 4ème, mais là je pinaille, on écrit "monadique" (un seul 'd') et "dyadique" (un 'y') :p

Alors, je réfléchis pour voir si j'ai compris...
1. Rajouter un free (à voir comment on utilise cette fonction) dans le else?
2. Rajouter var->droit = NULL?
3. Euuuh là, aucune idée!

4. Les noms sont fournis par le prof. Je ne peux pas y toucher parce qu'en fait, le module que j'écris est utilisé par d'autres modules, écrits par le prof mais que j'utilise pour la compilation. Si je change les noms, plus rien ne marchera...Mais je lui dirai, parce que c'est naze de faire des fautes, surtout en les glissant sournoisement dans des mots que je ne comprends pas.

rOut
16/06/2009, 22h53
Alors, je réfléchis pour voir si j'ai compris...
1. Rajouter un free (à voir comment on utilise cette fonction) dans le else?
2. Rajouter var->droit = NULL?
3. Euuuh là, aucune idée!
1. Tu vas défaire (libérer la mémoire) ce que tu viens de faire, c'est un peu inutile nan ? Autant éviter d'allouer si ya pas besoin.
TIP: Tu n'es pas obligé d'initialiser ta variable tout de suite lorsque tu la déclares.
3. Malloc renvoie NULL lorsqu'il n'y a plus de place il me semble... Même s'il y a peu de chance pour que ça arrive un jour dans ton programme. ^_^

Edit: Et puis du coup t'as le même problème d'allocation de mémoire dans creer_diaddique.

PolluXxX
16/06/2009, 22h58
Correction!


Item* creer_monaddique(char o, item *op1){
item* var;
if(o=='!' && malloc(sizeof(item))!= NULL){
var = malloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Je commence à comprendre...Je crois :(
Et oui, je modifierai aussi les autres créer_****

rOut
16/06/2009, 23h01
Ouais, mais alors dans ton if, tu alloues un petit bout de mémoire, tu testes effectivement si ça a marché, mais ensuite... Ce petit bout, il devient quoi ? Il reste perdu dans le noir, parce que personne ne l'utilise : tu alloue ta variable var dans un autre bout de mémoire - forcément pas le même - juste après.

Sp1d3r
16/06/2009, 23h06
Déjà un truc, en C tu n'alloues jamais quelque chose auquel tu ne peux pas accéder via une variable. Sinon, tu as une fuite mémoire, systématiquement. Fuite mémoire = mémoire perdu car non libérable.

Pour le pinaillage point 3 de newbie06, en fait il faut que tu testes après le malloc si le pointeur vaut null, via un "assert" (man assert :siffle:) par exemple.

De plus pour être plus précis encore, c'est pas parce que ton n-ième malloc est passé que ton (n+1)-ème passera. Donc tu peux pas vérifier comme ça à priori que tu auras encore de la place pour le malloc.

newbie06
16/06/2009, 23h18
3. Malloc renvoie NULL lorsqu'il n'y a plus de place il me semble... Même s'il y a peu de chance pour que ça arrive un jour dans ton programme. ^_^
Ca c'est très mal : c'est avec de telles idées qu'on se retrouve avec des OS qui font des BSOD :p La programmation défensive est extrêmement importante à moins que l'on puisse prouver que c'est inutile... Et que rien ne changera dans le programme qui pourrait modifier les conditions prouvant que c'est inutile.

Si je me souviens bien, tu peux limiter la taille de la zone de données d'un processus Linux. Ca permettrait de vérifier que la gestion de problèmes de mémoire est faite à peu près correctement :)

PolluXxX
16/06/2009, 23h20
Ok, donc je supprime la condition dans le if.
Je voulais juste voir, mais je n'avais pas pensé à la création d'un espace si utilisé en condition...

Et si...On considère qu'on aura toujours de la place? :rolleyes: :siffle:

Sp1d3r
16/06/2009, 23h24
C'est sûr mais à mon avis si son prof s'attendait à ce qu'il gère ça, il serait au courant. :ninja:

---------- Post ajouté à 23h24 ----------


Ok, donc je supprime la condition dans le if.
Je voulais juste voir, mais je n'avais pas pensé à la création d'un espace si utilisé en condition...

Et si...On considère qu'on aura toujours de la place? :rolleyes: :siffle:

En fait, tu fais le malloc, et dans la ligne d'après tu vérifie que le pointeur sur lequel tu as fait le malloc ne vaut pas null. Si c'est le cas, dans ce cas là t'arrêtes tout, il y a un soucis. :)
Pour faire ça, il y a "
Assert(pointeur!=null) ;
" par exemple.

PolluXxX
16/06/2009, 23h26
C'est sûr mais à mon avis si son prof s'attendait à ce qu'il gère ça, il serait au courant. :ninja:

Non mais je conçois tout à fait que la problématique est importante et intéressante.
Sauf que je manque cruellement de temps en ce moment, et arriver aux limites de l'énoncé sera déjà très bien je pense.
Mais toutes les problématiques que vous m'énoncez sont intéressantes, même si je n'irai parfois pas jusqu'à l'implémentation...

newbie06
16/06/2009, 23h34
Il suffit de "wrapper" le malloc :


Void *my_malloc(size_t size)
{
void *p = malloc(size);

if (p == NULL && size != 0) {
fputs("Plus de mémoire.\n", stderr);
exit(1);
}

return p;
}
Et voilà, au lieu d'utiliser malloc dans ton code, tu utilises my_malloc :)

Le test de size non nul est requis car certains malloc renvoient NULL pour une taille nulle, ce qui n'indique dans ce cas pas une erreur.

rOut
16/06/2009, 23h46
Ca c'est très mal : c'est avec de telles idées qu'on se retrouve avec des OS qui font des BSOD :p La programmation défensive est extrêmement importante à moins que l'on puisse prouver que c'est inutile... Et que rien ne changera dans le programme qui pourrait modifier les conditions prouvant que c'est inutile.

Si je me souviens bien, tu peux limiter la taille de la zone de données d'un processus Linux. Ca permettrait de vérifier que la gestion de problèmes de mémoire est faite à peu près correctement :)
Oui oui, je sais bien, c'était un peu pour plaisanter, je ne fais pas ce que je dis bien entendu :o.

---------- Post ajouté à 23h46 ----------

Par contre je pense que c'est dans ce genre de subtilité que le prof va faire la différence. L'énnoncé a l'air tout à fait classique, et je pense vraiment qu'il s'attends à ce que tout le monde ai a peu près le même code.

La différence se faisant entre ceux qui ont maîtrisé leur gestion de la mémoire (avec des mallocs / free bien gérés pour contrôler l'allocation, s'assurer que chaque allocation fera ensuite l'objet d'une libération et éviter toutes fuites de mémoire, ou avec un gros tableau alloué statiquement et servant de "pool" d'item) et ceux qui ont un code qui va écrire sur les plate-bandes du voisin.

olih
16/06/2009, 23h48
En fait ça gène vraiment les majuscules automatiques pour du code :(

rOut
16/06/2009, 23h50
En fait ça gène vraiment les majuscules automatiques pour du code :(
J'ai fait la remarque là bas :
http://forum.canardpc.com/showthread.php?p=2204140#post2204140

PolluXxX
17/06/2009, 08h46
Bon, j'ai continué un peu. J'ai cependant quelques questions:
Ca représente quoi cette ligne:

void *p = malloc(size);
Avec le return p à la fin?

Voilà la continuité de mon programme, corrigé avec le my_alloc.


#include <stdio.h>
#include <stdlib.h>

//A partir d'ici!!!!!!!!!!!

Typedef struct item
{
char label;
item* gauche;
item* droit;
} item;


Void inventorier_variables(item *expr)
{
if (expr!=NULL){
inventorier_variables(expr->gauche);
if(expr->label > 64 && expr->label < 91){
ajouter_variable(expr->label);
}
inventorier_variables(expr->droit);
}
}


Item* creer_variable(char v){
item* var;
if(v > 64 && v < 91){
var=my_alloc(sizeof(item));
var->label = v;
var->gauche = NULL;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Item* creer_diaddique(char o, item *op1, item *op2) ;
item* var;
if(o == '&' || o == '|' || o == '^'){
var=my_alloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = op2;
return var;
}else{
return NULL;
}
}

Item* creer_monaddique(char o, item *op1){
item* var;
if(o=='!'){
var=my_alloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Void *my_malloc(size_t size)
{
void *p = malloc(size);
if (p == NULL && size != 0) {
fputs("Plus de mémoire.\n", stderr);
exit(1);
}
return p;
}


Je ne me suis toujours pas occupé de l'affichage, je m'y pencherai plus ce soir, mais j'attends d'être sûr que le reste est bon!

newbie06
17/06/2009, 10h36
Bon, j'ai continué un peu. J'ai cependant quelques questions:
Ca représente quoi cette ligne:

void *p = malloc(size); Avec le return p à la fin?
C'est la meme chose que

void *p;
p = malloc(size);

la_bosse
17/06/2009, 10h53
Au fait, au passage, vous auriez un éditeur de C sympathique (Linux/Windows)? Disons qu'Emacs m'énerve un peu parfois.

Je pense que ca n'est pas ton probleme en ce moment (je comprends que tu es presse par le temps), mais un conseil qui ne vaut sans doute rien... Si tu as un pied dans emacs, mets y l'autre... Avec le temps, j'ai pas trouve mieux de mon cote (a part eclipse pour faire du java sans doute...). Mais le prix d'entree est effectivement un peu cher... (si y a vraiment des trucs qui t'enerves, on peut resoudre ca sans doute...)

---------- Post ajouté à 10h49 ----------


Déjà un truc, en C tu n'alloues jamais quelque chose auquel tu ne peux pas accéder via une variable. Sinon, tu as une fuite mémoire, systématiquement. Fuite mémoire = mémoire perdu car non libérable.

Pour le pinaillage point 3 de newbie06, en fait il faut que tu testes après le malloc si le pointeur vaut null, via un "assert" (man assert :siffle:) par exemple.

De plus pour être plus précis encore, c'est pas parce que ton n-ième malloc est passé que ton (n+1)-ème passera. Donc tu peux pas vérifier comme ça à priori que tu auras encore de la place pour le malloc.

Je pinaille, mais si on regarde de pres, beaucoup de programme C se contentent de ne jamais liberer la memoire... C'est le cas des programmes qui moulinent un truc et sortent un resultat derriere... Typiquement un compilo. L'explication est simple. Liberation memoire = complexite, possibilite de bugs... Dans certains domaines (aeronotique par exemple), il faut certifier le code... Chaque ligne de code doit etre justifie... Pas de free... Pas de justification. J'ai un fichier en entree, j'obtiens un fichier en sortie... Pas besoin de liberer de la memoire... Surtout sur les machines actuelles (bon c'est plus pedagogique de liberer sans doute...).

---------- Post ajouté à 10h53 ----------



Voilà la continuité de mon programme, corrigé avec le my_alloc.


...
Je ne me suis toujours pas occupé de l'affichage, je m'y pencherai plus ce soir, mais j'attends d'être sûr que le reste est bon!

Ca m'a l'air bien.

newbie06
17/06/2009, 11h00
Je pinaille, mais si on regarde de pres, beaucoup de programme C se contentent de ne jamais liberer la memoire... C'est le cas des programmes qui moulinent un truc et sortent un resultat derriere... Typiquement un compilo. L'explication est simple. Liberation memoire = complexite, possibilite de bugs... Dans certains domaines (aeronotique par exemple), il faut certifier le code... Chaque ligne de code doit etre justifie... Pas de free... Pas de justification. J'ai un fichier en entree, j'obtiens un fichier en sortie... Pas besoin de liberer de la memoire... Surtout sur les machines actuelles (bon c'est plus pedagogique de liberer sans doute...).
Petit hors-sujet : certaines contraintes de l'aeronautique sont d'une betise sans nom ; il faut assurer une couverture MC/DC du code et justifier ce qui n'est pas couvert par les tests ; du coup programmer defensivement devient tres penible, car il faut expliquer pourquoi du code qui teste une erreur potentielle n'est pas execute ; du coup, on met moins de code defensif. Resultat : du soft potentiellement moins fiable. Super hein ?

rOut
17/06/2009, 11h01
Bon, j'ai continué un peu. J'ai cependant quelques questions:
Ca représente quoi cette ligne:

void *p = malloc(size); Avec le return p à la fin?
Le void* décrit un pointeur vers n'importe quel type de structure en mémoire, cela permet d'allouer un pointeur vers une zone de mémoire de la taille que tu lui as donné, sans que ton pointeur soit contraint à un certain type de données. Tu peux donc allouer une portion de mémoire de la taille d'un "item" et utiliser le pointeur retourné par my_malloc comme un item* (le cast depuis void* vers n'importe quel autre pointeur se fait automatiquement). Si ton pointeur p était de type item*, tu ne pourrais pas utiliser ta fonction my_malloc pour allouer autre chose que des item*.

Et puis aussi, c'est my_malloc, et pas my_alloc dans ton code :p.

la_bosse
17/06/2009, 11h07
Petit hors-sujet : certaines contraintes de l'aeronautique sont d'une betise sans nom ; il faut assurer une couverture MC/DC du code et justifier ce qui n'est pas couvert par les tests ; du coup programmer defensivement devient tres penible, car il faut expliquer pourquoi du code qui teste une erreur potentielle n'est pas execute ; du coup, on met moins de code defensif. Resultat : du soft potentiellement moins fiable. Super hein ?
Euh, pour le coup je suis bien d'accord. Pour l'absence de free sur un compilo, qui ne bouffera que des fichiers de taille raisonable, la ca me choque moins...
Par contre si un jour tu file des fichiers genere a ce meme compilo... Tu peux avoir des surprises... Et un certain compilo C++ microsoft avait de drole de limitations en 2001... Aujourd'hui je sais pas, mais a l'epoque, c'etait maxi 255 parentheses....

DaP
17/06/2009, 19h19
Pour le pinaillage point 3 de newbie06, en fait il faut que tu testes après le malloc si le pointeur vaut null, via un "assert" (man assert :siffle:) par exemple.

Assert ne remplace pas la gestion des erreurs. Il sera sûrement viré en release, et le message d'erreur ne ressemblera à rien.

PolluXxX
17/06/2009, 21h57
Bon...J'ai fait un premier essai de display_expression, et j'ai essayé de compiler.
Je vous file mon code:



#include <stdio.h>
#include <stdlib.h>

//A partir d'ici!!!!!!!!!!!

Typedef struct item
{
char label;
item* gauche;
item* droit;
} item;


Void inventorier_variables(item *expr)
{
if (expr!=NULL){
inventorier_variables(expr->gauche);
if(expr->label > 64 && expr->label < 91){
ajouter_variable(expr->label);
}
inventorier_variables(expr->droit);
}
}


Item* creer_variable(char v){
item* var;
if(v > 64 && v < 91){
var=my_malloc(sizeof(item));
var->label = v;
var->gauche = NULL;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Item* creer_diaddique(char o, item *op1, item *op2){
item* var;
if(o == '&' || o == '|' || o == '^'){
var=my_malloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = op2;
return var;
}else{
return NULL;
}
}

Item* creer_monaddique(char o, item *op1){
item* var;
if(o=='!'){
var=my_malloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Void *my_malloc(size_t size)
{
void *p = malloc(size);
if (p == NULL && size != 0) {
fputs("Plus de mémoire.\n", stderr);
exit(1);
}
return p;
}


Void display_expression(item *expr)
{

if(expr->gauche==NULL){
printf("%c",'(');
}
display_expression(expr->gauche);
if(expr->gauche==NULL){
printf("%c",')');
}


switch(expr->label){
case '&': printf("%s"," AND ");break;
case '|': printf("%s"," OR ");break;
case '^':printf("%s"," XOR ");break;
case '!': printf("%s"," NOT ");break;
default: printf("%c",expr->label);break;
}


if(expr->droit==NULL){
printf("%c",'(');
}
display_expression(expr->droit);
if(expr->droit==NULL){
printf("%c",')');
}
}


Je ne suis pas encore sûr du display, mais à la compilation, j'ai un énorme problème...
Il m'affiche des erreurs de pointeurs:
Expression.c:44: erreur: ‘item’ has no member named ‘gauche’
Expression.c:32: erreur: ‘item’ has no member named ‘droit’

Ou encore :
Expression.c:55: attention : assignment makes pointer from integer without a cast

Comment ça se fait? Ca me le fait pour chaque appel d'un membre de expr...

EDIT : je rajoute aussi deux erreurs sur le my_malloc:

Expression.c:65: erreur: conflicting types for ‘my_malloc’
Expression.c:29: erreur: previous implicit declaration of ‘my_malloc’ was here


Comprends rien :emo:

olih
17/06/2009, 22h18
Bon...J'ai fait un premier essai de display_expression, et j'ai essayé de compiler.
Je vous file mon code:



#include <stdio.h>
#include <stdlib.h>

//A partir d'ici!!!!!!!!!!!

Typedef struct item
{
char label;
item* gauche;
item* droit;
} item;


Void inventorier_variables(item *expr)
{
if (expr!=NULL){
inventorier_variables(expr->gauche);
if(expr->label > 64 && expr->label < 91){
ajouter_variable(expr->label);
}
inventorier_variables(expr->droit);
}
}


Item* creer_variable(char v){
item* var;
if(v > 64 && v < 91){
var=my_malloc(sizeof(item));
var->label = v;
var->gauche = NULL;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Item* creer_diaddique(char o, item *op1, item *op2){
item* var;
if(o == '&' || o == '|' || o == '^'){
var=my_malloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = op2;
return var;
}else{
return NULL;
}
}

Item* creer_monaddique(char o, item *op1){
item* var;
if(o=='!'){
var=my_malloc(sizeof(item));
var->label = o;
var->gauche = op1;
var->droit = NULL;
return var;
}else{
return NULL;
}
}

Void *my_malloc(size_t size)
{
void *p = malloc(size);
if (p == NULL && size != 0) {
fputs("Plus de mémoire.\n", stderr);
exit(1);
}
return p;
}


Void display_expression(item *expr)
{

if(expr->gauche==NULL){
printf("%c",'(');
}
display_expression(expr->gauche);
if(expr->gauche==NULL){
printf("%c",')');
}


switch(expr->label){
case '&': printf("%s"," AND ");break;
case '|': printf("%s"," OR ");break;
case '^':printf("%s"," XOR ");break;
case '!': printf("%s"," NOT ");break;
default: printf("%c",expr->label);break;
}


if(expr->droit==NULL){
printf("%c",'(');
}
display_expression(expr->droit);
if(expr->droit==NULL){
printf("%c",')');
}
}
Je ne suis pas encore sûr du display, mais à la compilation, j'ai un énorme problème...
Il m'affiche des erreurs de pointeurs:
Expression.c:44: erreur: ‘item’ has no member named ‘gauche’
Expression.c:32: erreur: ‘item’ has no member named ‘droit’

Ou encore :
Expression.c:55: attention : assignment makes pointer from integer without a cast

Comment ça se fait? Ca me le fait pour chaque appel d'un membre de expr...

EDIT : je rajoute aussi deux erreurs sur le my_malloc:

Expression.c:65: erreur: conflicting types for ‘my_malloc’
Expression.c:29: erreur: previous implicit declaration of ‘my_malloc’ was here


Comprends rien :emo:
Et ça tombe ou dans ton code ? (ligne 65/25/44/32) ?
Pour la 29/35, déplace le corps de la fonction my_malloc avant les fonctions ou tu l'utilises.

PolluXxX
17/06/2009, 22h23
Et ça tombe ou dans ton code ? (ligne 65/25/44/32) ?
En fait, je ne vous ai copié qu'un petit bout des erreurs. J'ai ces erreurs à chaque appel du genre exp->gauche|droit.

Toutes mes erreurs (après le déplacement de my_alloc):


Expression.c:20: erreur: expected specifier-qualifier-list before ‘item’
Expression.c: In function ‘inventorier_variables’:
Expression.c:28: erreur: ‘item’ has no member named ‘gauche’
Expression.c:32: erreur: ‘item’ has no member named ‘droit’
Expression.c: In function ‘creer_variable’:
Expression.c:42: erreur: ‘item’ has no member named ‘gauche’
Expression.c:43: erreur: ‘item’ has no member named ‘droit’
Expression.c: In function ‘creer_diaddique’:
Expression.c:55: erreur: ‘item’ has no member named ‘gauche’
Expression.c:56: erreur: ‘item’ has no member named ‘droit’
Expression.c: In function ‘creer_monaddique’:
Expression.c:68: erreur: ‘item’ has no member named ‘gauche’
Expression.c:69: erreur: ‘item’ has no member named ‘droit’
Expression.c: In function ‘display_expression’:
Expression.c:80: erreur: ‘item’ has no member named ‘gauche’
Expression.c:83: erreur: ‘item’ has no member named ‘gauche’
Expression.c:84: erreur: ‘item’ has no member named ‘gauche’
Expression.c:98: erreur: ‘item’ has no member named ‘droit’
Expression.c:101: erreur: ‘item’ has no member named ‘droit’
Expression.c:102: erreur: ‘item’ has no member named ‘droit’


Je soupçonne une erreur dans la déclaration de ma structure...

rOut
17/06/2009, 22h40
Lors de la déclaration de ta structure, tu utilises un typedef pour pouvoir ensuite te passer du mot clef struct.

Normalement, lorsque tu déclares une structure, tu le fais de cette manière (la façon la plus élémentaire de déclarer une structure) :

struct machin {
int bidule;
}

Lorsque tu déclares ta structure de cette manière, la syntaxe du C nécessite qu'ensuite tu utilises "struct machin", comme type de variables (ou "struct machin*" comme type de pointeur), pour utiliser ta structure.

Un raccourcit du langage permet de s'affranchir de ce mot clef "struct" supplémentaire, à l'aide d'un typedef.
Un typedef c'est pour déclarer un nom de type de données à partir d'un autre. Tu peux par exemple écrire :


typedef int machin;

Cette ligne va te permettre ensuite d'utiliser "machin" comme n'importe quel type de données, ce sera un alias pour le type "int".

De la même manière, tu déclares ta structure directement dans le typedef, pour aller plus vite :


typedef struct machin { int bidule } machin_t;

"machin_t" sera alors un alias pour le type "struct machin".

Là ou je veux en venir, c'est que ton typedef n'est valable qu'après l'instruction ou tu l'as déclaré. Or, dans ton code, tu utilises "item" comme membre de ta structure. Or, à cet instant là, le typedef est toujours en cours de déclaration, il n'est pas encore connu.

Deux solutions s'offrent à toi :
Utiliser le type complet "struct item*" pour la déclaration de tes pointeurs gauche et droit, le type de la structure est bien connu par le compilateur au sein de sa propre déclaration :


typedef struct item {
char label;
struct item* gauche;
struct item* droit;
} item;

Ou alors, écrire ton typedef avant la déclaration de ta structure. Pour cela, tu peux profiter du fait que le C autorise la pré-déclaration de types. C'est à dire que tu peux indiquer au compilateur, que plus tard, il va trouver la déclaration d'un type, de la même manière que tu peux indiquer des entêtes de fonctions au début de fichier (ou dans un .h) et le code des fonctions plus loin :


// On indique qu'il existe dans notre programme une structure qui s'apelle item.
// Elle sera définie plus loin dans le code
struct item;

// On ajoute un alias item pour accéder à notre struct item.
// Du coup, la ligne d'avant n'est pas vraiment nécessaire, le typedef peut servir aussi de déclaration.
typedef struct item item;

// On définit maintenant notre structure. Le compilateur est content, il a trouvé l'endroit ou elle était définie... Sinon il aurait gueulé.
struct item {
char label;
item* gauche;
item* droit;
};

Edit : D'ailleurs, je te conseille, pour plus de clarté, de nommer tes typedef de structures avec un suffixe _t, pour éviter de faire la confusion entre le nom de ta structure (item) et le nom de ton typedef (item_t).

Autre possibilité offerte par le typedef de struct, créer des structures anonymes : tu n'es pas obligé de donner un nom à ta structure, tu peux par exemple écrire :


typedef struct { int machin; } struct_anonyme;

"struct_anonyme" sera alors un type parfaitement valide ayant le membre "machin". Par contre dans ton cas ce n'est pas possible vu que tu as besoin du nom de ta structure pour les membres gauche et droit (ou la déclarer précédemment, ce que tu ne peux pas faire de façon anonyme) ... Ou alors utiliser des pointeurs void* pour ces membres... Mais c'est pas sur que ce soit une bonne idée :p.

olih
17/06/2009, 22h42
Alors :
Dans ta déclaration de structure, tu utilises l'alias item, que le compilateur ne connait pas encore -> mets le nom complet du type c'est à dire struct item.

Et pour :
Expression.c:65: erreur: conflicting types for ‘my_malloc’
Expression.c:29: erreur: previous implicit declaration of ‘my_malloc’ was here
Cela signifie que le compilateur a déja rencontré la fonction dans le code (tu l'utilises avant sa définition).
Deux possibilités :
- mettre le corps de la fonction my_malloc dans le source avant les fonctions l'utilisant
- indiquer au compilateur à quoi ressemble la fonction

Edit: super grillé pour la structure.

PolluXxX
17/06/2009, 22h45
Ok, j'ai compris! Me reste plus que la dernière méthode, evaluer, et tester l'affichage.

Je vais commencer par tester l'affichage, comme ça on verra, pas encore sûr de ce que j'ai fait.

En tout cas, merci!

Résultat du premier test:
Erreur de segmentation...
Mouahahaha. C'est la merde. Je verrai éventuellement avec le prof le pourquoi du comment.

PolluXxX
17/06/2009, 23h44
Après 20 minutes d'intense et longue bataille contre emacs et mon terminal, j'ai triomphé!

En fait, j'avais merdé dans ma méthode d'affichage. J'ai donc corrigé ça:


Void display_expression(item* expr)
{

if(expr->gauche!=NULL){
printf("(");
}
if(expr->gauche!=NULL){display_expression(expr->gauche);}
//if(expr->gauche==NULL){
//printf(")");
//}


switch(expr->label){
case '&': printf(" AND ");break;
case '|': printf(" OR ");break;
case '^':printf(" XOR ");break;
case '!': printf(" NOT ");break;
default: printf("[%c]",expr->label);break;
}


//if(expr->droit==NULL){
// printf("(");
//}
if(expr->droit!=NULL){display_expression(expr->droit);}
if(expr->droit!=NULL){
printf(")");
}
}


Bon, ça marche presque. Encore quelques soucis, mais ça peut se rêgler vite je pense.
Voilà ce qu'il affiche:


Donner l'expression logique à étudier:
A&B|!C.
([A] AND ([B] OR ([C] NOT ))



Et ce qu'il devrait afficher:
Donner l'expression logique à étudier:
A&B|!C.
(([A] AND [B]) OR ( NOT [C]))


Je dois encore voir mon parenthèsage, vu que là, il fait l'inverse au niveau des priorités.
Si quelqu'un a une idée par rapport à ça, qu'il n'hésite pas!

En tout cas, un grand merci, j'ai plus appris avec vous en deux pages qu'en 4h de cours sur les pointeurs!

rOut
17/06/2009, 23h56
Pour le problème du NOT inversé, c'est que ta fonction d'affichage effectue toujours l'affichage du fils gauche avant l'affichage du label.

Tu pourrais déplacer la détection du type d'item plus haut, et faire plutôt quelque chose comme ça :


if (expr->label == '!') {
printf("(");
printf(" NOT ");
display_expression(expr->gauche);
printf(")");
} else if(expr->label > 64 && expr->label < 91) {
printf("[%c]", expr->label) {

// Peut être mettre un else if là aussi pour bien vérifier, mais bon... J'ai la flemme d'écrire la condition
} else {
printf("(");
display_expression(expr->gauche);
switch(expr->label) {
// gnagna...
}
display_expression(expr->droit);
printf(")");
}

Pour l'ordre des parenthèses, je pense que c'est dû à l'ordre dans lequel tu construit ton arbre à partir de l'expression. Je suppose que le OR est censé être prioritaire sur le AND dans l'énnoncé (je ne l'ai pas relu, je me trompe peut être), et tu n'en tiens pas contre dans ton parsing.

Edit: J'avais oublié les parenthèses.

PolluXxX
18/06/2009, 10h05
Bon, module terminé!

Encore des problèmes d'affichage (parenthèses et priorité), mais je dois voir ça avec le prof. En tout cas, un grand merci pour l'aide, je reviendrai p-e si je vois que ça ne va vraiment pas.
Là je vais voir avec le prof pour l'affichage!

PolluXxX
18/06/2009, 19h09
Ahahahaha.
J'ai toujours un problème d'affichage, j'ai passé une heure à essayer de comprendre pourquoi il gère mal les priorités avec leurs parenthèses.

Et bah en fait, le problème, il vient du module "parse", fourni par le prof, qui s'occupe de créer l'arbre à partir de la saisie.

Et bah en fait, il fait un arbre merdique. Con de prof :tired: