[RESOLU][C]renvoyer une chaîne de caractères dans une structure

Salut les geeks, je viens de m’amuser à écrire une fonction maison pour saisir des noms/prénoms composés, et je dois renvoyer ces noms dans une structure (une liste de membres en fait). Voilà comment je m’y prends:

  • je déclare une chaîne de caracs temp dans le main, je la passe en paramètres à la fonction de saisie
  • une fois que la fonction a rempli cette chaîne correctement je fais un strcpy (string copy) de cette chaîne depuis temp dans la structure (nouveau.nom ou .prénom)

N’y aurait-il quand même pas plus simple que de passer par une variable temporaire? Mais c’est peut-être mieux pour réutiliser la fonction dans d’autres cas en fait…

Si vous voulez jeter un coup d’oeil au code:
[codebox]typedef struct{
char nom[20],
prenom[30];
}Liste;

saisir(int, char*);

main()
{
Liste nouv;
char temp[30];

printf("Entrez un nom:\n");
saisir(20, temp);
strcpy(nouv.nom,temp);
system("cls");
printf("Entrez un prenom:\n");
saisir(30, temp);
strcpy(nouv.prenom,temp);
system("cls");
printf("Vous avez saisi: %s %s\n",nouv.prenom,nouv.nom);

}

saisir(int lon, char *pt)
{
char vec[30],
*pvec;

//écriture
fflush(stdin);
pvec=&vec[0];
do
{
	*pvec=getchar();
	*pvec=tolower(*pvec);
	if(*pvec!='\n')
	{
		if(*pvec>='a' || *pvec<='z')
		{
			pvec++;
		}
		if(*pvec==' ' || *pvec=='-' || *pvec=='_')
		{
			pvec++;
		}
	}
	else
	{
		*pvec='\0';
		pvec=&vec[lon];
	}
}
while(pvec<&vec[lon-1] && *(pvec-1)!='\0');

//mise en forme
for(pvec=&vec[1];*pvec!='\0';pvec++)
{
	if(*pvec<'a' || *pvec>'z')
	{
		*pvec='-';
		pvec++;
		*pvec=toupper(*pvec);
	}
}
vec[0]=toupper(vec[0]);
for(pvec=&vec[0];*pvec!='\0';pvec++,pt++)
{
	*pt=*pvec;
}
*pt='\0';

}[/codebox]

première remarque : quel est l’intérêt d’utiliser des noms de variable aussi courts ? Ca ne coute vraiment rien de nommer tes variables maVariableQuiDemonteDesOurs, si tant est que son nom veuille dire quelque chose. Par exemple, “vec”, j’ai du mal à saisir la signification (pvec et pt, ca va encore). Pareil pour lon au lieu de size ou longueur.

Ensuite, je vois pas ce qui t’empêche de faire un saisir(30, nouv.nom); au lieu de saisir(30, temp); strcpy(nouv.nom, temp);

essaye d’éviter strcpy. utilise strncpy à la place et force systématiquement le dernier caractère de la chaine destination à ‘\0’

avoir des entiers qui se baladent dans tout le code comme ça, c’est pas forcément une bonne idée non plus. Utilise plutot des constante LONGUEUR_NOM et LONGUEUR_SURNOM. Comme ca, si tu veux la changer partout, tu la changes en haut de ton code et c’est fini

Le nom de structure “Liste” est pas forcément logique.C’est peut-être volontaire, mais “identité” ou un truc similaire me parait plus approprié.

Pour les noms courts c’est une (sale) habitude que j’ai prise au début c’est tout, j’aime (trop) être concis.

Pour le saisir (30,nouv.nom) j’y avias pensé mais j’obtenais une erreur à la compil, j’ai du me planter dans le prototype de la fonction à un moment.

Enfin pour l’utilisation de constantes ce n’est pas bête, mais dans ce cas-ci ça m’avait l’air plus simple de passer par des entiers pour réutiliser plus simplement la fonction dans différents cas.

Merci :slight_smile:

Pourquoi tu n’alloues pas dynamiquement en fonction de la taille de la chaîne ?

Et sinon tu pourrais faire std::vectorstd::string

Pour avoir un fichier de structures avec des tailles fixes et se balader plus facilement dans les enregistrements (c’est pour un fichier contenant les données des membres d’un club)

Pour le "std::vectorstd::string … " je ne connais pas. C’est pas du C++ ça?

C’est du STL et c’est le bien (sans contraintes de temps bien sûr)

Je te crois sur parole, mais ici je dois tout faire en C (c’est pour un dossier) :slight_smile:

Tes prototypes sont toujours over deprecated et visiblement tu ne les comprends pas. Tu utilises quel compilateur ?

forums.c:11: warning: data definition has no type or storage class forums.c:11: warning: type defaults to 'int' in declaration of 'saisir' forums.c:14: warning: return type defaults to 'int' forums.c:14: warning: function declaration isn't a prototype forums.c:31: warning: return type defaults to 'int' forums.c:35:1: warning: C++ style comments are not allowed in ISO C90 forums.c:35:1: warning: (this will be reported only once per input file) forums.c: In function 'saisir': forums.c:77: warning: control reaches end of non-void function forums.c: In function 'main': forums.c:28: warning: control reaches end of non-void function

Visual Studio, pourquoi? Qu’est-ce que tu entends par over deprecated?

Je pense qu’il veut dire que tu ne definis pas quel retour est cense faire ta fonction (void, j’imagine). Et saymal! :slight_smile:

faut faire

void saisir(int lon, char *pt)

(et la meme chose pour le main, meme si je crois que la best practice c’est de renvoyer un int et de faire return 0, mais c’est vieux) et tu feras disparaitre tous ces vilains warnings

Mets les warnings au maximum (c’est l’option /W4).

Comme il l’a défini son main() retourne un int aussi, mais seulement en C89. La liste de paramètres vide est obsolète, même en C89.

En plus :
[ul]
[li]Il faudrait ajouter un return 0 à la fin de main() comme tu l’as dit[/li][li]Le type size_t est plus adapté qu’int pour la longueur de la chaîne[/li][li]Les paramètres de tolower() et toupper() doivent être représentables dans un unsigned char ou être égaux à EOF, a priori ça s’arrange en utilisant des unsigned char dans la fonction saisir()[/li][li]Je ne vois pas à quoi sert fflush(stdin), et c’est non standard et non portable[/li][li]Le retour de getchar() n’est pas vérifié en cas d’erreur[/li][li]Tester le code d’un caractère avec if(*pvec<‹ a › || *pvec>‹ z ›) n’est pas portable, il vaut mieux utiliser une fonction comme isalpha()[/li][li]J’avoue que je n’ai rien compris à l’algorithme lui-même, le code est trop alambiqué :)[/li][/ul]

My bad, j’ai corrigé les prototypes, c’est vrai que l’oubli de void c’est pas sérieux!

Pour le reste:

[quote=« DaP, post:11, topic: 47968 »]Mets les warnings au maximum (c’est l’option /W4).
Ok, je vais chercher l’option.

En plus :
[ul]
[li]Le type size_t est plus adapté qu’int pour la longueur de la chaîne
[/li]Même si je n’ai pas utilisé de sizeof dans le code?


[li]Je ne vois pas à quoi sert fflush(stdin), et c’est non standard et non portable
[/li]Ben c’est pour vider le buffer au cas où il resterait des caractères dedans


[li]Tester le code d’un caractère avec if(*pvec<‹ a › || *pvec>‹ z ›) n’est pas portable, il vaut mieux utiliser une fonction comme isalpha()
[/li]Oui mais il faut aussi vérifier si le caractère est un espace, un trait d’union ou une fin de ligne, ce que isalpha ne fait pas apparemment.


[li]J’avoue que je n’ai rien compris à l’algorithme lui-même, le code est trop alambiqué :slight_smile:
[/li]Le but est de saisir un (pré)nom qui peut être composé, auquel cas il sera mis en forme (Majuscule au début de chaque noms, séparés par un trait d’union).
[/ul][/quote]

size_t est adapté dès qu’on compte des trucs, il a l’avantage d’être non signé et de pouvoir contenir la taille de n’importe quelle variable. Il y en a beaucoup qui s’en servent aussi pour les indexes de tableau.

Si tu te contentes d’utiliser des fonctions de saisie qui ne laissent pas traîner de caractères (cf. l’exemple plus loin) il n’y a pas de raison pour se tracasser de ça.

Le problème de if(*pvec>=‹ a › || *pvec<=‹ z ›) c’est qu’il n’y a aucune garantie que les codes des lettres soient consécutifs (même si ça fonctionne sûrement sur ta machine et c’est garanti de fonctionner pour les codes des chiffres). Pour tester des égalités comme if(*pvec==’ ’ || *pvec==‹ - › || *pvec==‹ _ ›) je ne vois pas de problème.

À mon avis tu devrais écrire une fonction générique pour lire des chaînes au lieu de ressortir getchar() à chaque fois, par exemple j’avais écrit ça (oui je sais il y a des trucs redondants) :
[codebox]#include <stdio.h>

/*

  • readString
  • Lit (nbCar-1) caractères sur stdin et les stocke à l’adresse chaine
  • Le buffer de stdin est vide après l’appel de la fonction
  • Retour :
    • si la lecture s’est passée correctement : 0
    • si la chaîne a été tronquée : 1
    • si erreur de lecture, si nbCar <= 1 ou si chaine == NULL : 2
      */
      int readString (char chaine, size_t nbCar)
      {
      int err = 2;

/* si la saisie est impossible /
if (nbCar <= 1 || chaine == NULL)
{
err = 2;
}
/
si elle est possible */
else
{
int carTemp;
unsigned int i = 0;

/* lecture de stdin et copie dans la chaîne */
while ( (carTemp = getchar()) != '\n'
         && carTemp != EOF
         && i < nbCar-1 )
{
  chaine[i] = carTemp;
  ++i;
}

/* marquage de la fin de la chaîne */
chaine[i] = '\0';

/* si il y a eu une erreur de lecture */
if (carTemp == EOF)
{
  err = 2;
}
/* si des caractères ont été laissés dans le buffer */
else if (carTemp != '\n')
{
  err = 1;
     
  do
  {
    carTemp = getchar();
  }
  while (carTemp != '\n' && carTemp != EOF);
}
/* si tout s'est passé normalement */
else
{
  err = 0;
}

}

return err;
}
[/codebox]

Il me semblait que les codes des lettres sont basés sur la table ASCII, et donc se suivent?!?

Ce n’est pas garanti par la norme et on peut trouver au moins un encodage où ça ne se suit pas : http://en.wikipedia.org/wiki/Ebcdic#Codepage_layout

Attention !

Le ‘OU’ fait que ce test renverra ‘vrai’ pour tous les caractères…