OVH Cloud OVH Cloud

Structures, programmes et commentaires

9 réponses
Avatar
Eddahbi Karim
Bonjour, Bonsoir (Arracher la mention inutile)

Me revoilà avec mes exercices (et oui encore).
Cette fois ci j'attaque les structures, le but de l'exercice était de
faire une structure pour gérer les données d'un mailing et de les
afficher.

J'ai pris le problème comme si il fallait ajouter une nouvelle entrée dans
une base de données gérant les mailing

Le programme à été réduit (car les fonctions résument à peu près la
méthode pour prendre n'importe quelle valeur nécessaire à un mailing).

Donc voici le code source, si vous avez une réflexion, un commentaire ou
autre n'hésitez pas :)

/*-8<-----------------------------------------------------------------

mailing-entry -- This program let you add a complete entry in
the mailing database, including : address and
postal_code for the moment.

Author : Eddahbi Karim
E-mail : non.tu.ne.me.connais.pas.spavrai@ifrance.com
Nick : ThE_TemPLaR (forum, newsgroups), gamer (IRC)

----------------------------------------------------------------------

Usage : Launch the program and enter the informations needed to
add an entry in the mailing database.

----------------------------------------------------------------->8-*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*********************************************************************

NAME : wipestring( string )

USAGE : Wipe useless values from the string...

PARAMETERS : char const *string to wipe...

*********************************************************************/

void
wipestring (char const *string)
{
char *p = strchr (string, '\n');

if (p)
{
*p = '\0';
}
else
{
int c;

while ((c = getchar ()) != '\n' && c != EOF)
{
}
}
}

/*********************************************************************

NAME : verify ( string, size, function_name )

USAGE : verify if the user input is correct

PARAMETERS :
- The string to check and to fill if the user enter something
- The size of the string
- The name of the function to give accurate error messages

RETURNS :
- EXIT_SUCCESS
- EXIT_FAILURE

*********************************************************************/

int
verify (char *string, size_t size, const char *function_name)
{
/* The error number */
int err = EXIT_SUCCESS;

/* Check if there's an input */
if (fgets (string, (int) size, stdin) == NULL)
{
printf ("Input Error at %s\n", function_name);
err = EXIT_FAILURE;
}

/* Check if the user entered something */
else
{
wipestring (string);

if (*string == '\0')
{
printf ("No input at %s\n", function_name);
err = EXIT_FAILURE;
}
}

return err;
}

/*********************************************************************

NAME : takeadress ( address, size, name )

PARAMETERS :
- The address
- The size of this string
- Its name

RETURNS : See verify()

*********************************************************************/

int
takeadress (char *address, size_t size, const char *name)
{

/* The return value */
int ret = 0;

(void) printf ("Please enter the address of the person : ");
(void) fflush (stdout);

ret = verify (address, size, name);

/* Check if the value is correct */
(void) printf ("The address is %s\n", address);

return ret;
}

/*********************************************************************

NAME : take_postalcode ( name )

PARAMETERS : Name of the function.

RETURNS : The postal code entered by the user

*********************************************************************/

int
take_postalcode (const char *name)
{
/* The postal code */
unsigned int postal_code = 0;

/* User input */
char input[10];

/* Ask the postal code until the user gives one */
do
{
(void) printf ("Insert the postal code of the user city : ");
(void) fflush (stdout);

if (verify (input, sizeof (input), name) == EXIT_FAILURE)
{
postal_code = 0;
break;
}
}
while (sscanf (input, "%u", &postal_code) != 1);

/* Check if the value is correct */
(void) printf ("The postal code is : %d\n", postal_code);

return postal_code;
}

int
main (void)
{

/* Mailing list structure */
struct mailing_list
{
/* The address and its according postal_code */
char address[50];
int postal_code;
} mail;

/* The return value */
int ret = EXIT_SUCCESS;

/* Initialize the postal_code to 0 */
mail.postal_code = 0;

(void) printf ("Mailing list database.\n");
(void) printf ("---------------------\n");
(void) printf
("\nThis program will enter a new user in the mailing list database\n");

ret = takeadress (mail.address, sizeof (mail.address), "takeadress");

/* If all goes fine we continue */
if (ret == EXIT_SUCCESS)
mail.postal_code = take_postalcode ("take_postalcode");

/* If postal_code = 0, this is a failure */
if (mail.postal_code == 0)
ret = EXIT_FAILURE;

/* Successful */
if (ret == EXIT_SUCCESS)
printf ("Values added.\n");

return ret;
}

Voilà,
ThE_TemPLaR :)

9 réponses

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', "Eddahbi Karim"
wrote:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*********************************************************************

NAME : wipestring( string )

USAGE : Wipe useless values from the string...

PARAMETERS : char const *string to wipe...

*********************************************************************/

void
wipestring (char const *string)
{
char *p = strchr (string, 'n');

if (p)
{
*p = '';
}
else
{
int c;

while ((c = getchar ()) != 'n' && c != EOF)
{
}
}
}

/*********************************************************************

NAME : verify ( string, size, function_name )

USAGE : verify if the user input is correct

PARAMETERS :
- The string to check and to fill if the user enter something
- The size of the string
- The name of the function to give accurate error messages

RETURNS :
- EXIT_SUCCESS
- EXIT_FAILURE

*********************************************************************/

int
verify (char *string, size_t size, const char *function_name)


Clairement, cette fonction est une 'get_string()', certainement pas une
'verify()'. Attention à ne pas obscurcir le code gratuitement...

{
/* The error number */
int err = EXIT_SUCCESS;

/* Check if there's an input */
if (fgets (string, (int) size, stdin) == NULL)
{
printf ("Input Error at %sn", function_name);
err = EXIT_FAILURE;
}

/* Check if the user entered something */
else
{
wipestring (string);

if (*string == '')
{
printf ("No input at %sn", function_name);
err = EXIT_FAILURE;
}
}

return err;
}

/*********************************************************************

NAME : takeadress ( address, size, name )

PARAMETERS :
- The address
- The size of this string
- Its name

RETURNS : See verify()

*********************************************************************/

int
takeadress (char *address, size_t size, const char *name)
{

/* The return value */
int ret = 0;

(void) printf ("Please enter the address of the person : ");


Conception: Il ne serait pas inutile de préciser la longueur max...

(void) printf ("Please enter the address of the person "
"(%lu char. max) : "
, (unsigned long) size);


(void) fflush (stdout);

ret = verify (address, size, name);


Un peu bizarre que l'on verifie alors qu'on a encore rien saisi... Ou alors,
c'est que le nom est mal choisi. Ne pas oublier qu'un code doit se lire comme
un roman...

/* Check if the value is correct */


On ne vérifie rien. On se contente d'afficher le résultat de la saisie...

/* Print out the input */

(void) printf ("The address is %sn", address);

return ret;
}

/*********************************************************************

NAME : take_postalcode ( name )

PARAMETERS : Name of the function.

RETURNS : The postal code entered by the user

*********************************************************************/

int
take_postalcode (const char *name)
{
/* The postal code */
unsigned int postal_code = 0;

/* User input */
char input[10];

/* Ask the postal code until the user gives one */
do
{
(void) printf ("Insert the postal code of the user city : ");
(void) fflush (stdout);

if (verify (input, sizeof (input), name) == EXIT_FAILURE)
{
postal_code = 0;
break;
}
}
while (sscanf (input, "%u", &postal_code) != 1);

/* Check if the value is correct */
(void) printf ("The postal code is : %dn", postal_code);


"%u", car 'postal_code' est un 'unsigned'.

return postal_code;
}

int
main (void)
{

/* Mailing list structure */
struct mailing_list
{
/* The address and its according postal_code */
char address[50];
int postal_code;


<HS>
Spécifications:
- Je ne vois pas de nom!
- Un code postal n'est pas une valeur numérique. En France, c'est F-12345, en
Suisse, CH-1234, en Grande Bretagne, c'est le code de base est carrément
alphanumérique...

On utilise un champ numérique uniquement si on a l'intention de faire des
calculs dessus. (Quantité, valeur, prix, etc.)

Un numéro de maison est évidemment alpha-numérique :

1
2-bis

etc.

char postal_code[12];
</>

L'exemple classique pour jouer avec une structure aplhanumérique, c'est Nom,
Age, ou Article, Stock, Prix etc.

} mail;

/* The return value */
int ret = EXIT_SUCCESS;

/* Initialize the postal_code to 0 */
mail.postal_code = 0;


N'a évidemment plus de sens si c'est une tableau de char... Ok, admettons que
le code postal soit numérique...

(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf
("nThis program will enter a new user in the mailing list
databasen");

ret = takeadress (mail.address, sizeof (mail.address),
"takeadress");


Ok.

/* If all goes fine we continue */
if (ret == EXIT_SUCCESS)
mail.postal_code = take_postalcode ("take_postalcode");

/* If postal_code = 0, this is a failure */
if (mail.postal_code == 0)
ret = EXIT_FAILURE;

/* Successful */
if (ret == EXIT_SUCCESS)
printf ("Values added.n");


Pas encore!

return ret;
}


C'est pas mal.

--
-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
Eddahbi Karim
Voilà donc la nouvelle version de mon programme :

/*-8<-----------------------------------------------------------------

mailing-entry -- This program let you add a complete entry in
the mailing database, including : address and
postal_code for the moment.

Author : Eddahbi Karim
E-mail :
Nick : ThE_TemPLaR (forum, newsgroups), gamer (IRC)

----------------------------------------------------------------------

Usage : Launch the program and enter the informations needed to
add an entry in the mailing database.

----------------------------------------------------------------->8-*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*--------------------------------------------------------------------

NAME : IFSUCCESS

USAGE : Call the following function if there was no problem

--------------------------------------------------------------------*/

#define IFSUCCESS if ( ret == EXIT_SUCCESS )

/*********************************************************************

NAME : wipestring( string )

USAGE : Wipe useless values from the string...

PARAMETERS : char const *string to wipe...

*********************************************************************/

static void
wipestring (char const *string)
{
char *p = strchr (string, 'n');

if (p)
{
*p = '';
}
else
{
int c;

while ((c = getchar ()) != 'n' && c != EOF)
{
}
}
}

/*********************************************************************

NAME : getstring ( string, size )

USAGE : Take the user input and verify it...

PARAMETERS :
- The string to check and to fill if the user enter something
- The size of the string

RETURNS :
- EXIT_SUCCESS
- EXIT_FAILURE

*********************************************************************/

static int
getstring (char *string, size_t size)
{
/* The error number */
int err = EXIT_SUCCESS;

/* Check if there's an input */
if (fgets (string, (int) size, stdin) == NULL)
{
printf ("Input Errorn");
err = EXIT_FAILURE;
}

/* Check if the user entered something */
else
{
/* Wipe useless values */
wipestring (string);

if (*string == '')
{
printf ("No inputn");
err = EXIT_FAILURE;
}
}

return err;
}

/*********************************************************************

NAME : takeadress ( address, size )

PARAMETERS : The address and the size of this string

RETURNS : See getstring()

*********************************************************************/

static int
takeadress (char *address, size_t size)
{

/* The return value */
int ret = 0;

(void) printf ("Please enter the address of the person "
"(%lu char. max) : ",
(unsigned long) size);
(void) fflush (stdout);

ret = getstring (address, size);

/* Print the user input */
IFSUCCESS
(void) printf ("The address is %sn", address);

return ret;
}

/*********************************************************************

NAME : take_postalcode ( postalcode, size )

PARAMETERS : The postalcode and the size of this string

RETURNS : see getstring()

*********************************************************************/

static int
take_postalcode (char *postalcode, size_t size)
{

/* The return value */
int ret = 0;

(void) printf ("Enter the postal code of the adress "
"(%lu char. max) : ",
(unsigned long) size);

(void) fflush (stdout);

ret = getstring(postalcode, size);

/* Print the user input */
IFSUCCESS
(void) printf("The postal code is %sn", postalcode);

return ret;
}

/*********************************************************************

NAME : take_name ( name, size )

PARAMETERS : The name and the size of this string

RETURNS : see getstring()

*********************************************************************/

static int
take_name ( char *name, size_t size )
{

/* The return value */
int ret = 0;

(void) printf("Give the name and the first name of the user "
"(%lu char. max) : ",
(unsigned long) size);

(void) fflush(stdout);

ret = getstring( name, size );

/* Print user input if we got one */
IFSUCCESS
(void) printf("The name and first name of the user are : %sn", name);

return ret;
}

/*********************************************************************

NAME : main

USAGE : Describe the program and launch the functions behind

RETURNS : see getstring()

*********************************************************************/

int
main (void)
{

/* Mailing list structure */
struct mailing_list
{
char name[50];
char address[128];
char postal_code[12];
} mail;

/* The return value */
int ret = EXIT_SUCCESS;

/* Describe */
(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf
("nThis program will enter a new user in the mailing list databasen");

ret = take_name(mail.name, sizeof(mail.name));

/* Continue unless we got a EXIT_FAILURE */
IFSUCCESS
ret = takeadress (mail.address, sizeof(mail.address));

IFSUCCESS
ret = take_postalcode (mail.postal_code, sizeof(mail.postal_code));


/* Successful */
if (ret == EXIT_SUCCESS)
printf ("Values added.n");

return ret;
}

Le problème est que splint me retourne :

mailing-entry.c: (in function main)
mailing-entry.c:232:21: Passed storage mail.name not completely defined
(*(mail.name) is undefined): take_name (mail.name, ...)
Storage derivable from a parameter, return value or global is not defined.
Use /*@out@*/ to denote passed or returned storage which need not be defined.
(Use -compdef to inhibit warning)

Finished checking --- 1 code warning

Je trouve ça bizarre étant donné que les autres fonctions sont établies de
la même manière, contiennent les mêmes arguments mais n'ont pas de
warnings.

Merci ;),
Voila,
ThE_TEmPLaR
Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', "Eddahbi Karim"
wrote:

#define IFSUCCESS if ( ret == EXIT_SUCCESS )


Je déconseille ce genre de chose qui ne font qu'obscurcir le code.

/* Continue unless we got a EXIT_FAILURE */
IFSUCCESS
ret = takeadress (mail.address, sizeof(mail.address));

IFSUCCESS
ret = take_postalcode (mail.postal_code,
sizeof(mail.postal_code));


/* Successful */
if (ret == EXIT_SUCCESS)
printf ("Values added.n");

return ret;
}

mailing-entry.c: (in function main)
mailing-entry.c:232:21: Passed storage mail.name not completely defined
(*(mail.name) is undefined): take_name (mail.name, ...)
Storage derivable from a parameter, return value or global is not
defined. Use /*@out@*/ to denote passed or returned storage which need
not be defined. (Use -compdef to inhibit warning)

Finished checking --- 1 code warning


J'avoue ne pas trop bien comprendre ce que veux dire splint sur ce coup là.
Pour moi, le code est correct.

Pour le fun:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*********************************************************************

NAME : wipestring( string )

USAGE : Wipe useless values from the string...

PARAMETERS : char const *string to wipe...

*********************************************************************/
static void wipestring (char const *string)
{
char *p = strchr (string, 'n');

if (p)
{
*p = '';
}
else
{
int c;

while ((c = getchar ()) != 'n' && c != EOF)
{
}
}
}

/*********************************************************************

NAME : getstring ( string, size )

USAGE : Take the user input and verify it...

PARAMETERS :
- The string to check and to fill if the user enter something
- The size of the string

RETURNS :
- EXIT_SUCCESS
- EXIT_FAILURE

*********************************************************************/
static int getstring (char *string, size_t size)
{
/* The error number */
int err = 0;

/* Check if there's an input */
if (fgets (string, (int) size, stdin) == NULL)
{
printf ("Input Errorn");
err = 1;
}

/* Check if the user entered something */
else
{
/* Wipe useless values */
wipestring (string);

if (*string == '')
{
printf ("No inputn");
err = 1;
}
}

return err;
}

/* ------------------------------------------------------------------------
------------------------------------------------------------------------
*/
static void print_field (size_t const n)
{
size_t i;

putchar ('v');
for (i = 0; i < n - 2; i++)
{
putchar ('-');
}
putchar ('v');
putchar ('n');
}

/*********************************************************************

NAME : takeadress ( address, size )

PARAMETERS : The address and the size of this string

RETURNS : See getstring()

*********************************************************************/
static int take_address (char *address, size_t size)
{

/* The return value */
int err = 0;

(void) printf ("Please enter the address of the personn");
print_field (size - 1);

err = getstring (address, size);

/* Print the user input */
if (!err)
{
(void) printf ("The address isn[%s]n", address);
}

return err;
}

/*********************************************************************

NAME : take_postalcode ( postalcode, size )

PARAMETERS : The postalcode and the size of this string

RETURNS : see getstring()

*********************************************************************/
static int take_postalcode (char *postalcode, size_t size)
{

/* The return value */
int err = 0;

(void) printf ("Enter the postal code of the adressn");

print_field (size - 1);

err = getstring (postalcode, size);

/* Print the user input */
if (!err)
{
(void) printf ("The postal code isn[%s]n", postalcode);
}

return err;
}

/*********************************************************************

NAME : take_name ( name, size )

PARAMETERS : The name and the size of this string

RETURNS : see getstring()

*********************************************************************/

static int take_name (char *name, size_t size)
{

/* The return value */
int err = 0;

(void) printf ("Give the name and the first name of the usern");

print_field (size - 1);

err = getstring (name, size);

/* Print user input if we got one */
if (!err)
{
(void) printf ("The name and first name of the user aren[%s]n",
name);
}

return err;
}

/*********************************************************************

NAME : main

USAGE : Describe the program and launch the functions behind

RETURNS : see getstring()

*********************************************************************/

int main (void)
{

/* Mailing list structure */
struct mailing_list
{
char name[20];
char address[50];
char postal_code[12];
}
mail;

/* The return value */
int ret = EXIT_SUCCESS;

/* Describe */
(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf ("nThis program will enter a new user in the mailing"
" list databasen");


#define TERR
do
{
if (err)
{
ret = EXIT_FAILURE;
goto ERR;
}
}
while (0)

{
int err;

err = take_name (mail.name, sizeof (mail.name));
TERR;

err = take_address (mail.address, sizeof (mail.address));
TERR;

err = take_postalcode (mail.postal_code, sizeof (mail.postal_code));
TERR;
}
#undef TERR

/* Successful */
printf ("Values added:n");
#define FMT "%15s"
printf (FMT ": %sn", "Name", mail.name);
printf (FMT ": %sn", "Values Address", mail.address);
printf (FMT ": %sn", "Postal code", mail.postal_code);
#undef FMT

ERR:
return ret;
}

--
-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
Eddahbi Karim
putchar ('v');
for (i = 0; i < n - 2; i++)
{
putchar ('-');
}
putchar ('v');
putchar ('n');
}


Faut les (void)er ces petits, étant donné qu'ils ne rapportent rien :)

[snip code]

#define TERR
do
{
if (err)
{
ret = EXIT_FAILURE;
goto ERR;
}
}
while (0)


Tiens j'ai toujours rever de savoir comment faire un goto en C :).
T'as pas peur de déclencher une guerre avec talibans anti-goto ?

{
int err;

err = take_name (mail.name, sizeof (mail.name));
TERR;

err = take_address (mail.address, sizeof (mail.address));
TERR;

err = take_postalcode (mail.postal_code, sizeof (mail.postal_code));
TERR;
}
#undef TERR

/* Successful */
printf ("Values added:n");
#define FMT "%15s"
printf (FMT ": %sn", "Name", mail.name);
printf (FMT ": %sn", "Values Address", mail.address);
printf (FMT ": %sn", "Postal code", mail.postal_code);
#undef FMT

ERR:
return ret;


Si on veut délimiter ERR on fait comment ?

}


Merci beaucoup ;),
Voila,
ThE_TemPLaR

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', "Eddahbi Karim"
wrote:

#define TERR
do
{
if (err)
{
ret = EXIT_FAILURE;
goto ERR;
}
}
while (0)


Tiens j'ai toujours rever de savoir comment faire un goto en C :).
T'as pas peur de déclencher une guerre avec talibans anti-goto ?


M'en fous. J'étais parfaitement anti-goto quand je suis arrivé ici, mais
depuis je me suis secoué les neurones.


{
int err;

err = take_name (mail.name, sizeof (mail.name));
TERR;

err = take_address (mail.address, sizeof (mail.address));
TERR;

err = take_postalcode (mail.postal_code, sizeof (mail.postal_code));
TERR;
}
#undef TERR



<...>

ERR:
return ret;


Si on veut délimiter ERR on fait comment ?


Comprends pas.

}



--
-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
Richard Delorme
Emmanuel Delahaye a écrit:

int main (void)
{

/* Mailing list structure */
struct mailing_list
{
char name[20];
char address[50];
char postal_code[12];
}
mail;

/* The return value */
int ret = EXIT_SUCCESS;

/* Describe */
(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf ("nThis program will enter a new user in the mailing"
" list databasen");


#define TERR
do
{
if (err)
{
ret = EXIT_FAILURE;
goto ERR;
}
}
while (0)

{
int err;

err = take_name (mail.name, sizeof (mail.name));
TERR;

err = take_address (mail.address, sizeof (mail.address));
TERR;

err = take_postalcode (mail.postal_code, sizeof (mail.postal_code));
TERR;
}
#undef TERR

/* Successful */
printf ("Values added:n");
#define FMT "%15s"
printf (FMT ": %sn", "Name", mail.name);
printf (FMT ": %sn", "Values Address", mail.address);
printf (FMT ": %sn", "Postal code", mail.postal_code);
#undef FMT

ERR:
return ret;
}



Franchement, je trouve cela très lourd. Qu'est-ce qui ne va pas avec :

int main (void)
{

/* Mailing list structure */
struct mailing_list
{
char name[20];
char address[50];
char postal_code[12];
}
mail;

/* Describe */
(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf ("nThis program will enter a new user in the mailing"
" list databasen");

if (take_name (mail.name, sizeof (mail.name)))
return EXIT_FAILURE;

if (take_address (mail.address, sizeof (mail.address)))
return EXIT_FAILURE;

if (take_postalcode (mail.postal_code, sizeof (mail.postal_code)))
return EXIT_FAILURE;

/* Successful */
printf ("Values added:n");
#define FMT "%15s"
printf (FMT ": %sn", "Name", mail.name);
printf (FMT ": %sn", "Values Address", mail.address);
printf (FMT ": %sn", "Postal code", mail.postal_code);
#undef FMT

return EXIT_SUCCESS;
}

Ce code est 30% plus court que le précédent, et à mon avis, nettement plus
clair. D'où vient cette théorie du point de sortie systématiquement unique
d'une fonction ? Dans le cas présent, je trouve que cela obscurcit le code,
le ralentit (ajout de sauts et d'affectations) sans rien apporter. Je sais
qu'il est parfois pratique d'avoir un point de sortie unique, si l'on
nettoie certaines ressources (fichiers fermés, mémoire libérée,...), mais
ce n'est pas le cas ici. Il s'agit donc d'un abus de principe. Je ne suis
pas sûr non plus que cela facilite la maintenance. Et j'imagine bien un
future programmeur compléter le code précédent ainsi :

...
/* ajout de code ici */
fclose(fichier);
free(data);

ERR:
/* au lieu d'ici */
return ret;
}

Ruinant tous les efforts de spaghettisation du code.

--
Richard

Avatar
Richard Delorme
Emmanuel Delahaye a écrit:

In 'fr.comp.lang.c', Richard Delorme wrote:
<snip mon code>

Franchement, je trouve cela très lourd. Qu'est-ce qui ne va pas avec :

int main (void)
{

/* Mailing list structure */
struct mailing_list
{
char name[20];
char address[50];
char postal_code[12];
}
mail;

/* Describe */
(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf ("nThis program will enter a new user in the mailing"
" list databasen");

if (take_name (mail.name, sizeof (mail.name)))
return EXIT_FAILURE;

if (take_address (mail.address, sizeof (mail.address)))
return EXIT_FAILURE;

if (take_postalcode (mail.postal_code, sizeof (mail.postal_code)))
return EXIT_FAILURE;

/* Successful */
printf ("Values added:n");
#define FMT "%15s"
printf (FMT ": %sn", "Name", mail.name);
printf (FMT ": %sn", "Values Address", mail.address);
printf (FMT ": %sn", "Postal code", mail.postal_code);
#undef FMT

return EXIT_SUCCESS;
}


On risque de louper une action importante en sortie (libération de
ressource).

Si je veux mettre un point d'arret sur la sortie, je fais comment?

Ce code est 30% plus court que le précédent, et à mon avis, nettement
plus


Faut pas exagerer.

[...]


Ramené au reste du code, ça fait pas lourd!


Je parlais sur l'ensemble du main, en nombre de lignes de code.
Ton main() fait 58 lignes, le mien 37 lignes, soit (58 - 37) / 58 = 36.2%

clair. D'où vient cette théorie du point de sortie systématiquement
unique d'une fonction ? Dans le cas présent, je trouve que cela obscurcit
le code,


De la programmation structurée.


Il reste à définir programmation structurée. L'inventeur de cette
terminologie est Dijsktra, qui est aussi l'auteur de "Go To Statement
Considered Harmful", et donc pas un fervent utilisateur du goto. En
programmation structurée, il me semble qu'on aurait dû écrire :

int main(void) {
[...]
if(take_name (mail.name, sizeof (mail.name))) {
ret = EXIT_FAILURE;
}else if(take_address (mail.address, sizeof (mail.address))) {
ret = EXIT_FAILURE;
}else if(take_postalcode (mail.postal_code, sizeof (mail.postal_code))){
ret = EXIT_FAILURE;
} else {
/* Successful */
printf ("Values added:n");
#define FMT "%15s"
printf (FMT ": %sn", "Name", mail.name);
printf (FMT ": %sn", "Values Address", mail.address);
printf (FMT ": %sn", "Postal code", mail.postal_code);
#undef FMT
ret = EXIT_SUCCESS;
}
return ret;
}

Je ne comprend pas bien ta remarque sur
l'obscurcissement. On reproche souvent au C d'obliger le programmeur à
passer du temps à écrire du code de test, imbriquer des ifs etc. Je montre
une méthode d'allégement du codage, et non il faut encore qu'il y en ait
un qui rale.


Je n'ai pas vu d'allègement. C'est pourquoi je râle.


Je te signale que le principe d'utiliser la macro permet par simple
recompilation d'affiner l'instrumentation du code, comme ajouter une trace
avec __FILE__, __LINE__ sans alourdir le code. Seule la macro est
modifiée(ou une macro spéciale de debug est crée). Visiblement, tu manques
d'expérience sur les gros projets pour ne pas comprendre l'intérêt de la
chose...


Je ne suis pas contre le principe de la macro, juste contre les macros qui
allourdissent le code et cache le déroulement du programme. C'est un peu
comme cacher la nature pointeuresque d'un pointeur via un typedef. Dans ton
exemple, le goto est caché dans la macro. Ça n'aide pas à suivre le
déroulement du programme. Il faut examiner le contenu de la macro, pour
suivre ce qui se passe. Dans un programme simple comme ici, c'est
supportable, mais dans un gros programme, ça doit être plus dur à gérer.

le ralentit (ajout de sauts et d'affectations) sans rien apporter. Je
sais


Ca apporte de la sécurité en matière de conception. On sait exactement par
où on passe en quittant. On sait ce qu'on a à faire, et on a pas besoin de
le multiplier.


Non, on ne sait pas. Le goto est caché dans la macro, et le flux du
programme est assez dur à suivre.

qu'il est parfois pratique d'avoir un point de sortie unique, si l'on
nettoie certaines ressources (fichiers fermés, mémoire libérée,...), mais
ce n'est pas le cas ici. Il s'agit donc d'un abus de principe. Je ne suis


Je code de façon générique. Je ne m'occupe pas des cas particuliers ou
d'école des 'toy programs'. Je préfère exposer des principes généraux
réutilisables en toutes circonstances et qui on fait leur preuves.


Donc, au lieu d'écrire :

int a = 1;

Tu écris :
int i, a[1];
for(i = 0; i < sizeof a; i++) {
a[i] = 1;
}

parce qu'un scalaire est un cas particulier de vecteur à 1 dimension, et que
l'emploi d'un vecteur est plus générique ?

Ruinant tous les efforts de spaghettisation du code.


Tu veux sans doute dire de 'despaghettisation'.


Non car le déroulement du programme est plus dur à suivre.

D'accord, un commentaire
serait le bienvenu, ou alors un traitement particulier pour les cas
d'erreur.

END:
/* fin commune */
fclose(fichier);
free(data);
return ret;

ERR:
/* traitement d'erreur */
goto END;
}


Ça c'est du code cappellini...

Je ne suis pas un adepte de goto, loin de là (Dégouté du BASIC, sauvé par
Pascal), mais au cours de nombreux débats sur fcl et clc, j'ai fini par
admettre que dans certains cas, il pouvait être utile, notamment pour
alléger les traitement d'erreurs (à condition de cacher les details gores
dans une macro a-priori locale)


Je ne suis ni pour ni contre le goto. Je suis pour le code le plus simple
possible, et si le goto permet cela, il est bienvenu, mais c'est rare.

--
Richard


Avatar
Olivier Aumage
"Eddahbi Karim" writes:

int
main (void)
{

/* Mailing list structure */
struct mailing_list
{
char name[50];
char address[128];
char postal_code[12];
} mail;

/* The return value */
int ret = EXIT_SUCCESS;

/* Describe */
(void) printf ("Mailing list database.n");
(void) printf ("---------------------n");
(void) printf
("nThis program will enter a new user in the mailing list databasen");

ret = take_name(mail.name, sizeof(mail.name));

/* Continue unless we got a EXIT_FAILURE */
IFSUCCESS
ret = takeadress (mail.address, sizeof(mail.address));

IFSUCCESS
ret = take_postalcode (mail.postal_code, sizeof(mail.postal_code));

/* Successful */
if (ret == EXIT_SUCCESS)
printf ("Values added.n");

return ret;
}

Le problème est que splint me retourne :

mailing-entry.c: (in function main)
mailing-entry.c:232:21: Passed storage mail.name not completely defined
(*(mail.name) is undefined): take_name (mail.name, ...)
Storage derivable from a parameter, return value or global is not defined.
Use /*@out@*/ to denote passed or returned storage which need not be defined.
(Use -compdef to inhibit warning)

Finished checking --- 1 code warning


Les champs de 'mail' sont passés en arguments de take_name,
take_address et take_postalcode avant d'avoir été initialisés, ce qui
inquiète 'splint'.

Une manière de corriger la situation est de déclarer la variable mail
avec son initialiseur, de la façon suivante :

%-------------------------------------------
/* Mailing list structure */
struct mailing_list
{
char name[50];
char address[128];
char postal_code[12];
} mail = {"", "", ""};
%-------------------------------------------

Une autre manière est d'initialiser les champs un par un, de la façon
suivante, avant qu'ils soient utilisés :

%-------------------------------------------
mail.name[0] = '';
mail.address[0] = '';
mail.postal_code[0] = '';
%-------------------------------------------

--
Olivier

Avatar
Eddahbi Karim
Le Sat, 05 Jul 2003 20:15:06 +0200, Olivier Aumage a écrit :

%-------------------------------------------
/* Mailing list structure */
struct mailing_list
{
char name[50];
char address[128];
char postal_code[12];
} mail = {"", "", ""};
%-------------------------------------------

Une autre manière est d'initialiser les champs un par un, de la façon
suivante, avant qu'ils soient utilisés :

%-------------------------------------------
mail.name[0] = '';
mail.address[0] = '';
mail.postal_code[0] = '';
%-------------------------------------------


Je te remercie beaucoup, je me demandait comment initialiser les variables
d'une structure directement ;)

Merci,
Voila ;),
ThE_TemPLaR