Contenu | Rechercher | Menus

Annonce

La nouvelle clé USB Ubuntu-fr est en prévente
Rendez-vous sur la boutique En Vente Libre

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 08/09/2019, à 17:38

chris7522

pointeur et fonction (C)

Bonjour a toutes et a tous,
je coince sur un truc qui doit etre evident mais comme j'ai les yeux un peu explosé , je sollicite votre aide :

#include <stdio.h>

typedef struct player
{ char prenom[20];
  char nom[20];
  int age;
}player;

void changement(player *p)
{ printf("quel est ton prenom ?\n");
  scanf("%s",p->prenom);
  printf("quel est ton nom ?\n");
  scanf("%s",p->nom); 
  printf("quel est ton age ?\n");
  scanf("%d",p->age);
}
int main(void){
 player p1,p2={"","",0};
 printf("\n____1er JOUEUR____\n");
 changement(&p1);
 printf("\n____2eme JOUEUR____\n");
 changement(&p2);
 printf("Le prenom du 1er joueur est %s,le nom %s et l'age est %d\n",p1.prenom,p1.nom,p1.age);
 printf("Le prenom du 2 eme joueur est %s,le nom %s et l'age est %d\n",p2.prenom,p2.nom,p2.age);
return 0;
}
 
 

Hors ligne

#2 Le 08/09/2019, à 18:14

elburgos

Re : pointeur et fonction (C)

Ton erreur est là :

scanf("%d",p->age);

ici c'est un pointeur sur un entier (*int) qui est attendu et c'est un entier qui est donné (int).
Pour les prénom et nom précédement ça passe, car un tableau est un pointeur statique.
Donc

p->age # est un entier int

et si je veux son adresse (donc un pointeur) je rajoute simplement un & devant (comme tu l'as fait plus loin dans le code):

&(p->age) # est un pointeur sur int , qui pointe vers l'adresse de ton entier 

Les parenthèse sont optionnelles ici et purement dans un soucis didactique de lisibilité.


Envoyé depuis mon Windows Phone

Hors ligne

#3 Le 08/09/2019, à 18:20

pingouinux

Re : pointeur et fonction (C)

Bonjour,
Tu as dû obtenir un message d'erreur à la compilation, que tu aurais pu fournir :

$ make chri && ./chri
cc     chri.c   -o chri
chri.c: In function ‘changement’:
chri.c:15:9: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
   scanf("%d",p->age);

La ligne n° 15 est à modifier, le second argument devant être un pointeur sur un entier et non un entier.

  scanf("%d",&p->age);

Hors ligne

#4 Le 08/09/2019, à 18:52

chris7522

Re : pointeur et fonction (C)

Merci a tous les deux  . Je ne comprends pas toujours les messages d'erreurs , mais apres vos explications et en relisant ce qui est dit a la compilation , c' est assez explicite finalement .

Hors ligne

#5 Le 08/09/2019, à 18:56

Zakhar

Re : pointeur et fonction (C)

Et on persiste à faire un scanf sur un tableau de longueur sans limiter la longueur lue... la meilleure façon de provoquer un plantage mémoire !
Seul p2 est initialisé, pas p1, donc si on fait Enter/Enter/enter sur le premier joueur, on a de fortes chances de provoquer un crash.

Le C ne gère absolument pas la mémoire pour l'utilisateur, tant que vous n'avez pas bien compris les pointeurs, tableaux, allocations, vous allez faire des choses dangereuses !


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#6 Le 08/09/2019, à 19:39

chris7522

Re : pointeur et fonction (C)

C'est peut etre mieux ainsi :

#include <stdio.h>
#define MAX 10
typedef struct player
{ char prenom[MAX];
  char nom[MAX];
  int age;
}player;

void chargement(player *p)
{ printf("quel est ton prenom ?\n");
  scanf("%s",p->prenom);
  printf("quel est ton nom ?\n");
  scanf("%s",p->nom); 
  printf("quel est ton age ?\n");
  scanf("%d",&p->age);
}
int main(void){
 player p1={"","",0};
 player p2={"","",0};
 printf("\n____1er JOUEUR____\n");
 chargement(&p1);
 printf("\n____2eme JOUEUR____\n");
 chargement(&p2);
 printf("\n____PARTICIPANT______\n");
 printf("Le prenom du 1er joueur est %s,le nom %s et l'age est %d\n",p1.prenom,p1.nom,p1.age);
 printf("Le prenom du 2 eme joueur est %s,le nom %s et l'age est %d\n",p2.prenom,p2.nom,p2.age);
return 0;
}
 
 

Hors ligne

#7 Le 09/09/2019, à 20:56

grigouille

Re : pointeur et fonction (C)

Ce n'est pas encore au point :

$ gcc -o toto toto.c
$ ./toto 

____1er JOUEUR____
quel est ton prenom ?
bla blu bli blo blu
quel est ton nom ?
quel est ton age ?

____2eme JOUEUR____
quel est ton prenom ?
quel est ton nom ?
quel est ton age ?

____PARTICIPANT______
Le prenom du 1er joueur est bla,le nom blu et l'age est 0
Le prenom du 2 eme joueur est bli,le nom blo et l'age est 0
$ 

Xubuntu 18.04

Hors ligne

#8 Le 10/09/2019, à 19:14

claudius01

Re : pointeur et fonction (C)

Bonjour,

Personnellement, je bannirai purement et simplement l’utilisation de cette méthode scanf et autre fscanf au profit de gets et fgets ;-)
Cf. Why is scanf() in C considered harmful or bad? et Disadvantages of scanf

Hors ligne

#9 Le 11/09/2019, à 23:03

Zakhar

Re : pointeur et fonction (C)

grigouille a écrit :

Ce n'est pas encore au point :

$ gcc -o toto toto.c
$ ./toto 

____1er JOUEUR____
quel est ton prenom ?
bla blu bli blo blu
quel est ton nom ?
quel est ton age ?

____2eme JOUEUR____
quel est ton prenom ?
quel est ton nom ?
quel est ton age ?

____PARTICIPANT______
Le prenom du 1er joueur est bla,le nom blu et l'age est 0
Le prenom du 2 eme joueur est bli,le nom blo et l'age est 0
$ 

Oui, en l'occurrence ce n'est pas le bug d'overflow, mais l'autre "bug" (fonctionnalité ?) connu car

man scanf

dit

       s      Matches  a  sequence of non-white-space characters; the next pointer must be a pointer to the initial element of a character array that is long enough to hold the input sequence and the termi‐
              nating null byte ('\0'), which is added automatically.  The input string stops at white space or at the maximum field width, whichever occurs first.

Par conséquent, les "espaces" terminent la lecture de chaque chaîne.

C'est un autre travers de "%s" dans le scanf !

Dernière modification par Zakhar (Le 11/09/2019, à 23:04)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#10 Le 12/09/2019, à 06:47

pingouinux

Re : pointeur et fonction (C)

Bonjour,

Zakhar #9 a écrit :

Par conséquent, les "espaces" terminent la lecture de chaque chaîne.
C'est un autre travers de "%s" dans le scanf !

Il existe néanmoins une option pour choisir les caractères qui seront lus, ou ceux qui termineront la chaîne.
Dans l'exemple ci-dessous, on remplace %s par %[^]0-9-].

man scanf -> Conversions a écrit :

       [      Matches a nonempty sequence of characters from the specified set of accepted characters;  the
              next  pointer must be a pointer to char, and there must be enough room for all the characters
              in the string, plus a terminating null byte.  The usual skip of leading white space  is  sup‐
              pressed.   The string is to be made up of characters in (or not in) a particular set; the set
              is defined by the characters between the open bracket [ character and a close bracket ] char‐
              acter.   The set excludes those characters if the first character after the open bracket is a
              circumflex (^).  To include a close bracket in the set, make it the first character after the
              open  bracket or the circumflex; any other position will end the set.  The hyphen character -
              is also special; when placed between two other characters, it adds all intervening characters
              to  the set.  To include a hyphen, make it the last character before the final close bracket.
              For instance, [^]0-9-] means the set "everything except close bracket, zero through nine, and
              hyphen".
   The  string ends with the appearance of a character not in the (or, with a circum‐
              flex, in) set or when the field width runs out.

Hors ligne

#11 Le 12/09/2019, à 07:49

Zakhar

Re : pointeur et fonction (C)

pingouinux a écrit :

Bonjour,
Il existe néanmoins une option pour choisir les caractères qui seront lus, ou ceux qui termineront la chaîne.

Oui, et le flag m pour allouer automatiquement la bonne longueur de buffer... qu'il ne faut pas oublier de free() quand on n'en a plus besoin !

Dernière modification par Zakhar (Le 12/09/2019, à 07:50)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#12 Le 12/09/2019, à 11:30

chris7522

Re : pointeur et fonction (C)

pingouinux a écrit :

Il existe néanmoins une option pour choisir les caractères qui seront lus, ou ceux qui termineront la chaîne.
Dans l'exemple ci-dessous, on remplace %s par %[^]0-9-].

Ce que tu proposes pour eviter les bugs de toutes sorte est il applicable a mon code ? J'ai tenté mais sans succès .

Hors ligne

#13 Le 12/09/2019, à 15:46

NicoApi73

Re : pointeur et fonction (C)

Bonjour,

#include <stdio.h>

typedef struct player
{ 
	char prenom[20];
	char nom[20];
	int age;
} player;

void changement(player *p)
{ 
	printf("quel est ton prenom ?\n");
	scanf("%19s",p->prenom);
	printf("quel est ton nom ?\n");
	scanf("%19s",p->nom); 
	printf("quel est ton age ?\n");
	while (!scanf("%d",&p->age))
		scanf ("%*c");
}

int main(void)
{
	player p1,p2;

	printf("\n____1er JOUEUR____\n");
	changement(&p1);
	printf("\n____2eme JOUEUR____\n");
	changement(&p2);
	printf("Le prenom du 1er joueur est %s,le nom %s et l'age est %d\n",p1.prenom,p1.nom,p1.age);
	printf("Le prenom du 2 eme joueur est %s,le nom %s et l'age est %d\n",p2.prenom,p2.nom,p2.age);

	return 0;
}

Hors ligne

#14 Le 12/09/2019, à 18:00

chris7522

Re : pointeur et fonction (C)

Dans l'instruction : scanf("%19s",p->prenom);
19 est le nombre de caractere autorisé  ?
Je ne comprends pas :
while (!scanf("%d",&p->age))
        scanf ("%*c");

Hors ligne

#15 Le 12/09/2019, à 18:03

NicoApi73

Re : pointeur et fonction (C)

Oui pour la première question.

Pour ce que tu ne comprends pas, pose toi la question : Que se passe t'il si tu entre plus de 19 caractères pour le prénom, puis pour le nom? Ou que se passe t'il si tu entres une séquence "toto titi tata 4"?

EDIT : comme indiqué par Zakhar il est possible de ne pas limiter à 19 caractères en utilisant le caractère m dans la chaîne de conversion. Je te laisse nous proposer une solution smile

Dernière modification par NicoApi73 (Le 12/09/2019, à 18:06)

Hors ligne

#16 Le 12/09/2019, à 20:49

Zakhar

Re : pointeur et fonction (C)

En fait NicoApi73 n'a pas tout à fait suivi les "bonnes pratiques" pour te donner un exemple rapidement. Mais a donné un meilleure solution qui évite les débordement de mémoire, et c'est essentiel. La "bonne pratique" en question est juste une question de "style".

La "bonne pratique" veut qu'on évite d'utiliser des "Magic numbers", c'est à dire des nombres qui "sortent du chapeau" sans qu'on ne sache pourquoi (comme 19 !).

Les seules choses tolérées sont zéro : 0, un : 1, et éventuellement -1

Pour le reste, et cela vaut dans tous les langages de programmation, on remplace donc le "Magic number" par un  #define en C (ou un enum si c'est approprié), comme tu l'as fait au post #6 @chris7522

#include <stdio.h>

#define NAMES_MAX_LENGTH    19  /* Maximum length of names, not including '\0' */

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)

static const char names_scan_fmt[]= "%" STRINGIFY(NAMES_MAX_LENGTH) "s";

typedef struct player
{ 
        char prenom[NAMES_MAX_LENGTH + 1];  /* +1 for the '\0' */
        char nom[NAMES_MAX_LENGTH + 1];
        int age;
} player;

void changement(player *p)
{ 
        printf("quel est ton prenom ?\n");
        scanf(names_scan_fmt,p->prenom);
        printf("quel est ton nom ?\n");
        scanf(names_scan_fmt,p->nom); 
        printf("quel est ton age ?\n");
        while (!scanf("%d",&p->age))
                scanf ("%*c");
}

int main(void)
{
        player p1,p2;

        printf("\n____1er JOUEUR____\n");
        changement(&p1);
        printf("\n____2eme JOUEUR____\n");
        changement(&p2);
        printf("Le prenom du 1er joueur est %s,le nom %s et l'age est %d\n",p1.prenom,p1.nom,p1.age);
        printf("Le prenom du 2 eme joueur est %s,le nom %s et l'age est %d\n",p2.prenom,p2.nom,p2.age);

        return 0;
}

run :

$ ./test 

____1er JOUEUR____
quel est ton prenom ?
aezeareafzersefwsertqrfsezrf     
quel est ton nom ?
quel est ton age ?
1

____2eme JOUEUR____
quel est ton prenom ?
zeqezq
quel est ton nom ?
eazea
quel est ton age ?
3
Le prenom du 1er joueur est aezeareafzersefwser,le nom tqrfsezrf et l'age est 1
Le prenom du 2 eme joueur est zeqezq,le nom eazea et l'age est 3

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

$ printf "%s" "aezeareafzersefwser" | wc -c
19

On voit bien que même si on saisit une 30 aine de caractère, le scanf n'en prend que 19 (la 20ème position réservée pour le '\0' de fin de chaîne), comme le prouve le comptage avec wc -c
Mais on a bien sûr toujours le "bug", les caractères au delà du 19ème sont lus comme le prénom.
De même si on mettait des espaces.

L'utilisation du flag "m" évite de fixer un maximum (si c'est approprié) pour les noms, et évite donc d'avoir à le définir. Il n'y a plus le syndrome de la saisie trop longue qui est prise en entrée pour le %s suivant... mais on a toujours le sujet des espaces.

Pour tout régler, comme indiqué plus haut, il faut utiliser à la fois "m" et définir les caractères autorisés dans un nom.

... ou alors faire un algorithme à base de fgetc !..

Dernière modification par Zakhar (Le 12/09/2019, à 20:55)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#17 Le 14/09/2019, à 08:55

chris7522

Re : pointeur et fonction (C)

Merci Zakhar d'avoir pris le temps de toutes ces explications smile

Hors ligne