N'oublie pas le tag si ton problème l'est
Pour lire_double(), il faut mettre errno a 0 avant d'appeler strtod(), tester errno == ERANGE (erreur)...Envoyé par odsen.s
Pour les 3 fonctions : vérifier aussi que la valeur est retourné via un pointeur <> NULL...
Ok. errno prend comme valeur ERANGE quand on veut mettre dans le double trop de chiffres, c'est ça ?Envoyé par Emmanuel Delahaye
Je ne comprend pas bien. De quelle valeur parles-tu ?Pour les 3 fonctions : vérifier aussi que la valeur est retourné via un pointeur <> NULL...
Des fonctions comme strtol() modifient des pointeurs : Il ne faut pas tester la valeur pointée avant d'avoir vérifié que le pointeur en question n'est pas NULL.
D'accord. Quelle valeur renvoyer si ce pointeur vaut NULL ? (A quelle erreur de la fonction strto* ça correspond ?)
EDIT : la fonction lire_double n'a pas l'air de fonctionner correctement :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 123456789.987654321 [saisie du double] err : 0, lf : 123456789.9876543300
Plus exactement, lorsque la valeur est trop grande pour être convertie.Envoyé par odsen.s
Pour récupérer la valeur convertie, tu as décidé de passer un pointeur à la fonction.Je ne comprend pas bien. De quelle valeur parles-tu ?
Pourquoi pas (je l'aurais appelé 'pl' au lieu de 'l').
Code : Sélectionner tout - Visualiser dans une fenêtre à part int lire_long(long *l)
Il faut, avant de déréférencer ce pointeur, s'assurer qu'il est différent de NULL.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 if (l != NULL) { *l = ...
Normal. Les doubles servent à obtenir de grandes valeurs mais c'est au détriment de la précision.Envoyé par odsen.s
Ok, merci bien.
Voici ce que ça donne au final :
As-tu des remarques ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 /* lire_long : lit une chaine de caracteres et convertit en long */ int lire_long(long *p_l) { int err; if(p_l != NULL) { char temp[NB_CAR_LONG] = {0}; if((err = lire_ligne(temp, sizeof temp)) == ENTREE_OK) { char *p_conversion = NULL; *p_l = strtol(temp, &p_conversion, 10); if(p_conversion != NULL) { if(*p_conversion != 0) /* format incorrect */ { err = ERR_FORMAT; } } } } else { err = ERR_P_NULL; } return err; } /* lire_ulong : lit une chaine de caracteres et convertit en long non signé */ int lire_ulong(unsigned long *p_ul) { int err; if(p_ul != NULL) { char temp[NB_CAR_LONG] = {0}; if((err = lire_ligne(temp, sizeof temp)) == ENTREE_OK) { char *p_conversion = NULL; *p_ul = strtoul(temp, &p_conversion, 10); if(p_conversion != NULL) { if(*p_conversion != 0) /* format incorrect */ { err = ERR_FORMAT; } } } } else { err = ERR_P_NULL; } return err; } /* lire_double : lit une chaine de caracteres et convertit en double */ int lire_double(double *p_lf) { int err; if(p_lf != NULL) { char temp[512] = {0}; if((err = lire_ligne(temp, sizeof temp)) == ENTREE_OK) { char *p_conversion = NULL; errno = 0; *p_lf = strtod(temp, &p_conversion); if(p_conversion != NULL) { if(*p_conversion == 0) /* format correct ? */ { if(errno == ERANGE) /* valeur trop grande */ { err = ERR_VAL; } } else { err = ERR_FORMAT; } } } } else { err = ERR_P_NULL; } return err; }
A part que tu piges vite, pas vraiment !Envoyé par odsen.s
Si, une remarque, je n'aime pas trop la combinaison de if et de =. J'évite...
Je conseille ceci :
Il faut écrire du code de test qui permet de valider l'affaire.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 #include <stdio.h> #include <stdlib.h> #include <errno.h> enum { ENTREE_OK, ERR_TAILLE_SAISIE, ERR_TAILLE_TAB, ERR_P_NULL, ERR_FORMAT, ERR_VAL, ERR_NB }; #define NB_CAR_LONG 64 void vider_stdin (void) { int c; while ((c = getchar ()) != '\n' && c != EOF); } /* lire_ligne : lit une ligne sur l'entree standard et l'écrit dans un tableau de caractères */ int lire_ligne (char *s, size_t t) { int c; int err; size_t i; /* le pointeur doit contenir une adresse */ if (s != NULL) { /* il faut au moins pouvoir écrire un caractère */ if (t > 0) { i = 0; /* on arrête la lecture à la fin de ligne, ou lorsque qu'il ne reste de la place que pour le caractere de fin de chaîne */ while ((c = getchar ()) != '\n' && c != EOF && i < t - 1) { s[i++] = c; } /* ajout du caractère de fin de chaîne */ s[i] = 0; /* s'est-t'on arrêté de lire car on était à la fin de la ligne ou parce qu'il fallait réserver une place pour le caractère de fin de chaîne ? */ if (c == '\n' || c == EOF) { err = ENTREE_OK; } else { /* il reste sans doute des caractères dans le flux d'entrée, il faut alors le vider */ vider_stdin (); err = ERR_TAILLE_SAISIE; } } else { err = ERR_TAILLE_TAB; } } else { err = ERR_P_NULL; } return err; } /* lire_long : lit une chaine de caracteres et convertit en long */ int lire_long (long *p_l) { int err; if (p_l != NULL) { char temp[NB_CAR_LONG] = { 0 }; err = lire_ligne (temp, sizeof temp); if (err == ENTREE_OK) { char *p_conversion = NULL; *p_l = strtol (temp, &p_conversion, 10); if (p_conversion != NULL) { if (*p_conversion != 0) /* format incorrect */ { err = ERR_FORMAT; } } } } else { err = ERR_P_NULL; } return err; } /* lire_ulong : lit une chaine de caracteres et convertit en long non signé */ int lire_ulong (unsigned long *p_ul) { int err; if (p_ul != NULL) { char temp[NB_CAR_LONG] = { 0 }; err = lire_ligne (temp, sizeof temp); if (err == ENTREE_OK) { char *p_conversion = NULL; *p_ul = strtoul (temp, &p_conversion, 10); if (p_conversion != NULL) { if (*p_conversion != 0) /* format incorrect */ { err = ERR_FORMAT; } } } } else { err = ERR_P_NULL; } return err; } /* lire_double : lit une chaine de caracteres et convertit en double */ int lire_double (double *p_lf) { int err; if (p_lf != NULL) { char temp[512] = { 0 }; err = lire_ligne (temp, sizeof temp); if (err == ENTREE_OK) { char *p_conversion = NULL; errno = 0; *p_lf = strtod (temp, &p_conversion); if (p_conversion != NULL) { if (*p_conversion == 0) /* format correct ? */ { if (errno == ERANGE) /* valeur trop grande */ { err = ERR_VAL; } } else { err = ERR_FORMAT; } } } } else { err = ERR_P_NULL; } return err; } int main (void) { char s[5]; int err; do { err = lire_ligne (s, sizeof s); if (!err) { printf ("'%s'\n", s); } else { printf ("erreur %d\n", err); } } while (err || *s != 0); /* etc. */ return 0; }
J'ai fait ceci pour tester l'ensemble des fonctions :
Rien d'incohérent aux tests, hormis que j'arrive à stocker un nombre négatif dans la variable ul
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include <stdio.h> #include <string.h> #include "entrees.h" int main(void) { char s[5] = {0}; char c; long l = 0; unsigned long ul = 0; double lf = 0.0; while(strcmp(s,"quit") != NULL) { printf("err : %d, s : \"%s\"\n", lire_ligne(s, sizeof s), s); printf("c : %c\n", lire_car()); printf("err : %d, l : %ld\n", lire_long(&l), l); printf("err : %d, ul : %ld\n", lire_long(&ul), ul); printf("err : %d, lf : %.10f\n", lire_double(&lf), lf); } return 0; }
Sinon, j'ai entendu parler à plusieurs reprises de "test unitaire".
En quoi ça consiste exactement ?
Ca peut s'appliquer ici ?
Je pense que ces appels à printf() sont à éviter, car l'ordre d'évaluation des paramètres n'est pas garanti, et donc rien ne dit que ul ne sera pas évalué avant l'appel à lire_ulong() par exemple. Mieux vaut séparer en deux appels.Envoyé par odsen.s
C'est normal, vous appelez lire_long(&ul) au lieu de lire_ulong(&ul). En outre, le format dans printf() doit être:Envoyé par odsen.s
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 printf("err : %d, ",lire_ulong(&ul)); printf("ul : %lu\n", ul);
Ok.Mieux vaut séparer en deux appels.
Ah ben oui, en effetC'est normal, vous appelez lire_long(&ul) au lieu de lire_ulong(&ul). En outre, le format dans printf() doit être:
Code :
printf("err : %d, ",lire_ulong(&ul)); printf("ul : %lu\n", ul);
Merci.
EDIT: en corrigeant mon code de test, il y a toujours un problème. Si j'essaie d'entrer un nombre négatif pour ul, l'erreur n'est pas détectée (err = 0) et la variable prend une valeur inatendue :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 -1 [saisie clavier] err : 0, ul : 4294967295
Ce n'est pas une erreur, c'est une 'feature'... strtoul() convertit les nombres négatifs en valeurs non signées. Ce comportement est défini par la norme.Envoyé par odsen.s
Si tu veux interdire ça, il faut chercher le '-' dans la chaine avant d'appeler strtoul().
Hop, je préfère comme ça :
Ca semble fonctionner sans soucis désormais, merci à tous
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 /* lire_ulong : lit une chaine de caracteres et convertit en long non signé */ int lire_ulong(unsigned long *p_ul) { int err; if(p_ul != NULL) { char temp[NB_CAR_LONG] = {0}; err = lire_ligne(temp, sizeof temp); if(err == ENTREE_OK) { if(strchr(temp,'-') == NULL) /* pas de négatif */ { char *p_conversion = NULL; *p_ul = strtoul(temp, &p_conversion, 10); if(p_conversion != NULL) { if(*p_conversion != 0) /* format incorrect */ { err = ERR_FORMAT; } } } else { err = ERR_FORMAT; } } } else { err = ERR_P_NULL; } return err; }
J'ai rassemblé sur une page de mon site tout le travail effectué dans ce sujet :
http://benoit.aun.free.fr/article_c_entrees_fiables.php
Ca pourra toujours aider les membres qui cherchent à saisir correctement le clavier, notamment avec les valeurs numériques
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager