Bonjour, je me permets d'écrire mon premier sujet car je rencontre un problème dans un programme "plus conséquent". J'ai isolé la partie du code qui me pose problème. Le problème rencontrer est une exception non générée lors de l'écriture dans le fichier via fprintf. Merci à vous !
typedef struct dated {
int d;
int m;
int y;
} dated;
typedef struct user {
char *name[SIZE_NAME];
dated create;
} user;
void main()
{
user player, player_find; // actual player = player
FILE *files;
printf("Name : ");
gets(player.name);
files = "Register.txt";
if (fopen(files, "w") != NULL)
{
fprintf(files, "%s %02d.%02d.%d", player.name, player.create.d, player.create.m, player.create.y);
fclose(files);
}
else printf("error !");
_getch();
}
Le plus gros c'est que tu ne récupère pas le pointeur FILE retourné par fopen et tu t'en sers pour stocker une chaîne de caractère, alors qu'il n'est pas fait pour cela.
Le code fonctionne maintenant avec char *name[SIZE_NAME]; , quelle est la différence entre
char name[SIZE_NAME];
et
char *name[SIZE_NAME];
Grâce au pointeur sur le tableau, il sera possible d'écrire name = "Jean" par exemple tandis que sans le pointeur sur le tableau, on est obliger de faire l'acquisition de chaque lettre du nom dans une for ? Si oui, pourquoi me proposez-vous de supprimer * ?
Une chaîne de caractère se stocke dans un tableau de char pas dans un tableau de pointeur sur char. Au vu de ton code, ton but est bien de stocker une chaîne de caractère dans un tableau de char.
Merci de votre réponse ! J'ai relu mon cours. En effet, je me mélange les pinceaux car j'ai du mal à comprendre la notion de variables de types pointeur... Serait-il possible d'avoir des explications supplémentaires, voir des exemples afin de savoir leurs utilités et leurs fonctionnement ? Serait-il également possible de savoir comment les modifier dans une fonction ? Merci à vous.
Une explication qui me semble simple, je pense, c'est de dire qu'un pointeur est une variable destinée à contenir une adresse. Par exemple :
char *name[SIZE_NAME];
est un tableau dont les éléments sont de type char *, c'est-à-dire dont les éléments sont des adresses (plus précisément des adresses de variables contenant un char). Ces adresses peuvent être utiles ou non, ça dépend du programmeur...
Le meilleur cours sur les pointeurs à ma connaissance est celui-ci : https://www.labri.fr/perso/billaud/travaux/Pointeurs/pointeurs.html (attention, c'est complet, mais on apprend déjà plein de choses en lisant le début). Son auteur est un intervenant régulier du forum.
Imaginons que le texte d'un fichier (dont les lignes sont de longueur variable) soit mis dans un tableau de caractères appelé tabchar. C'est un tableau unidimensionnel : il contient les caractères à la suite, y compris les \n de passage à la ligne.
Une fonction pourrait alors analyser ce tableau et créer un tableau de pointeurs appelé tabligne contenant les adresses des débuts de chaque ligne. Le premier élément de ce tableau de pointeurs serait l'adresse du premier élément de tabchar. Ensuite, la fonction parcourt le tableau tabchar et, chaque fois qu'elle rencontre un \n, elle le remplace par un \0 et ajoute l'adresse de l'élément suivant dans notre tableau d'adresses (tabligne). On pourra alors écrire tabligne[n] pour accéder à la ligne numéro n du fichier, et ce sera géré comme une chaîne de caractères puisqu'on a remplacé \n par \0. Par exemple on pourra écrire printf("%s", tabligne[n]).
Du coup je me suis fait un petit T.P. pour profiter du 15 août, je trouvais l'idée intéressante.
Voici le programme (je l'ai fait à la va-vite, il y a sûrement des trucs à revoir, par exemple dans la dernière boucle j'ai dû mettre i <= numligne au lieu de i < numligne sinon je n'avais pas la dernière ligne, je ne sais pas trop pourquoi (*)) :
/* Découpage d'un fichier en lignes */
/* */
/* Ce programme lit un fichier et range les caractères dans un */
/* tableau unidimensionnel, puis le décompose en lignes. */
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#define TAILLEMAX 65536 // nombre maxi de caractères du fichier
#define LIGNEMAX 10000 // nombre maxi de lignes du fichier
void remplir(FILE *fichier, char tableau[], int *pnbchar)
// Remplit le tableau de caractères à partir du fichier
{
int i = 0 ; // compteur
int c ; // caractère courant
while (true)
{
c = fgetc(fichier) ;
if ((c == EOF) || (i >= TAILLEMAX))
break ;
else
{
tableau[i] = c ;
i++ ;
}
}
*pnbchar = i ;
}
void pointerlignes(char tabchar[], char *tabligne[], int nbchar, int *pnumligne)
// Remplit le tableau d'adresses des lignes à partir du tableau de caractères
{
// Première ligne :
*pnumligne = 0 ;
tabligne[0] = &tabchar[0] ;
// Les autres lignes :
for (int i = 0 ; i < nbchar ; i++)
{
if (tabchar[i] == '\n')
{ // le prochain caractère sera une nouvelle ligne...
tabchar[i] = '\0' ;
(*pnumligne)++ ;
// ... du moins si ce n'est pas le dernier caractère du fichier
if (i+1 < nbchar)
tabligne[*pnumligne] = &tabchar[i+1] ;
}
}
}
int main(void)
{
char nomfichier[256] ;
FILE *fichier ;
char tabchar[TAILLEMAX] ; // tableau contenant tous les caractères
char *tabligne[LIGNEMAX] ; // tableau des adresses de chaque ligne
int nbchar ; // nombre de caractères du fichier
int numligne ; // nombre de lignes du fichier
printf("Entrer le nom d'un fichier :\n") ;
printf("(Le fichier doit exister et faire 64 Kio maxi)\n") ;
printf("--> ") ;
scanf("%s", nomfichier) ;
fichier = fopen(nomfichier, "r") ;
if (fichier == NULL) return 1 ;
// Création du tableau unidimensionnel
remplir(fichier, tabchar, &nbchar) ;
// Création du tableau de pointeurs...
pointerlignes(tabchar, tabligne, nbchar, &numligne) ;
// ... ce qui permet d'afficher les lignes numérotées
for (int i = 0 ; i <= numligne ; i++)
{
printf("%3d : %s\n", i+1, tabligne[i]) ;
}
fclose(fichier) ;
return 0 ;
}
Et quand on applique ce programme sur le fichier source, ça donne :
robun@Ordi:~/prog/c/2019 > decfich
Entrer le nom d'un fichier :
(Le fichier doit exister et faire 64 Kio maxi)
--> decfich.c
1 : /* Découpage d'un fichier en lignes */
2 : /* */
3 : /* Ce programme lit un fichier et range les caractères dans un */
4 : /* tableau unidimensionnel, puis le décompose en lignes. */
5 :
6 : #include <stdbool.h>
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 : #define TAILLEMAX 65536 // nombre maxi de caractères du fichier
10 : #define LIGNEMAX 10000 // nombre maxi de lignes du fichier
11 :
12 : void remplir(FILE *fichier, char tableau[], int *pnbchar)
13 : // Remplit le tableau de caractères à partir du fichier
14 : {
15 : int i = 0 ; // compteur
16 : int c ; // caractère courant
17 : while (true)
18 : {
19 : c = fgetc(fichier) ;
20 : if ((c == EOF) || (i >= TAILLEMAX))
21 : break ;
22 : else
23 : {
24 : tableau[i] = c ;
25 : i++ ;
26 : }
27 : }
28 : *pnbchar = i ;
29 : }
30 :
31 : void pointerlignes(char tabchar[], char *tabligne[], int nbchar, int *pnumligne)
32 : // Remplit le tableau d'adresses des lignes à partir du tableau de caractères
33 : {
34 : // Première ligne :
35 : *pnumligne = 0 ;
36 : tabligne[0] = &tabchar[0] ;
37 : // Les autres lignes :
38 : for (int i = 0 ; i < nbchar ; i++)
39 : {
40 : if (tabchar[i] == '\n')
41 : { // le prochain caractère sera une nouvelle ligne...
42 : tabchar[i] = '\0' ;
43 : (*pnumligne)++ ;
44 : // ... du moins si ce n'est pas le dernier caractère du fichier
45 : if (i+1 < nbchar)
46 : tabligne[*pnumligne] = &tabchar[i+1] ;
47 : }
48 : }
49 : }
50 :
51 : int main(void)
52 : {
53 : char nomfichier[256] ;
54 : FILE *fichier ;
55 : char tabchar[TAILLEMAX] ; // tableau contenant tous les caractères
56 : char *tabligne[LIGNEMAX] ; // tableau des adresses de chaque ligne
57 : int nbchar ; // nombre de caractères du fichier
58 : int numligne ; // nombre de lignes du fichier
59 : printf("Entrer le nom d'un fichier :\n") ;
60 : printf("(Le fichier doit exister et faire 64 Kio maxi)\n") ;
61 : printf("--> ") ;
62 : scanf("%s", nomfichier) ;
63 : fichier = fopen(nomfichier, "r") ;
64 : if (fichier == NULL) return 1 ;
65 : // Création du tableau unidimensionnel
66 : remplir(fichier, tabchar, &nbchar) ;
67 : // Création du tableau de pointeurs...
68 : pointerlignes(tabchar, tabligne, nbchar, &numligne) ;
69 : // ... ce qui permet d'afficher les lignes numérotées
70 : for (int i = 0 ; i <= numligne ; i++)
71 : {
72 : printf("%3d : %s\n", i+1, tabligne[i]) ;
73 : }
74 : fclose(fichier) ;
75 : return 0 ;
76 : }
robun@Ordi:~/prog/c/2019 >
(*) Ah : à la ligne 38 j'aurais dû initialiser i = 1 je crois.
Merci, j'ai tout compris grâce à votre exemple ! C'est effectivement très fort Cependant, pourriez-vous me dire à quoi sert le #include <stdbool.h> ? Merci beaucoup !
Je m'en suis servi uniquement pour écrire while (true), qui est une boucle dont on ne sort que par une instruction break. Sinon on peut faire while (1), mais true est plus clair je trouve. C'est une façon un peu bizarre d'écrire des boucles, mais je la trouve pratique. (Les pros du C feraient plutôt while (c = fgetc(fichier) != EOF) mais je n'aime pas mélanger deux instructions en une, ça m'embrouille...)
La boucle infinie : autrefois, on écrivait for(;;){...}, sachant que la condition (second paramètre) du for est vraie par défaut. C'est une des premières choses qu'on apprenait : Infinite loop, kernighan et ritchie 2nd ed. ,p60
De nos jours, on joue moins à faire du "code cryptique" sous prétexte d'optimisation, les compilateurs font ça mieux que les programmeurs. Une version clean pour une boucle sur un texte :
for(;;) { // ou while(true)
int c = getchar();
if (c == EOF) break;
...
}
qui a l'intérêt de limiter la portée de la déclaration de c.
Mais bon, si on y tient, on peut aussi se faire une macro
qui définit une boucle "pour tout caractère, faire".
A quoi sert stdbool.h ? Suffit de regarder dans le fichier stdbool.h (/usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h, par exemple)
#define bool _Bool
#define true 1
#define false 0
En résumé : ça définit
deux constantes true et false
le type bool comme redéfinition de _Bool
_Bool est le type "officiel" pour les booléens, depuis C99.
---
La question qu'on peut poser, c'est pourquoi le type officiel des booléens, c'est pas bool ?
Pour ça il faut revenir aux débuts de C. La philosophie de l'époque, c'était
> pour les booléens, y a pas besoin de type spécial au niveau du compilateur. Déja 1) le compilateur considère comme vrai tout ce qui n'est pas zero, 2) vous le collez dans un entier ou un champ de bits de structure, et ça vous fait l'affaire. Démerdez-vous avec.
En pratique, les programmeurs aimaient quand même bien se définir un type bool, ou boolean avec deux constantes true/false. Ou True/False. Ou TRUE/FALSE. Dans un int, un short, ou un char. Ou alors une enumération. Avec un consensus pour que false soit 0, mais pour true, ça pouvait être 1 ou -1 (tous les bits à 1).
Donc on se retrouve, dans un programme, à utiliser des bibliothèques qui n'ont pas les mêmes conventions pour représenter les booléens. C'est le bordel.
Un autre problème, c'est que si un booléen n'est pas égal à faux, c'est pas pour autant qu'il est égal à vrai (la constatnte qui représente vrai) :
typef int bool;
#define true 1
#define false 0
bool truc = 3; // vrai parce que non nul
if (truc == true) {
// ah non, on ne passe pas par là
}
D'où la nécessité faire quelque chose. En C99, le comité a donc introduit un type _Bool géré par le compilateur, qui s'occupe de "normaliser la représentation". C'est à dire que si on affecte quelque chose qui n'est pas faux dans un booléen, il est rectifié à sa valeur standard qui est "true". De façon à ce qu'une variable booléenne ne puisse prendre que DEUX valeurs.
Pourquoi l'avoir appelé _Bool plutôt que bool ? Parce que chaque code existant ou presque avait déjà défini le type bool (voir plus haut), et que ça aurait sévèrement clashé à la première compilation. Quand on fait évoluer un langage, il faut essayer de rester compatible...
Donc stratégie :
le type _Bool est prédéfini et généré par le compilateur
pour ceusses qui n'en veulent, on fournit bool, true et false via un #include <stdbool.h>
1. Il était mal foutu au départ (les tableaux et les chaines de caractères sont un désastre), avec une philosophie qui n'est plus du tout au goût du jour (qui s'est avérée ruineuse en coût de développement/maintenance - quand vous entendez parler de fuites mémoire ou d'attaque par débordement de tampon, pas besoin de trop réfléchir pour savoir dans quel langage ça a été programmé )
2. les tentatives pour l'arranger sont bloquées par la masse de code existant, et n'ont pas rattrapé le coup.
Bonjour à tous ! Ne trouvant pas sur internet, je me demandais si par hasard il serait possible de calculer la dimension d'un tableau sur base de la taille d'un fichier ? Je voudrais donc pouvoir avoir un tableau qui fait la taille de mon fichier. Serait-il possible de savoir comment faire ? Merci à tous et bon week-end !
int size_array = sizeof(files);
char array[size_array];
Bonjour, je me permets de poser une question qui est surement débile mais je suppose qu'il n'est pas possible de faire le calcul de la dimension d'un tableau a chaque passage dans une boucle do while sachant que la réservation des emplacements mémoire se fait lors du débogage ?
- Edité par VHS 18 août 2019 à 12:12:12
Problème d'écriture dans un fichier ( C )
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent