[C] pointeur vers structures

Salut,
je suis en train de m’écrire un petit truc pour m’aider à tenir à jour mes cachets de musicien.
Chaque cachet (“gig” en anglais) est une structure (date, lieu, groupe, salaire, etc…). Je voudrais gèrer tout les cachets en les groupant dans un tableau rangés selon leur date.
L’idée ressemble un peu à une entreprise qui veut se créer un fichier contenant des infos sur ses employés.
Déjà, est ce que ça vous semble raisonnable?
L’idée serait ensuite de pouvoir calculer les sommes déclarées par mois/trimestre/année ou par groupe. J’ai essayé avec un simple tableur mais je ne sais automatiser ces calculs.

Voila où j’en suis:
Cette fonction ne compile pas malgré la ligne presta = &tmp:

[code]gig* create_gig(void) {
gig *presta,tmp;

tmp.date = 0;
presta = &tmp;
scanf("Date: %d",presta->date);
return (presta);

}

functions.c:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘*’ token[/code]

Mon prototype:

[code]typedef struct gig {
int date;
// char *place;
// char *band;
// float pay;
// int status;
// char *employer;
// char *misc;
} gig;

gig* create_gig (void);[/code]

Le main.c est tout simple et j’utilise un makefile assez basique.

Une fois que j’ai corrigé l’utilisation du scanf.
Pour essaie j’ai créé le fichier essaie.c : pastebin.com/5XJg7i5c
que je compile sans makefile :

J’ai aucun warning.

Le compilateur ne connaît pas gig et se plaint. Soit il manque le fichier gig.h qui va bien, soit la définition de gig est mal positionnée.

Merci à vous deux. Mon erreur venait du fait que je n’avais pas inclu mon fichier .h. La definition de la strucuture n’apparaissait nulle part…

J’en profite.
Sachant que je passe mes structures à travers des fnctions et qu’elles contiennent des chaines de caractères de longueur variables, vaut il meix faire qqchose du genre:

[code]struct mastructure {
int *p1
char p2
char
p3
etc… } test;

int a;
char s[10];

//remplir a et s;
test->p1 = &a;
test->p2 = s;[/code]

ou bien:

[code]struct mastructure {
int a;
char s[10];
etc… } test;

//remplir directement la structure?[/code]

Ça dépend de pas mal de choses, mais je ne saurais que trop te recommander d’utiliser Bstring (bstring.sourceforge.net/) qui permet d’éviter un tas d’erreur et de faille dans la gestion des string en C.

Il n’y a aucun intérêt à utiliser des pointeurs pour les types de taille fixe comme int. Pour les chaînes, ça se discute mais si on utilise des pointeurs il vaut mieux savoir exactement ce qu’on fait : le pointeur pointe vers quelque chose, et il faut gérer l’allocation mémoire de ce quelque chose.

PS : rassure-moi, la vraie fonction create_gig() ne contient pas le code que tu as montré, n’est-ce pas ? Tu es conscient qu’affecter à un pointeur l’adresse d’une variable locale automatique, puis retourner la valeur de ce pointeur en sortie est stupide ?

Ma fonction a changé un peu depuis mais je crois que c’est dangereux:

[code]char buffer[BUFSIZ];
gig *presta,tmp;

fflush(NULL);
presta = &tmp;

printf("Date: ");
fgets(buffer,sizeof(buffer),stdin);
presta->date = strtol(buffer,NULL,0);


return(presta);[/code]

J’imagine que c’est foireux vu que je passe l’adresse d’une variable déclarée localement.
Quelles solutions? utiliser malloc dans la fonction revient au même non?
Il faut declarer la variable dans le main() et la passer en argument?

Pas du tout, l’allocation mémoire ne se fait pas au même endroit (l’allocation automatique est sur la pile et l’autre sur le tas). Le tas est bien plus grand que la pile. Mais surtout la mémoire allouée sur la pile va être libérée automatiquement quand tu sort de la fonction.

Ok.
Donc si je retourne un pointeur vers une variable locale, cela revient à pointer vers un espace sur la pile. Je risque de voir mes données écrasées dès que je sors de la fonction?
Il faut que le pointeur pointe vers un espace alloué sur le tas. J’utilise donc malloc, realloc. Ca m’eviterait de rajouter des variables dans le main().
J’ai bon?

Malloc (et realloc) est il le seul à faire ça? Si je comprends bien, il faut faire des free dans ses fonctions sinon l’espace reste alloué est inaccessible une fois sortie de la fonction?

Pour faire simple, l’allocation sous quelque forme (malloc, variable…) ne doit pas être faite dans la fonction appelée mais dans la fonction appelante ou au-dessus, puisque c’est cette dernière qui va utiliser le résultat.

Ok, je vais m’y résoudre.
Ca donnera pour un tableau de taille n:

main () ... tab_gig = realloc(tab_gig,(n+1)*sizeof(gig*)); create_gig(tab_gig[n]); n++; ...

La fonction écrit directement dans tab_gig[n]. C’est decidement la même chose qu’avec les tableaux.

Pas sûr d’avoir compris ce que tu veux faire. Sauf erreur de ma part, d’après ton code l’espace mémoire pointé par tab_gig contient des pointeurs vers des structures de type gig et non des structures de types gig. Dans ce cas il faut aussi allouer de la mémoire pour contenir les structures vers lesquelles pointent ces pointeurs. C’est bien ce que tu veux ?

Au départ c’était mon idée et je pensais que cette allocation pouvait se faire dans la fonction appelée.
J’ai fait comme ça car je ne voyais pas comment passer certaines cases en argument si je ne passais pas par des pointeurs.

Admettons que je decide d’utiliser un tableau de gig au lieu d’un tableau de gig*. Comment faire pour passer l’adresse d’une case de mon tableau à une fonction afin de modifier cette case?

tab_gig = malloc(n*sizeof(gig)); remplir_tab(tab_gig,n); modif_tab(tab_gig[3]); ici on passe la variable pas valeur, pas de modif possible. modif_tab(&tab_gig[3]);? équivalent à modif_gig(tab_gig+3)?

Ça dépend comment tu as défini tab_gig. Si c’est comme gig * (pointeur sur type structure gig), alors les deux formes de la dernière ligne, &tab_gig[3] et tab_gig+3, sont équivalentes en tant que pointeurs vers le 3e élément de type gig. La seconde forme a peut-être l’avantage de rappeler que tab_gig est un pointeur et pas un tableau.

Dans l’avant-dernière ligne le commentaire est faux : il ne s’agit pas d’un passage par adresse (pointeur) mais au contraire par valeur. Comme c’est une copie de la variable qui est passée à la fonction, cette dernière ne peut modifier la variable originelle.

Pour le commentaire, je voulais dire par valeur bien sûr. Etourderie, je corrige.

tab_gig sera déclaré comme pointeur vers structure gig: gig *tab_gig; puis alloué dynamiquement.
Je m’y mets.

Merci pour cette discussion enrichissante.

C’est tout à fait possible.

[code]$ less fic.c
#include “stdio.h”
#include “stdlib.h”

typedef struct gig {
int date;
} gig;

gig* create_gig (void) {
gig* tmp = malloc(sizeof(gig));
tmp->date = 42;
return tmp;
}

int main (int argc, char** argv) {
gig* foo = create_gig();
printf(“foo->date = %d\n”, foo->date);
free(foo);
return EXIT_SUCCESS;
}
$ CC=clang CFLAGS=-Wall make fic
clang -Wall fic.c -o fic
$ ./fic
foo->date = 42[/code]

Ce n’est pas forcément recommandé car tu ne libère pas la mémoire au même endroit que tu ne l’alloue (mais avec une conception objet, tu crée un destructeur et ce n’est pas forcément plus mal).

Non ça ne dépend pas de comment tu l’a défini. Ceci :

[code]#include “stdio.h”
#include “stdlib.h”

void foo (int * val) {
printf(“foo = %d\n”, *val);
}

int main (int argc, char** argv) {
int tab [] = {1, 2, 3};
foo(tab+1);
return EXIT_SUCCESS;
}[/code]
fonctionne très bien.

Oui, l’équivalence tableau/pointeur.
J’esseairai de me souvenir de cette histoire d’allocation mémoire sur le tas (et penser à faire les free()).

Je ne vois pas ce que ton exemple est censé montrer ni en quoi il contredit ce que j’ai écrit.
Si le pointeur n’est pas défini avec le type approprié (void…), alors le calcul de l’offset ne sera pas fait correctement à moins de faire un cast explicite. C’est tout ce que je voulais dire.

Ok on s’est mal compris j’ai pensais que tu faisais une différence entre une déclaration comme pointeur (type * ident) et un tableau (type ident []). Pour le typage ça me semblait une évidence.

Salut,
je continue sur mon projet et j’ai réglé pas mal de problèmes en ayant prit conscience de l’existence de la pile et du tas -> obligation d’utiliser malloc ou realloc dans mon cas (j’utilisais des variables locales :/)

Par contre j’ai un problème lorsque je récupère mon tableau dans la fonction appelante. Il faut que la fonction appelée retourne le pointeur sinon il y a un seg fault dès que je veux lire mon tableau apparement.
Je me rappelle pas être tombé sur un truc comme ça avant!
Je pensais que c’était justement l’avantage de manipuler des pointeurs: pouvoirs manipuler les valeurs de la fonction appelante.

Ex: foo est une fonction bidon qui fait un realloc d’une case donne une valaur à tab[0] puir return(tab).

[code]int main () {
int *tab = NULL;

foo(tab);
//tab = foo(tab);

printf("%d\n",tab[0]);
	
return (0);

}[/code]

Un seg fault se produit avec le printf. Il faut utiliser la ligne commentée. Ca m’ennuie car j’écrivais mes fonctions de manière à ce qu’elle modifie mon tableau/pointeur (édition, rajout, suppression de case) et retourne le nombres d’éléments.

Ma question est:
J’écris une fonction (pour lire mon tableau à partir d’un fichier). Comment je fais pour recupèrer mes données et la taille du tableau?

Dans le cas d’un tableau d’entier ou de float, on pourrait peut être utiliser sizeof pour trouver le nombres d’éléments mais est ce que ça marche pour des structures contenant des chaines de caractères dynamique?
J’ai lu des forums où il est question de tab-1 où serait stocker le nombres d’éléments.

D’ailleurs comment est géré la mémoire dans un tableau où chaque case est de taille différente? l’incrémtation tab+1 est toujours valide?