OVH Cloud OVH Cloud

allocation dynamique d'un pointeur

56 réponses
Avatar
prat
bonjour,

J'ai vu dans un bouquin de cours de langage C sur les pointeurs
l'algorithme suivant:

main( )
{
float * pr1,*pr2,*pr3,somme=3D0;


/*allocation dynamique */
pr1=3Dmalloc(sizeof(float));
pr2=3Dmalloc(sizeof(float));
pr3=3Dmalloc(sizeof(float));

/*saisie*/
printf("Entrez 3 notes: ");
scanf("%f %f %f ",pr1,pr2,pr3);

/*calcul de la somme*/
somme=3D*pr1+ *pr2+ *pr3;

/*calcul de la moyenne*/
printf("Moyenne :%f \n ",somme/3);

/*liberation de la m=E9moire*/
free(pr1);
free(pr2);
free(pr3);

}

Mes questions sont:
-les pointeurs ont =E9t=E9 d=E9clar=E9 initialement =E0 la premi=E8re
instruction,pourquoi en plus une allocation dynamique avec malloc( ) et
free( )?Est-elle n=E9cessaire?
-aurai-je pu utiliser l'allocation dynamique sans avoir au pr=E9alable
d=E9clar=E9 mes pointeurs comme =E0 la premi=E8re instruction?


merci.

10 réponses

1 2 3 4 5
Avatar
Marc Boyer
Le 17-01-2007, prat a écrit :
J'ai vu dans un bouquin de cours de langage C sur les pointeurs
l'algorithme suivant:

main( )
{
float * pr1,*pr2,*pr3,somme=0;

/*allocation dynamique */
pr1=malloc(sizeof(float));
pr2=malloc(sizeof(float));
pr3=malloc(sizeof(float));

/*saisie*/
printf("Entrez 3 notes: ");
scanf("%f %f %f ",pr1,pr2,pr3);

/*calcul de la somme*/
somme=*pr1+ *pr2+ *pr3;

/*calcul de la moyenne*/
printf("Moyenne :%f n ",somme/3);

/*liberation de la mémoire*/
free(pr1);
free(pr2);
free(pr3);

}

Mes questions sont:
-les pointeurs ont été déclaré initialement à la première
instruction,pourquoi en plus une allocation dynamique avec malloc( ) et
free( )?Est-elle nécessaire?


Si le bouquin n'explique pas celà, il est de bien piètre qualité.
Un pointeur ne sert qu'à désigner une *autre* variable. Le code
float* prt1;
déclare un pointeur prt1 qui désignera un float. Mais ce float
n'est pas construit lorsque l'on déclare le pointeur.
L'instruction 'malloc(sizeof(float))' cherche de la place pour
stoquer un float. Si elle réussit (laissons de côté pour commencer
la getsion des échec), un tels float est créé.
ptr1= malloc(sizeof(float));
signifie alors que ptr1 désigne la variable nouvellement crée.
On y accède par *ptr1.

Sans allocation dynamique, ptr1 désigne nimporte quoi.

-aurai-je pu utiliser l'allocation dynamique sans avoir au préalable
déclaré mes pointeurs comme à la première instruction?


Et comment ? Un appel à 'malloc' réserve de la mémoire, mais si tu
ne stoque pas dans un pointeur où est cette mémoire, tu l'as perdu.
Un peu comme si tu réserves un billet électronique à la SNCF mais
que tu n'imprimes pas ou ne note pas l'identifiant du billet. Lorsqu'il
faudra prendre le train, comment se souvenir de la place qui t'a
été attribuée ?

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
Antoine Leca
ALain Montfranc écrivit:
Eric Levenez a écrit
Donc si tu as trouvé ce code dans un bouquin : poubelle direct.


Je vous trouve un tantinet intégriste.


Les questions que posent "prat" montrent clairement que le « bouquin » n'a
pas correctement atteint son objectif ;-). D'où l'à-propos du conseil d'Éric
de changer de livre.


Essayez de montrer l'utilisation d'un pointeur à un débutant


Montrer à un débutant l'utilisation des pointeurs par

float * pr1,*pr2,*pr3;
/*...*/

scanf("%f %f %f ",pr1,pr2,pr3);

est une horreur: même si c'est effectivement correct en C, cela suppose
d'avoir passer plusieurs étapes préalables, en particulier d'avoir fait
assimiler passage par référence et pointeurs en C.
Sans compter qu'un tel code peut ne pas marcher pendant un bon bout de
temps, sans que le pauvre débutant ne comprenne pourquoi, à cause de
l'absence totale de contrôle d'erreur.
Et le comble est qu'un tel code n'est pas représentatif, car personne
n'utilise l'allocation dynamique pour 3 flottants, mais bien plutôt pour
allouer des tableaux : donc il est beaucoup plus facile et logique
d'expliquer les pointeurs puis l'allocation dynamique à partir des tableaux.


AMHA il vaut mieux commencer par du code simpliste puis le
critiquer en expliquant ses défauts pour ensuite aboutir
au code propre.


Oui.
Mais le code ci-dessus n'est pas simpliciste, il est plutôt trompeur, à mon
avis. Lorsque l'on apprend scanf(), on devrait ÀMHA apprendre à passer
systématiquement des références, donc ici:

if( 3 != scanf("%f %f %f ", &*pr1, &*pr2, &*pr3) )
PROTESTE();


Les critiques sur int main ou return 0 sont d'un autre ordre, cela relève du
style et je n'ai pas à me prononcer.


Antoine


Avatar
Marc Boyer
Bruno Desthuilliers a écrit :

int main(void) /* au minimum */

ou

int main(int argc, char **argv)



Ben non. Il me semble bien que
int main()
est tout à fait correct.


http://www.faqs.org/faqs/fr/comp/lang/faq-c-2/

8<------------------------------------------------
9.4 Quels sont les prototypes valides de main() ?


Oui, mais il ne s'agit pas AMHA d'un prototype ici,
mais de la declaration de la fonction.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)



Avatar
Bruno Desthuilliers
[...]

Il ne faut pas confondre du code simpliste avec du mauvais code.



On peut avoir du code débile justifié par une progression pédagogique.
Si on n'a pas à disposition le fil du chapitre, on peut être injustement
négatif sur le bout de code proposé.


Je conçois mal quelle "progression pédagogique" peut justifier le fait
de ne pas respecter la signature de main().

D'un coté, il y a sur ce groupe moribond un plaisir pervers à critiquer
toute tentative un peu imparfaite.


Je ne suis plus guère ce groupe qu'épisodiquement, mais c'est quand même
grâce à cette tendance à la critique (qu'on retrouve(ait?) aussi sur
clc) que j'ai le plus appris.

D'un autre coté, même (et surtout)
dans un ouvrage de niveau zéro je n'aurais pas osé proposer le code de
l'OP.


<aol />


Avatar
Bruno Desthuilliers
Bruno Desthuilliers writes:



bonjour,

J'ai vu dans un bouquin de cours de langage C sur les pointeurs
l'algorithme suivant:

main( )


Bon. Tu peux d'ors et déjà jeter le bouquin (ou caler une armoire
avec, ou t'en servir à d'autres fins si le papier est adéquat...)

C'est:

int main(void) /* au minimum */

ou

int main(int argc, char **argv)



Ça dépend du standard. En K&R,


Un peu obsolète, non ? Il y a eu le K&R2, depuis!-)

Mes questions sont:
-les pointeurs ont été déclaré initialement à la première
instruction,pourquoi en plus une allocation dynamique avec malloc( ) et
free( )?Est-elle nécessaire?


Ca dépend de ce que tu veux faire. En l'occurrence, je ne vois pas de
besoin pour dans le code (pourri) en question. Mais es-tu sûr d'avoir
compris les notions de pointeur et d'allocation dynamique ?



Bonne question. La réponse à apporter serait que déclarer un pointeur
ne l'initialise pas! Il peut pointer n'importe où, et même être
invalide.

Si on ne l'initialise pas avec une adresse (soit obtenue par malloc,
soit prise par l'opérateur unaire &), ça peut planter si on le
déréférence.


J'aime assez le "ça peut" !-)


-aurai-je pu utiliser l'allocation dynamique sans avoir au préalable
déclaré mes pointeurs comme à la première instruction?




On pourrait écrire:

float *pr1=malloc(sizeof(*pr1));
float *pr2=malloc(sizeof(*pr2));
float *pr3=malloc(sizeof(*pr3));
...


Et ne pas oublier de vérifier la valeur de pr1, pr2 et pr3 avant
d'entreprendre quoi que ce soit d'autre...






Avatar
Bruno Desthuilliers

int main(void) /* au minimum */

ou

int main(int argc, char **argv)



Ben non. Il me semble bien que
int main()
est tout à fait correct.


http://www.faqs.org/faqs/fr/comp/lang/faq-c-2/

8<------------------------------------------------
9.4 Quels sont les prototypes valides de main() ?


La fonction main() renvoie toujours un int.
Les prototypes valides sont :


int main(void);
int main(int argc, char * argv[]);


Tout autre prototype n'est pas du tout portable et ne doit jamais
être utilisé (même s'il est accepté par votre compilateur).

(...)

Enfin, rappelons que le prototype suivant

int main () ;

est parfaitement valide en C++ (et est synonyme du premier
présenté ici), mais ne l'est pas en C.
8<------------------------------------------------


Avatar
Marc Boyer
Bruno Desthuilliers a écrit :
(snip)
Si le bouquin n'explique pas celà, il est de bien piètre qualité.
Un pointeur ne sert qu'à désigner une *autre* variable.


s/désigner une *autre* variable/stocker une adresse mémoire/


Non, la presentation que tu donnes est ce qui est fait par les
compilateurs, et qui parait naturelle pour quiconque a des bases
d'assembleur, mais ce n'est qu'un moyen de realiser une semantique.
Dans un interpreteur C, un pointeur pourrait tout a fait stoquer
une chaine qui soit le nom de la variable.

Le code
float* prt1;
déclare un pointeur prt1 qui désignera un float.


déclare une variable (de type pointeur sur float) ptr1, qui stockera une
adresse mémoire dont le contenu sera interprété comme un float quand on
déréférencera le pointeur (nb: opération consistant à lire le contenu du
bloc mémoire dont le pointeur donne l'adresse).


Idem

Mais ce float
n'est pas construit lorsque l'on déclare le pointeur.
L'instruction 'malloc(sizeof(float))' cherche de la place pour
stoquer un float. Si elle réussit (laissons de côté pour commencer
la getsion des échec), un tels float est créé.


s/un tels float est créé/un bloc mémoire d'une taille suffisante pour
stocker un float est réservée, et son adresse de départ retournée/.


Oui, quand je parle a un debutant, je laisse de cote certaines
faiblesses du C.

ptr1= malloc(sizeof(float));
signifie alors que ptr1 désigne la variable nouvellement crée.


s/désigne la variable nouvellement crée/a pour valeur l'adresse de
départ du bloc mémoire réservé/.


Ce qui est equivalent.

Sans allocation dynamique, ptr1 désigne nimporte quoi.


float f=3.14;
float *ptr pf = &f;

Pas d'allocation de mémoire dynamique ici, et pourtant, pf pointe sur
une adresse valide....


Et oui.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Avatar
Marc Boyer
Le 18-01-2007, Marc Boyer a écrit :
Bruno Desthuilliers a écrit :
int main(void) /* au minimum */

ou

int main(int argc, char **argv)


Ben non. Il me semble bien que
int main()
est tout à fait correct. Le void ne sert que s'il s'agit
d'une déclaration de fonction, mais lors de la définition,
cela ne donne pas d'ambiguïté.


Je me repond a moi meme: ben non, je m'etais plante.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Avatar
Bruno Desthuilliers
(snip)
Si le bouquin n'explique pas celà, il est de bien piètre qualité.
Un pointeur ne sert qu'à désigner une *autre* variable.


s/désigner une *autre* variable/stocker une adresse mémoire/

Le code
float* prt1;
déclare un pointeur prt1 qui désignera un float.


déclare une variable (de type pointeur sur float) ptr1, qui stockera une
adresse mémoire dont le contenu sera interprété comme un float quand on
déréférencera le pointeur (nb: opération consistant à lire le contenu du
bloc mémoire dont le pointeur donne l'adresse).

Mais ce float
n'est pas construit lorsque l'on déclare le pointeur.
L'instruction 'malloc(sizeof(float))' cherche de la place pour
stoquer un float. Si elle réussit (laissons de côté pour commencer
la getsion des échec), un tels float est créé.


s/un tels float est créé/un bloc mémoire d'une taille suffisante pour
stocker un float est réservée, et son adresse de départ retournée/.

ptr1= malloc(sizeof(float));
signifie alors que ptr1 désigne la variable nouvellement crée.


s/désigne la variable nouvellement crée/a pour valeur l'adresse de
départ du bloc mémoire réservé/.

On y accède par *ptr1.


s/On y accède par *ptr1/On peut alors manipuler le contenu de cette
adresse mémoire par la notation *ptr1/

Sans allocation dynamique, ptr1 désigne nimporte quoi.


float f=3.14;
float *ptr pf = &f;

Pas d'allocation de mémoire dynamique ici, et pourtant, pf pointe sur
une adresse valide....

<OP>
En C, les variables déclarée dans une fonction ne sont initialisée à une
valeur par défaut, et leur valeur est totalement aléatoire. Une bonne
habitude est de les initialiser systématiquement lors de la déclaration.
Dans le cas des variables de type pointeur - qui, comme tu devrais
l'avoir compris maintenant, ont pour valeur une adresse mémoire - cela
implique qu'un pointeur non initialisé est susceptible de pointer vers
une adresse totalement aléatoire - n'appartenant éventuellement même pas
au processus. Il va sans dire qu'en essayant d'écrire à une telle
adresse, on s'expose aux résultats les plus surprenants...

Les deux utilisations possibles d'une variable de type pointeur sont
1/ de lui affecter l'adresse d'une autre variable, auquel cas le
pointeur sera comme un "alias" de la variable en question. C'est parfois
utile, mais rien de bien passionnant jusque là.
2/ de lui affecter l'adresse retournée par un appel à malloc() (ou
équivalent), qui est réservée dans un espace mémoire (usuellement appelé
le "tas", ou "heap") différent (et plus important) que celui utilisé
pour les variables "automatiques" (espace usuellement appelé la "pile",
ou "stack"), et qui (théoriquement) reste disponible durant toute
l'exécution du programme.
</OP>


-aurai-je pu utiliser l'allocation dynamique sans avoir au préalable
déclaré mes pointeurs comme à la première instruction?



Et comment ? Un appel à 'malloc' réserve de la mémoire, mais si tu
ne stoque pas dans un pointeur où est cette mémoire, tu l'as perdu.


Où, plus précisément, tu a perdu tout moyen d'y accéder - et donc de la
libérer. Ce qui provoque ce qu'on appelle une fuite mémoire, dont le
résultat peut s'avérer gênant sur un process "longue durée"... (bin oui,
la mémoire, c'est pas extensible à l'infini...).


Avatar
Bruno Desthuilliers




int main(void) /* au minimum */

ou

int main(int argc, char **argv)



Ben non. Il me semble bien que
int main()
est tout à fait correct.


http://www.faqs.org/faqs/fr/comp/lang/faq-c-2/

8<------------------------------------------------
9.4 Quels sont les prototypes valides de main() ?



Oui, mais il ne s'agit pas AMHA d'un prototype ici,


En l'absence de déclaration préablable (ce qui est généralement le cas
pour main() !-), la définition vaut déclaration...

mais de la declaration de la fonction.


s/déclaration/définition/

Si ma mémoire est bonne, le cas de main() est particulier, en ce que
c'est le point d'entrée du programme. Il y a donc une convention à
respecter entre le programme et l'environnement hôte. Toute
implémentation conforme à la norme garantie que les deux signatures
mentionnées sont valides. En dehors de ça, c'est au bon vouloir de
l'implémentation et de la plateforme hôte. En l'occurrence, et si ma
mémoire est bonne, la signature que tu proposes déclare une fonction
prenant un nombre indéterminé d'arguments... (que quelqu'un me corrige
si je dis une couennerie...)




1 2 3 4 5