OVH Cloud OVH Cloud

Ecrire une structure dans un fichier binaire

31 réponses
Avatar
citrouille
En fait, j'ai une structure très simple que je veux écrire dans un
fichier :

typedef struct{
char *txt;
int x;
}TEST;


Ecriture dans le fichier :

TEST *t = (TEST *)malloc(sizeof(TEST));
FILE *file;

t->txt = "pouet pouet";
t->x = 257;

file = fopen("fichier_test", "rb");
write(file, t, sizeof(TEST));
fclose(file);

Quand je regarde le fichier créé avec un éditeur héxadécimal, je
constate que ma chaine de caractere n'est pas enregistré, mais
seulement son adresse (je pense), car j'ai beau changer la chaine,
c'est toujour la meme chose d'enregistré. tandi que le int lui est
bien enregistré dans le fichier.

Pourrais-je avoir la bonne technique pour faire bien cette écriture ?

10 réponses

1 2 3 4
Avatar
Bertrand Mollinier Toublet
Christophe Le Gal wrote:
In article <3f3c05c4$0$9626$,
Richard Delorme wrote:

char *chaine = "salut"; // ok.
chaine = " pouet pouet"; // correct.
chaine = "truc bidule machin chose"; //encore correct.
chaine = tableau; // toujours ok.
chaine = malloc(123); // ok.



Pour en rajouter une couche :
Ce genre de couche, il vaut mieux s'abstenir...


"toto"[0] = 'T' ; // ok
NON

strcpy("toto", "tutu") ; //ok
NON

strcpy("toto", chaine) ; //ok
NON


Mais
"toto" = "tutu" ; // non
"toto" = chaine ; // non

enfin, j'ai pas verifie la norme, mais
je ne vois pas pourquoi ca ne serait pas le cas, et en tous cas
ca se passe comme ca avec gcc.
Comme tu dis, tu n'as pas verifie la norme qui dit que les, euh, "string

litterals", i.e. ton "toto" ci-dessus, ne sont pas modifiable, sous
peine d'UB. En consequence, tu ne peux pas verifier que c'est bien le
cas avec ton compilateur prefere, puisqu'un UB peut se manifester par
quelque chose "qui marche".

"toto" est un char *, au meme titre que 1 est un int.
C'est une constante char * donc.
Avec une difference par rapport a 1, qui est que sa valeur
correspond a l'adresse d'une zone de memoire qui contiendra
les caracteres t, o, t, o, au chargement du programme.

Chez moi la compilation de strcpy("toto", "tutu"), si elle
se passe sans le moindre warning, genere un programme dont l'execution
provoque, on s'en doutait un peu, un Segmentation fault.

La, je comprends plus. Tu es schizo ou quoi ? Plus haut, tu ecris, a

tort: strcpy("toto", "tutu");//ok et ici, tu dis que ca ne marche
evidement pas ? Mmmmh...

Mais je suis sur que dans le passe j'ai deja fait qqchose du genre
printf("Gunaydin dunyan") ;
strcpy("Gunaydin dunyan", "Hello worldn") ;
printf("Gunaydin dunyan") ;

avec suivant les compilo est les plateformes des resultats differents :
parfois le 2e printf affiche "Gunaydin dunyan", parfois "Hello worldn".
Ca depend si le compilo est assez malin pour creer une seule fois
la chaine ou non.

Oui... et non. C'est deja pas mal que ton programme ne t'aie pas explose

a la figure a la ligne strcpy.

--
Bertrand Mollinier Toublet
"In regard to Ducatis vs. women, it has been said: 'One is a sexy thing
that you've just got to ride, even if it breaks down a lot, costs a lot
of money, and will probably try to kill you'. However, nowadays I can't
seem to remember which one is which." -- Peer Landa


Avatar
Stephane Legras-Decussy
"Christophe Le Gal" a écrit dans le message
news:
Pour en rajouter une couche :
Ce genre de couche, il vaut mieux s'abstenir...




bon euh...c'est pas tout ça mais alors j'avais raison ou pas alors ?



Avatar
Stephane Legras-Decussy
"Christophe Le Gal" a écrit dans le message
news:
Explique moi, sans m'insulter encore stp, ce qu'est un UB
(Unchecked boundary ?), et en quoi ca concerne la *syntaxe*.
Comment le compilo pourrait il laisser passer (sauf bug) une erreur
syntaxique ?


UB undefined behaviour .... comportement imprevisible...

tu ne peux pas avoir la preuve qu'un truc est correct simplement
en verifiant que marche...

ça peut marcher par hazard...

par exemple t[i++] = i ;

est un UB et ça passe bien à la compilation...

Avatar
Horst Kraemer
On 14 Aug 2003 01:46:48 -0700, (ztn) wrote:

En fait, j'ai une structure très simple que je veux écrire dans un
fichier :

typedef struct{
char *txt;
int x;
}TEST;


Ecriture dans le fichier :

TEST *t = (TEST *)malloc(sizeof(TEST));
FILE *file;

t->txt = "pouet pouet";
t->x = 257;

file = fopen("fichier_test", "rb");
write(file, t, sizeof(TEST));
fclose(file);

Quand je regarde le fichier créé avec un éditeur héxadécimal, je
constate que ma chaine de caractere n'est pas enregistré, mais
seulement son adresse (je pense), car j'ai beau changer la chaine,
c'est toujour la meme chose d'enregistré. tandi que le int lui est
bien enregistré dans le fichier.


C'est normal, parce que la structure ne contient qu'un pointeur vers
une chaine et non une chaine. Il n'a pas de sens de stocker un
pointeur dans un fichier.

file = fopen("fichier_test", "w");

fprintf(file,"%sn",t->txt);
fprintf(file,"%dn",t->x);

fclose(file);

Voilà le contenu "logique" de ta structure. Un entier et une chaine.
Deux lignes dans un fichier texte. Une partie du contenu physique (le
pointeur) est perdu - mais ce n'est pas de perte grave parce que le
pointeur n'a un sens que dans le programme qui écrit la structure.

--
Horst

Avatar
Christophe Le Gal
In article <3f3d24b9$0$27025$,
Stephane Legras-Decussy wrote:

UB undefined behaviour .... comportement imprevisible...

tu ne peux pas avoir la preuve qu'un truc est correct simplement
en verifiant que marche...

ça peut marcher par hazard...

par exemple t[i++] = i ;


Certes. Et ton exemple est un bon exemple. C'est imprevisible, parce
que on ne sait meme pas ce que c'est sense compiler. On ne precise
par quel code doit etre genere.

Les exemples que j'ai donnes ne peuvent pas etre dans la meme
categorie d'erreur : ils sont sans ambiguites. J'utilise un
langage C syntaxiquement et contextuellement correct (je ne suis
pas sur pour le reste), et le programme est complement
clair et net :
quand j'ecris ((char *)0)[0] = 'X' ;
il n'y a pas trop de suspense pour savoir comment ce programme
va etre compile.
C'est, il me semble, egalement le cas pour "toto"[0]='X' ;


--
Christophe Le Gal

Avatar
Bertrand Mollinier Toublet
Christophe Le Gal wrote:
Pour en rajouter une couche :


Ce genre de couche, il vaut mieux s'abstenir...



Si tu pouvais t'abstenir d'etre agressif envers les gens qui ne t'on
encore rien fait, ca aiderait aussi.


Bon, j'etais en vacances, ca explique le manque de reactivite :-)
Il n'y a pas d'agressivite ci-dessus. Juste une remarque pragmatique que
pour autant que j'ai pu comprendre tu as ecris des horreurs dont il
aurait mieux valu s'abstenir...


Explique moi, sans m'insulter encore stp, ce qu'est un UB
(Unchecked boundary ?), et en quoi ca concerne la *syntaxe*.
Comment le compilo pourrait il laisser passer (sauf bug) une erreur
syntaxique ?



UB, c'est Undefined Behaviour, ce qui se traduirait en "comportement
indefini", mais n'est pas a interpreter litteralement. Il faut au
contraire l'interpreter selon la definition de la norme (et c'est en
anglais, d'ou le conditionel a "traduirait")(par esprit de completion,
je t'inclus toutes les definitions):

3.4
1 behavior
external appearance or action

3.4.1
1 implementation-defined behavior
unspecified behavior where each implementation documents how the choice
is made

2 EXAMPLE An example of implementation-defined behavior is the
propagation of the high-order bit when a signed integer is shifted right.

3.4.2
1 locale-specific behavior
behavior that depends on local conventions of nationality, culture, and
language that each implementation documents

2 EXAMPLE An example of locale-specific behavior is whether the islower
function returns true for characters other than the 26 lowercase Latin
letters.

3.4.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of
erroneous data, for which this International Standard imposes no
requirements

2 NOTE Possible undefined behavior ranges from ignoring the situation
completely with unpredictable results, to behaving during translation or
program execution in a documented manner characteristic of the
environment (with or without the issuance of a diagnostic message), to
terminating a translation or execution (with the issuance of a
diagnostic message).

3 EXAMPLE An example of undefined behavior is the behavior on integer
overflow.

3.4.4
1 unspecified behavior
behavior where this International Standard provides two or more
possibilities and imposes no further requirements on which is chosen in
any instance

2 EXAMPLE An example of unspecified behavior is the order in which the
arguments to a function are evaluated.

Alors, maintenant, en quoi ca concerne la syntaxe, il va falloir voir ca
dans le contexte de ma reponse, que tu as coupe. Apres verification, je
n'ai parle nulle part de syntaxe. Il va falloir que tu me reprecises le
sens de ta question.


Chez moi la compilation de strcpy("toto", "tutu"), si elle
se passe sans le moindre warning, genere un programme dont l'execution
provoque, on s'en doutait un peu, un Segmentation fault.


La, je comprends plus. Tu es schizo ou quoi ? Plus haut, tu ecris, a
tort: strcpy("toto", "tutu");//ok et ici, tu dis que ca ne marche
evidement pas ? Mmmmh...


Non je ne suis pas schizo. Enfin je ne crois pas. Simplement je sais lire.
Il y a une difference entre quelque chose qui compile, et quelquechose
qui marche.
((char *)0)[0] = 'X' ;

est legal que je sache. Ca n'a pourtant aucune chance de marcher.
ou si tu prefere absolument des notations plus habituelles :

char *T ;
T=NULL ;
T[0]='X' ;

(mais c'est vraiment juste pour gaspiller un nom)

Donc peut etre que la norme interdit explicitement mes horreurs avec
"toto". Comme je l'ai dit depuis le debut, je n'en sais rien, et en
l'occurrence ca n'a pas tellement d'importance puisque je voulais
illustrer le faite "toto" est un pointeur constant. Mais le fait
que je dise que ca ne marche "evidemment" pas n'a rien a voir la
dedans, et ne me fait pas meriter le qualificatif de schizo.

Il fallait bien entendu prendre le qualificatif au sens clinique du

terme. Apparement, tu disais, a quelques lignes d'intervalles, deux
choses totalement opposees, si bien que je n'ai pu que supposer que tu
souffrais de dedoublement de la personalite :-) Bref...

Tes deux exemples sont causent egalement des UB. Le premier parce que tu
"crees" un pointeur vers une zone de memoire qui /a priori/ ne
t'appartient pas. Le deuxieme, parce que tu dereferences le pointeur
nul. Dans les deux cas, un compilateur agressif (et/ou bien configure)
va t'indiquer les problemes.

Note egalement que les deux exemples ne sont pas equivalents,
contrairement a ce que tu peux laisser entendre.


Mais je suis sur que dans le passe j'ai deja fait qqchose du genre
printf("Gunaydin dunyan") ;
strcpy("Gunaydin dunyan", "Hello worldn") ;
printf("Gunaydin dunyan") ;

avec suivant les compilo est les plateformes des resultats differents :
parfois le 2e printf affiche "Gunaydin dunyan", parfois "Hello worldn".
Ca depend si le compilo est assez malin pour creer une seule fois
la chaine ou non.


Oui... et non. C'est deja pas mal que ton programme ne t'aie pas explose
a la figure a la ligne strcpy.


Il m'explose a la figure a la ligne strcpy. Et alors ?
C'est interdit de faire des programmes qui explosent a la figure ?


Si le programme est a valeur didactique, comme par exemple quand tu
postes dans f.c.l.c, j'aurais tendance a penser que oui. Ou alors, tu
indiques clairement que le programme est mine jusqu'a la moelle, ce que
tu n'as pas (suffisament) fait.

Enfin du temps de dos il ne le faisait pas. Et ca n'etait pas interdit
(toujours que je sache) non plus. C'etait juste encore plus sale que


C'est une question de contexte. Dans le contexte du C ISO (89 et 99),
c'est strictement interdit (c'est a dire que c'est explicitement indique
commme causant un UB). Si je ne me trompe, c'etait deja interdit en K&R
C. Mais je peux me tromper.

maintenant, puisqu'a l'epoque on pouvait avoir un petit doute quant a
savoir si l'auteur de ces lignes est en train de blaguer ou non.



Alors, contrairement aux apparences, ce n'est pas de l'acharnement. Le
seul truc, c'est que quand tu postes dans f.c.l.c et a mon avis dans un
newsgroup technique en general, a defaut d'etre irreprochable, il faut
au moins etre solide. Ce que tu racontais etait pour le moins... branlant.
--
Bertrand Mollinier Toublet
"Bikes are like ladies, if you don't take care of them all the time,
when you feel like going back to them, it takes a lot of work"
-- Riccardo Turchetto



Avatar
Bertrand Mollinier Toublet
Christophe Le Gal wrote:
In article <3f3d24b9$0$27025$,
Stephane Legras-Decussy wrote:


UB undefined behaviour .... comportement imprevisible...

tu ne peux pas avoir la preuve qu'un truc est correct simplement
en verifiant que marche...

ça peut marcher par hazard...

par exemple t[i++] = i ;



Certes. Et ton exemple est un bon exemple. C'est imprevisible, parce
que on ne sait meme pas ce que c'est sense compiler. On ne precise
par quel code doit etre genere.

Les exemples que j'ai donnes ne peuvent pas etre dans la meme
categorie d'erreur : ils sont sans ambiguites. J'utilise un
langage C syntaxiquement et contextuellement correct (je ne suis
pas sur pour le reste), et le programme est complement
clair et net :
quand j'ecris ((char *)0)[0] = 'X' ;
il n'y a pas trop de suspense pour savoir comment ce programme
va etre compile.
C'est, il me semble, egalement le cas pour "toto"[0]='X' ;


C'est parce que tu as une vue trop concrete de la machine sur laquelle

ton programme est compile (et que tu utilises des compilateurs pas assez
creatifs :-). Nonobstant les raccourcis qui finalement tendent vers une
image de la cible compatible avec l'idee que tu t'en fais, un
compilateur C est cense compiler pour la "machine virtuelle" decrite
dans la norme. En particulier, si la norme indique qu'une construction
est cause d'UB, un compilateur pervers a entierement le droit d'ignorer
totalement la construction en question et de faire quelque chose
d'absolument pas en rapport.

Dans l'esprit, le comportement indefini est une liberte offerte a
l'implementeur d'un compilateur pour simplifier son design. Dans le cas
des chaines litterales, par exemple, d'une architecture a l'autre, le
format du code objet fera que les chaines seront en lecture seule ou
pas. Dire que c'est un UB de modifier une chaine litterale permet aux
implementeurs de ne pas se soucier de ce detail et de laisser agir un
mecanisme sous jacent, par exemple la levee d'un signal SIGSEGV.

Ca permet aussi a l'implementeur /pervers/ de te formatter ton disque
systeme a la figure ou de faire sauter la planete (cf Armed Response
Technology sur http://dspace.dial.pipex.com/town/green/gfd34/art/).

--
Bertrand Mollinier Toublet
"Reality exists" - Richard Heathfield, 1 July 2003


Avatar
Christophe Le Gal
Il fallait bien entendu prendre le qualificatif au sens clinique du
terme. Apparement, tu disais, a quelques lignes d'intervalles, deux
choses totalement opposees, si bien que je n'ai pu que supposer que tu
souffrais de dedoublement de la personalite :-) Bref...


Cette histoire est trop vieille pour que je revienne sur le fond.
Mais je maintiens que mon post n'etait pas contradictoire. Je n'ai
pas dis 2 choses differentes.
Le fait de dire qu'une construction est syntaxiquement correcte (qui
se trouve ne pas etre autorisee par la norme, mais ca ne se passe
pas au niveau syntaxe; et j'ai clairement dit que je ne savais pas
si la norme l'autorisait) signifie pas que que ca ne va pas exploser.

Juste pour etre bien sur de comprendre :

Un programme qui ferait :
scanf("%d", &index) ;
printf("%d", tab[index]) ;

serait interdit par la norme ?
On ne peut pas garantir que index sera dedans ou dehors les limites
de tab. On doit meme pouvoir en choisissant bien "index" taper
pile dans l'adresse d'une chaine constante.
Donc si je comprends bien c'est un UB.
Qu'est-ce qui ferait que ce programme serait du C legal, et mes
exemples non ?
Le fait que dans mes exemples on peut _statiquement_ prouver
qu'on va au devant de graves problemes ?
Mais dans ce cas ca voudrait dire que l'intelligence du compilo
a une influence sur la norme. Ou la norme fixe-t-elle un
niveau de facilite de detection, interdisant les erreurs "facilement
detectables" ?
*((char *)(0)) = '' ; est interdit ?

x = 0 ;
*((char *)(0+x)) = '' ?

x = (int)arcsin(3.14159265) ;
*((char *)(0+x)) = '' ?


Tes deux exemples sont causent egalement des UB. Le premier parce que tu
"crees" un pointeur vers une zone de memoire qui /a priori/ ne
t'appartient pas.


C'est interdit ?
Il me semblant que c'est plutot en le dereferencant que je faisait
une erreur _runtime_

char t[100] ;
char *p ;
char *end ;

p=&(t[0]) ;
end = p+100 ;
for(;p<end;p++) est alors interdit aussi je suppose.

Si le programme est a valeur didactique, comme par exemple quand tu
postes dans f.c.l.c, j'aurais tendance a penser que oui. Ou alors, tu
indiques clairement que le programme est mine jusqu'a la moelle, ce que
tu n'as pas (suffisament) fait.


Tu as vraiment cru que ce programme avait une valeur autre que
didactique (independamment du fait que tu as peut etre pense que
sa valeur didactique etait nulle, mais c'est un autre probleme) ?
Je connais des gens qui programment salement. Mais je n'ai encore jamais
vu qqn qui s'amuse a modifier des chaines constantes "pour de vrai".
Surtout que ce serait bien con, vu que, norme ou pas, ub ou pas,
ca peut faire un segfault, et la facon de creer les chaines constantes
depend du compilateur (ce qui etait d'ailleurs le point que j'illustrait
ici).

--
Christophe Le Gal

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', (ztn) wrote:

En fait, j'ai une structure très simple que je veux écrire dans un
fichier :

typedef struct{
char *txt;
int x;
}TEST;


Une structure non linéaire (avec pointeurs) n'est pas indiquée pour servir de
modèle d'enregistement. Sans précautions particulières, tu risques simplement
d'enregistrer une adresse, et non la valeur pointée.

Ecriture dans le fichier :

TEST *t = (TEST *)malloc(sizeof(TEST));


Une façon compliquée d'écrire :

TEST *t = malloc sizeof *t;

FILE *file;

t->txt = "pouet pouet";


Tu enregistres l'adresse d'une chaine litterale.

t->x = 257;

file = fopen("fichier_test", "rb");
write(file, t, sizeof(TEST));


Fonction inconnue. En C, on utilise fwrite().

fwrite (t, 1, sizeof *t, file);

Comme prévu, la recopie brutale d'une structure non linéaire se traduit par
des absurdités.

fclose(file);

Quand je regarde le fichier créé avec un éditeur héxadécimal, je
constate que ma chaine de caractere n'est pas enregistré, mais
seulement son adresse (je pense), car j'ai beau changer la chaine,
c'est toujour la meme chose d'enregistré. tandi que le int lui est
bien enregistré dans le fichier.

Pourrais-je avoir la bonne technique pour faire bien cette écriture ?


Il faut passer par une structure linéaire :

typedef struct
{
char txt[256]; /* par exemple */
int x;
}
record_s;


record_s rec;

strcpy (rec.txt, t.txt);
strcpy (rec.x, t.x);

<...>

fwrite (&rec, 1, sizeof rec, fp);

--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', "Stephane Legras-Decussy"
wrote:

typedef struct{
char *txt;
int x;
}TEST;

TEST *t = (TEST *)malloc(sizeof(TEST));
FILE *file;

t->txt = "pouet pouet";


probleme :

a aucun moment tu n'a reservé l'espace pour contenir
ta chaine "pouet pouet"...


Et ?

tu a juste reservé l'adresse de cette chaine...

tu dois soit reserver cet espace en statique de taille fixe, soit
en dynamique en faisant a un moment :

txt = malloc ( longueur de la chaine )

ensuite tu utiliseras strcpy( ) pour remplir la chaine avec "pouet pouet",
(pas de = )


quant ça marchera, tu pourras reflechir à la methode pour ecrire
tout ça dans un fichier...


Non. Le vrai problème est que la structure n'est pas linéaire, et que donc
fwrite() ne fonctionne pas. Il faut revoir la conception.

--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/


1 2 3 4