Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

appels systèmes read() et write()

9 réponses
Avatar
swoog
Bonjour,

J'ai un petit problème avec mon code.
Je dois écrire dans un fichier un tableau de structure dans un premier temps,
puis relire ce fichier et afficher les données dans la console. Cependant, je
pédale dans la semoule, car les appels système ne sont pas mon fort (en réalité,
je n'ai pas vraiment tout compris).

Pour info, j'exécute ce programme sur une distribution UBUNTU.

Voici mon code, en espérant que vous pourrez corriger mes erreurs et me guider
dans la résolution de cet exercice.

1 #include <stdio.h>
2 #include <fcntl.h>
3
4 #define DIM 1000
5
6 struct complexe
7 {
8 char c;
9 int i;
10 float f;
11 };
12
13 int main()
14 {
15 struct complexe tableau[DIM];
16 int i, fichier;
17
18
19 for( i = 0 ; i < DIM ; i++ )
20 {
21 tableau[i].i = i;
22 tableau[i].c = (char)i;
23 tableau[i].f = (float)i;
24 }
25
26 fichier = open("test2", O_WRONLY|O_CREAT);
27
28 for( i = 0 ; i < DIM ; i++ )
29 write(fichier, tableau[i], sizeof(struct complexe));
30
31 close(fichier);
32
33 open("test2", O_RDONLY);
34
35 for( i = 0 ; i < DIM ; i++)
36 {
37 char *tampon;
38 read(fichier, tampon, sizeof(struct complexe));
39 printf("ligne %d : %s\n", i, tampon);
40 }
41 close(fichier);
42
43 }

9 réponses

Avatar
Mickaël Wolff
swoog wrote:

Pour info, j'exécute ce programme sur une distribution UBUNTU.



Quelle ligne de commande as-tu utilisé pour compiler ton source ?
Quels sont les nombreux messages d'erreur et d'avertissement que le
compilateur a crié ?

Voici mon code, en espérant que vous pourrez corriger mes erreurs et me guider
dans la résolution de cet exercice.



[snip]
35 for( i = 0 ; i < DIM ; i++)
36 {
37 char *tampon;
38 read(fichier, tampon, sizeof(struct complexe));



struct complexe tampon ;
read(fichier, (char *) &tampon, sizeof tampon) ;

Tu devais relire les manuels de read et write, tu as oublié quelques
includes. De plus, tu ne teste aucun retour de fonctions, ce qui peut
conduire à de droles de comportements.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Samuel Devulder
swoog a écrit :

25
26 fichier = open("test2", O_WRONLY|O_CREAT);
27
28 for( i = 0 ; i < DIM ; i++ )
29 write(fichier, tableau[i], sizeof(struct complexe));



Tu devrais passer l'adresse de la valeur à sauvegarder:
write(fichier, &tableau[i], sizeof tableau[0]);
(il serait bien de vérifier que tout s'est bien déroulé dans le write).

30
31 close(fichier);
32
33 open("test2", O_RDONLY);
34
35 for( i = 0 ; i < DIM ; i++)
36 {
37 char *tampon;
38 read(fichier, tampon, sizeof(struct complexe));



tampon pointe sur aucune zonne de mémoire allouée! Ce que tu as écrit va
ecrire n'importe où en mémoire. Il faut là encore passer l'adresse de la
zone mémoire qui va recevoir les données. Le mieux est d'allouer une
seule valeur de struct complexe et de l'utiliser comme tampon d'entrée:
{
struct complexe tampon;
read(fichier, (char*)&tampon, sizeof(tampon));
(note: idem il faudrait vérifier que l'on a bien lu sizeof(tampon)).

39 printf("ligne %d : %sn", i, tampon);



heu.. ici tu considère que tampon est une chaine, or c'est une struct...
Ca n'a pas de sens. Tu veux peut-etre écrire:
printf("ligne %d : c=%c i=%d ...n", i, tampon.c, tampon.i);
Note: j'ai volontairement oublié d'imprimer le tampon.f... c'est pas le
sujet ici.

40 }
41 close(fichier);
42
43 }



sam.
Avatar
Thierry B
On 2009-09-22, swoog wrote:
32
33 open("test2", O_RDONLY);
34



EPIC FAIL !

--
Collection de bugs:
http://wwwzenger.informatik.tu-muenchen.de/persons/huckle/bugse.html
Avatar
-ed-
On 23 sep, 01:15, swoog wrote:
Voici mon code, en espérant que vous pourrez corriger mes erreurs et me guider
dans la résolution de cet exercice.

  1 #include <stdio.h>
  2 #include <fcntl.h>
  3
  4 #define DIM 1000
  5
  6 struct complexe
  7 {
  8         char c;
  9         int i;
 10         float f;
 11 };



C'est du BASIC ?


-------------- Build: Debug in hello ---------------

Compiling: main.c
Linking console executable: binDebughello.exe
C:devhellomain.c:1: error: syntax error before numeric constant
C:devhellomain.c:1: error: syntax error at '#' token
C:devhellomain.c:2: error: syntax error at '#' token
C:devhellomain.c:4: error: syntax error at '#' token
C:devhellomain.c:11: warning: ISO C does not allow extra `;'
outside of a function
Process terminated with status 1 (0 minutes, 1 seconds)
4 errors, 1 warnings

Retirons ces numéros de lignes ...


-------------- Build: Debug in hello ---------------

Compiling: main.c
Linking console executable: binDebughello.exe
C:devhellomain.c:14: warning: function declaration isn't a
prototype
C:devhellomain.c: In function `main':
C:devhellomain.c:29: error: incompatible type for argument 2 of
`write'
Process terminated with status 1 (0 minutes, 0 seconds)
1 errors, 1 warnings

on y voit déjà plus clair...

Ceci-dit, il n'y a pas de fonction read() ni write() e langage C. Ce
sont des fonctions du styème Unix (et des tout système compatible
POSIX.1). Leur usage est rarement justifié, mais si tu y tiens, la doc
est là :

http://www.opengroup.org/onlinepubs/000095399/functions/write.html
http://www.opengroup.org/onlinepubs/000095399/functions/read.html

struct complexe tableau[DIM];
...
for( i = 0 ; i < DIM ; i++ )
write(fichier, tableau[i], sizeof(struct complexe));



C'est l'adresse des données qu'il faut passer :

write(fichier, tableau + i, sizeof(struct complexe));

open ("test2", O_RDONLY);



Il fait récupérer (et tester) la valeur retournée par open(). Elle es t
ensuite utilisée par read() et close().

char *tampon;
read(fichier, tampon, sizeof(struct complexe));



Ceci est bien horrible !

tampon est un pointeur non initialisé. Tu passes donc une valeur
indéterminée à une fonction. Le comportement est indéterminé

En supposant que l'écriture et la lecture se fasse sur la même
machine, et dans les mêmes conditions (ce qui est le cas ci, mais
c'est un cas très particulier et plutôt rare dans la pratique), tu
peux utiliser le même type de structure à la lecture qu'à
l'enregistrement. Il suffit donc de définir ceci :

read (fichier, &tampon, sizeof tampon);

Je recommande une séparation totale entre le code d'écriture et le
code de lecture. Ca évite les erreurs de recyclage de variables ayant
une valeur erronée

Enfin, pour valider un principe, il vaut mieux se limiter à une
dizaine de valeurs que l'on peut vérifier visuellement rapidement...

#include <stdio.h>
#include <fcntl.h>

#define DIM 10

struct complexe
{
char c;
int i;
float f;
};

int main (void)
{
/* write */
{
struct complexe tableau[DIM];
{
int i;
for (i = 0; i < DIM; i++)
{
tableau[i].i = i;
tableau[i].c = (char) i;
tableau[i].f = (float) i;
}
}
{
int fichier = open ("test2", O_WRONLY | O_CREAT);
if (fichier != -1)
{
int i;
for (i = 0; i < DIM; i++)
write (fichier, tableau + i, sizeof *tableau);

close (fichier);
}
}
}

/*read */
{
int fichier = open ("test2", O_RDONLY);
if (fichier != -1)
{
int i;
for (i = 0; i < DIM; i++)
{
struct complexe tampon;
read (fichier, &tampon, sizeof tampon);
printf ("ligne %d : %d, '%c', %fn", i, tampon.i,
tampon.c,
tampon.f);
}
close (fichier);
}
}
return 0;
}
Avatar
-ed-
On 23 sep, 06:28, Mickaël Wolff wrote:

struct complexe tampon ;
read(fichier, (char *) &tampon, sizeof tampon) ;



A quoi sert le cast ? Le type attendu par read() est void*, non ?

ssize_t read(int fildes, void *buf, size_t nbyte);

--
Mickaël Wolff aka Lupus Michaelishttp://lupusmic.org


Avatar
Pierre Maurette
swoog, le 23/09/2009 a écrit :
Bonjour,



Bonjour,

Je suis dans la continuité de la réponse de -ed- donc je ne reviens pas
sur ce qui a été dit.
Votre but était semble-t-il de copier puis relire l'image mémoire de
votre tableau de structures. Cette solution a des avantgages et des
inconvénients. Pour profiter de ces avantages, il faudrait alors sauver
d'un bloc le tableau de structures, un seul write(). Puis le recharger
d'un bloc, ou charger une structure particulière.

L'inconvénient c'est que cette méthode n'est pas adaptée aux échanges,
ni même aux changements de version d'ailleurs. Le même programme
compilé avec un changement dans la taille des int, char ou float, voire
un padding ou un boutisme différent, et ça pourra ne pas fonctionner.
Le choix est une question d'utilisation.

Une autre solution est donc de sérialiser vos données sous forme de
texte. Lisible par l'homme est d'autres langages c'est mieux. Voici un
premier jet, à partir de la version de -ed-, avec la contrainte de
n'utiliser que open(), read() et write():

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define DIM 10

struct complexe
{
char c;
int i;
float f;
};

int
main(void)
{
char s[60] = "";
puts("write 1");
{
struct complexe tableau[DIM];
{
int i;
for (i = 0; i < DIM; i++)
{
tableau[i].i = i;
tableau[i].c = '0' + (char) i;
tableau[i].f = (float) i;
}
}
{
int fichier = open("test2", O_CREAT | O_WRONLY, 0644);
if (fichier != -1)
{
int i;
for (i = 0; i < DIM; i++)
{
sprintf(s, "%d %d %fn", tableau[i].i, tableau[i].c,
tableau[i].f);
write(fichier, s, strlen(s));
printf("%s", s);
}

close(fichier);
}
}
}

puts("read");
{
int fichier = open("test2", O_RDONLY);
if (fichier != -1)
{
int i, j;
for (i = 0; i < DIM; i++)
{
struct complexe tampon;
j = 0;
do
{
read(fichier, &s[j], 1);
}
while (s[j++] != 'n');
sscanf(s, "%d %c %fn", &tampon.i, &tampon.c, &tampon.f);
printf("ligne %d : %d, '%c', %fn", i, tampon.i,
tampon.c,
tampon.f);
}
close(fichier);
}
else
{
printf("%in", errno);
}
}
return 0;
}

--
Pierre Maurette
Avatar
Pierre Maurette
(supersedes )

swoog, le 23/09/2009 a écrit :
Bonjour,



Bonjour,

Je suis dans la continuité de la réponse de -ed- donc je ne reviens pas
sur ce qui a été dit.
Votre but était semble-t-il de copier puis relire l'image mémoire de
votre tableau de structures. Cette solution a des avantgages et des
inconvénients. Pour profiter de ces avantages, il faudrait alors sauver
d'un bloc le tableau de structures, un seul write(). Puis le recharger
d'un bloc, ou charger une structure particulière.

L'inconvénient c'est que cette méthode n'est pas adaptée aux échanges,
ni même aux changements de version d'ailleurs. Le même programme
compilé avec un changement dans la taille des int, char ou float, voire
un padding ou un boutisme différent, et ça pourra ne pas fonctionner.
Le choix est une question d'utilisation.

Une autre solution est donc de sérialiser vos données sous forme de
texte. Lisible par l'homme est d'autres langages c'est mieux. Voici un
premier jet, à partir de la version de -ed-, avec la contrainte de
n'utiliser que open(), read() et write():

[Je corrige une erreur en centralisant la définition de la chaîne de
format. Il faut décider si le char de la structure est un caractère ou
un entier. Si c'était un entier, il faudrait préciser son signe, signed
char ou unsigned char. C'est donc un caractère, et il peut y avoir des
problèmes avec certaines valeurs non imprimables. J'avais d'ailleurs
initialisé à '0' + i.]

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define DIM 10

struct complexe
{
char c;
int i;
float f;
};

int
main(void)
{
char s[128] = "";
const char* format = "%d %c %fn";
puts("write 1");
{
struct complexe tableau[DIM];
{
int i;
for (i = 0; i < DIM; i++)
{
tableau[i].i = i;
tableau[i].c = '0' + i;
tableau[i].f = (float) i;
}
}
{
int fichier = open("test2", O_CREAT | O_WRONLY, 0644);
if (fichier != -1)
{
int i;
for (i = 0; i < DIM; i++)
{
sprintf(s, format, tableau[i].i, tableau[i].c,
tableau[i].f);
write(fichier, s, strlen(s));
printf("%s", s);
}

close(fichier);
}
}
}

puts("read");
{
int fichier = open("test2", O_RDONLY);
if (fichier != -1)
{
int i, j;
for (i = 0; i < DIM; i++)
{
struct complexe tampon;
j = 0;
do
{
read(fichier, &s[j], 1);
}
while (s[j++] != 'n');
sscanf(s, format, &tampon.i, &tampon.c, &tampon.f);
printf("ligne %d : %d, '%c', %fn", i, tampon.i,
tampon.c,
tampon.f);
}
close(fichier);
}
else
{
printf("%in", errno);
}
}
return 0;
}

--
Pierre Maurette
Avatar
Mickaël Wolff
-ed- wrote:

struct complexe tampon ;
read(fichier, (char *) &tampon, sizeof tampon) ;



A quoi sert le cast ? Le type attendu par read() est void*, non ?



Au temps pour moi, j'avais lu char * dans le man.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
espie
1/ je ne suis pas sur que lui faire completement son exercice soit une
bonne idee.

2/ comme d'autres l'ont deja mentionne, un peu plus d'attention a ce qu'on
fait devrait eviter la majorite des problemes. On a ici un code que je
qualifierais de "code Shadock": je fais n'importe quoi sans comprendre en
esperant que ca finira par marcher. La principale chose qui va aider, c'est
plus de rigueur: demander des avertissements au compilo, prendre ces
avertissements en compte, et relire attentivement ce qu'on ecrit.

3/ de facon generale, il faudra a un moment ou un autre comprendre comment
marche la memoire...

l'image mentale que je me fais de read et write, c'est un truc qui copie
des donnees entre une zone memoire et le disque. Il faut que la zone
memoire soit bien definie (en particulier allouee). D'ou necessite d'avoir
un vrai tampon, et pas juste un pointeur qui ne pointe sur rien.

Genre:

char t[256];

n1 = read(rfd, t, 256); va lire n <= 256 octets dans le tampon.

et
n2 = write(wfd, t, 256); va essayer d'ecrire 256 octets depuis le tampon
(et va dire combien il a ecrit).

Le truc le plus vicieux avec les entrees-sorties unix, c'est qu'on ne
peut pas presumer des valeurs de retour de read et write: sans de grosses
hypotheses supplementaires, on peut tres bien avoir n1 < 256 ou n2 < 256.

Il faut "coder defensif", en consequence...