IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

[Pointeurs] Passage de paramètres VERSUS return


Sujet :

C

  1. #1
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut [Pointeurs] Passage de paramètres VERSUS return
    Greetings !!

    Je me suis retrouvé très con lorsqu'un de mes élèves m'a demandé pourquoi lorsqu'il utilisait return ça fonctionnait alors que en modifiant sa liste passée par adresse à la fonction ça ne fonctionnait pas.
    Je "vois" plus ou moins mais j'ai du mal à formuler.

    Voici la fonction qui "fonctionne" lol...

    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
     
    LinkedList* lc_sort(LinkedList *source,int (*fctcompare)(void*,void*))
    {
    	LinkedList	*tmpList=lc_init();
    	lc_Datas		*tmpPG=NULL;
     
    	int position=1;
     
    	if(source==NULL) return NULL;
     
     
    	lc_Datas		*compareme;
    	lc_Datas		*parcours;
    	do
    	{
    		parcours=source->pHead;
    		if(parcours==NULL) break;																							
     
    		compareme=lc_get(source,position);
     
    		if(compareme==NULL) // cas du dernier élément non trié... automatiquement le plus petit
    		{
    			int ItemID=lc_add(parcours->value,tmpList,parcours->dataType,parcours->dataSize);
    			lc_setDisplayByID(tmpList,ItemID,parcours->pDisplay);
    			lc_delete(source,parcours->item_Number);
    			break;
    		}
    		tmpPG=compareme;
     
    		if(parcours->dataType!=compareme->dataType) break;																// on ne compare pas des pommes et des poires (ni C et Python)
    		if(parcours->dataType==uepuserdef && fctcompare==NULL) break;										// si il n'y a pas de fonction de comparaison on quitte
     
    		do
    		{
     
    			switch(parcours->dataType)
    			{
    				case uepbyte:			if(*((char*)parcours->value)>*((char*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case uepshort:    if(*((short*)parcours->value)>*((short*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case uepint:			if(*((int*)parcours->value)>*((int*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case uepfloat:		if(*((float*)parcours->value)>*((float*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case ueplong:			if(*((long*)parcours->value)>*((long*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case uepllong:	  if(*((long long*)parcours->value)>*((long long*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case uepdouble:		if(*((double*)parcours->value)>*((double*)tmpPG->value)) tmpPG=parcours;
    													break;
    				case uepldouble:	if(*((long double*)parcours->value)>*((long double*)tmpPG->value)) tmpPG=parcours;
    													break;	
    				default:					if(fctcompare(parcours->value,tmpPG->value)>0) tmpPG=parcours;
    			}
    			parcours=parcours->pNext;
    		}while(parcours!=NULL);
    		// Ici nous avons l'élément le plus grand de la collection de valeurs...
    		lc_Datas *copy=lc_search(source,tmpPG->item_Number);
    		int ItemID=lc_add(copy->value,tmpList,tmpPG->dataType,tmpPG->dataSize);
    		lc_setDisplayByID(tmpList,ItemID,compareme->pDisplay);
    		lc_delete(source,tmpPG->item_Number);
    		tmpPG=NULL;
    	}while(source->NbElem>0);
    	// La liste devrait être triée
    	return tmpList;
    }
    Ici on obtient une liste chaînée contenant "quelque chose" qu'il faut trier.
    Cette manière de procéder permet dans le code source de faire quelque chose comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    printf("Entiers: \n");
    lc_showList(ll_datas_int);
    ll_datas_int=lc_sort(ll_datas_int,NULL);
    printf("Entiers triés: \n");
    lc_showList(ll_datas_int);
    ...et d'obtenir ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Entiers: 
    35
    66
    38
    24
    85
    Entiers triés: 
    85
    66
    38
    35
    24
    ...ce qui est magnifique !!

    Maintenant, au départ l'étudiant -- avait fait ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void lc_sort(LinkedList *source,int (*fctcompare)(void*,void*))
    {
      LinkedList	*tmpList=lc_init();
    ...
    et tout à la fin avant la fin de la fonction
      source=tmpList;
    }
    ...là par contre ça ne fonctionnait pas...

    Je dois bien dire que -- pour moi -- ça devrait fonctionner, source est un pointeur vers une structure de type LinkedList et tmpList pointe sur une structure de type LinkedList.
    Le passage par adresse, pour moi, permet de modifier le contenu de l'argument (ou paramètre) passé à la fonction... or ici manifestement ce n'est pas le cas, au retour de la fonction, ll_datas_int contient bien une adresse mais
    tout le reste de la structure est NULL... du coup je sais pas trop bien expliquer.

    Ici source est placé sur la pile donc je supposes que l'adresse est codée sur 64bits, tout comme tmpList on aurait tendance de le dire, mais, lui, il provient du heap... lc_init() cache un calloc() ... je pense que le soucis vient de là.
    Lorsque nous faisons return nous "dépilons" donc il devrait y avoir un alignement par rapport aux données pointées par les pointeurs respectifs.

    J'ai vraiment du mal à être clair sur ce sujet et je ne parviens pas à expliquer pourquoi, ici, les pointeurs n'ont pas l'air de fonctionner et semblent ne pas permettre de se défaire, notamment, de la "limitation liée aux variables locales aux fonctions".

  2. #2
    Membre expérimenté
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Juillet 2020
    Messages
    352
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Juillet 2020
    Messages : 352
    Points : 1 376
    Points
    1 376
    Par défaut
    Bonjour,
    parce que source est une variable locale à la fonction et que sa valeur est assignée lors de l'appel. Si tu en modifies la valeur il n'y aura pas d'effets visibles au retour de l'appel.

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 963
    Points
    32 963
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par hurukan Voir le message
    Je dois bien dire que -- pour moi -- ça devrait fonctionner
    Ouch

    Citation Envoyé par hurukan Voir le message
    Le passage par adresse, pour moi, permet de modifier le contenu de l'argument (ou paramètre) passé à la fonction... or ici manifestement ce n'est pas le cas, au retour de la fonction, ll_datas_int contient bien une adresse mais
    tout le reste de la structure est NULL... du coup je sais pas trop bien expliquer.
    Un pointeur permet d'accéder à ce qui est pointé et donc le modifier via un déférencement.
    Ton code ne fait pas ça du tout. source=tmpList; modifie ce qui est pointé par la variable locale.

    Citation Envoyé par hurukan Voir le message
    Ici source est placé sur la pile donc je supposes que l'adresse est codée sur 64bits, tout comme tmpList on aurait tendance de le dire, mais, lui, il provient du heap... lc_init() cache un calloc() ... je pense que le soucis vient de là.
    Lorsque nous faisons return nous "dépilons" donc il devrait y avoir un alignement par rapport aux données pointées par les pointeurs respectifs.
    Kamoulox, tu cherches un problème où il n'y en a pas, tu as juste oublié la portée des variables.
    Ton code est litérallement void toto(int test) { test = 3; }, ou pour reprendre un pointeur void toto(int* test) { test = NULL; } et tu t'étonnes que le paramètre donné à toto ne change pas après son appel dans le code appelant.

    Citation Envoyé par hurukan Voir le message
    J'ai vraiment du mal à être clair sur ce sujet et je ne parviens pas à expliquer pourquoi, ici, les pointeurs n'ont pas l'air de fonctionner et semblent ne pas permettre de se défaire, notamment, de la "limitation liée aux variables locales aux fonctions".
    la règle est plutôt simple il me semble : un paramètre out doit être un pointeur.
    Mais si ta donnée est déjà un pointeur, ben ça devient juste un double pointeur.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Citation Envoyé par Bousk Voir le message
    tu as juste oublié la portée des variables.
    .
    Je me suis auto-suggéré que les pointeurs permettent de "passer outre" la portée des variables locales.
    Je vais corriger cela... j'ai indiqué des conn****s à mes élèves... tout ça pour pas leur faire peur avec les doubles indirections à mon avis (je cherche des excuses, je suis page 80 dans le livre "trouver des excuses quand on ne sait pas programmer en C") ^ ^

    Merci, j'y vois plus clair.

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par hurukan Voir le message
    Je me suis auto-suggéré que les pointeurs permettent de "passer outre" la portée des variables locales.
    En réalité tu avais raison car c'est exactement ça.
    Tu as une fonction, ex main, toutes les variables de cette fonction, même les variables locales, ne sont visibles que depuis le main. MAIS elles existent durant toute la vie du main.
    Si tu appelles une autre fonction, les variables du main continuent à exister même pendant que la fonction s'exécute. Maintenant la fonction ne connait pas les variables du main MAIS connait la mémoire du programme. Donc si la fonction connait les adresses des variables du main, elle pourra quand-même y accéder et donc passer outre le fait qu'elle n'y a pas accès via leur nom.

    Ton souci principal vient de la syntaxe. Si le main possède un int, il faut que la fonction connaisse l'adresse de cet int pour pouvoir y accéder. Or l'adresse d'un int se stocke dans un int étoile. Donc tu écriras int *pt et tu t'arrangeras pour que "pt" récupère l'adresse de l'int en question. Et dans la fonction, tu utiliseras *pt pour travailler sur la variable qui correspond à cette adresse.
    Remplaçons maintenant dans cette phrase "int" par "float", tout reste à l'identique. Si le main possède un float , il faut que la fonction connaisse l'adresse de ce float pour pouvoir y accéder. Or l'adresse d'un float se stocke dans un float étoile. Donc tu écriras float *pt et tu t'arrangeras pour que "pt" récupère l'adresse du float en question. Et dans la fonction, tu utiliseras *pt pour travailler sur la variable qui correspond à cette adresse.
    Ainsi la règle est vraiment triviale: l'adresse d'un truc se stocke dans un "truc étoile". Bref on rajoute toujours une étoile de plus que le type de départ. Et dans la fonction, travailler sur "*pt" permet de travailler sur la variable correspondante par ricochet.

    Mais si "truc" est déjà un pointeur? Hé bien la règle reste exactement la même. On rajoute une étoile de plus. Donc si truc est de type "int étoile" son adresse sera stockée dans un "int étoile étoile" sans se poser de question et surtout sans s'effrayer de voir d'un coup deux étoiles à devoir gérer, car en réalité on n'en gère qu'une seule, car on ne gère toujours qu'un seul niveau d'indirection (la fonction peut modifier le pointeur cible mais n'a pas à savoir à quoi celui-ci correspond)

    Résumé
    Code c : 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
    #include <stdio.h>
     
    void modifInt(int *, int);
    void modifPt(int **, int*);
     
    int main() {
    	int a=123;
    	int b=456;
    	int *pt=&a;
    	printf("a=%d, b=%d, *pt=%d\n", a, b, *pt);
     
    	modifInt(&a, 1234);
    	printf("a=%d, b=%d, *pt=%d\n", a, b, *pt);
     
    	modifInt(&b, 4567);
    	printf("a=%d, b=%d, *pt=%d\n", a, b, *pt);
     
    	modifPt(&pt, &b);
    	printf("a=%d, b=%d, *pt=%d\n", a, b, *pt);
     
    	int c=8910;
    	modifPt(&pt, &c);
    	printf("a=%d, b=%d, *pt=%d\n", a, b, *pt);
    }
     
    void modifInt(int *pt, int n) {
    	*pt=n;
    }
     
    void modifPt(int **pt, int *n) {
    	*pt=n;
    }
    Deux étoiles pour ma dernière fonction? Peu importe elle n'en gère qu'une seule. Bien entendu, il faut toujours savoir de quoi il s'agit quand on écrit le travail de ladite fonction.

    Citation Envoyé par hurukan Voir le message
    tout ça pour pas leur faire peur avec les doubles indirections
    Il n'y a absolument pas de quoi avoir peur. Mais c'est un point qu'il ne faut pas non plus laisser dans l'oubli ou le déni. Chaque fois qu'on se décale, on rajoute une étoile de plus. Si on se décale deux fois on aura deux étoiles, si on se décale 3 fois on aura 3 étoiles etc. Evidemment le cerveau humain étant limité, il y aura fatalement une limite dans la conceptualisation des choses mais cette limite ne doit pas être causée par la peur de l'étoile.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Merci bien Sve@r...

    Moi personnellement ça ne m'effraie pas mais quand je dois expliquer cela à mes élèves, je préfères avoir plusieurs sons de cloche parce que j'ai tendance à "simplifier/raccourcir" pour pas devoir appeler les ambulances.
    D'autant que je ne suis pas du tout un expert en C... j'ai lu des bouquins j'ai programmé mais expliquer clairement certains concepts avec rigueur et correction des fois ça coince, comme ici.
    J'ai des collègues qui passent sous Python, carrément, soi-disant que c'est plus simple pour les débutants qui n'ont jamais fait de programmation... moi je n'aime pas ce langage, je suis beaucoup plus à l'aise avec les langages compilés dans la famille du C.

    Et puis il y a des bizarreries sous Python... j'aimerais bien entendre mes collègues se dépatouiller pour répondre aux questions des élèves ^^
    C'est pas si simple, la syntaxe est horrible (on se croirait revenus au Cobol ANS82) et dès qu'on doit travailler sur plusieurs modules
    on se rend compte que sans PyCharm c'est la galère... sans parler des projets vraiment balaises où s'y retrouver dans le code source nous ramène à la magnifique époque de l'assembleur.
    Quand je pense que Python a plus de 30 ans... pas de "backward compatibility" (même pas d'obsolete flag comme dans certains langages)... je comprends pas du tout leur choix.

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hurukan Voir le message
    J'ai des collègues qui passent sous Python... je comprends pas du tout leur choix.
    Aie aie aie... sais-tu que je suis passé du C (que j'ai enseigné plusieurs années) à Python en 2007 et que je n'ai jamais regretté? Je suis d'ailleurs très actif dans le fofo Python. Et j'ai même écrit un tuto Python qui sera bientôt mis en ligne sur le site (si tu le veux en avant-première...). D'ailleurs il m'arrive aussi parfois de l'enseigner et si tu as un exemple de "bizarreries" dont tu parlais je serai heureux de t'expliquer pourquoi elles n'en sont en réalité pas et si elles te gênent comment t'en débarrasser
    Quand tu vois qu'il faut 15 lignes de C pour agrandir un tableau (realloc+test+...) alors qu'en Python tu fais simplement tab.extend(autre_tab)...
    Après il y a des langages qu'on aime, et d'autres qu'on n'aime pas. Ou qu'on aime mais qu'on sait parfaitement inadaptés à tel ou tel projet et ça c'est autre chose. On peut passer de C à Python pour telle ou telle raison, ou passer de Python au C pour telle ou telle autre raison, mais effectivement passer de C à Python parce que "c'est plus simple pour apprendre à programmer" effectivement je suis d'accord avec toi. En C tu apprends des technosX, en Python des technosY, parfois le C et Python possèdent une technoZ similaire mais de syntaxe différente et les deux langages sont tous deux importants dans leurs catégories.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  8. #8
    Expert éminent sénior
    Avatar de Kannagi
    Homme Profil pro
    cyber-paléontologue
    Inscrit en
    Mai 2010
    Messages
    3 214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : cyber-paléontologue

    Informations forums :
    Inscription : Mai 2010
    Messages : 3 214
    Points : 10 140
    Points
    10 140
    Par défaut
    Citation Envoyé par hurukan Voir le message
    J'ai des collègues qui passent sous Python, carrément, soi-disant que c'est plus simple pour les débutants qui n'ont jamais fait de programmation... moi je n'aime pas ce langage, je suis beaucoup plus à l'aise avec les langages compilés dans la famille du C.
    C'est pas soi-disant , c'est plus simple.
    Sinon oui , c'est une excellente chose de commencer par Python ,je suis sur un Discord de prog ,quand un débutant veut apprendre la prog (et qui se trouve dans le canal C) on le redirige vers Python.

    Le C a pas mal de tare , notamment de nombreux UB , et les débutants passe trop de temps sur la résolution des UB et segfaults que à la résolutions des algo.
    De plus pour résoudre un soucis de segfaults , ça demande d'utiliser un debugger ,et celui du C est tout sauf friendly.

    j'ai indiqué des conn****s à mes élèves...
    Une raison de ne pas enseigner le C , (si on plus soit même on maîtrise pas la langage )

    Sans parler , que il faut avoir un gros bagage derrière soi pour enseigner les bonnes méthodes en C , parce que bon programmé en C , au final c'est aussi enfaîte suivre quelque méthode et règles pour pas faire trop de connerie...

  9. #9
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Je vais pas troller, c'est pas mon genre... je n'aime pas Python.
    Je me vois pas du tout l'enseigner, je n'en ai aucune envie, dans notre programme des cours on ne voit de l'OO qu'en sixième (je donne cours aux 4,5,6 en Belgique), et je me sens plus à l'aise avec Java.

    Bon, je dois reconnaître que PyGame en jette un max dans les présentations de l'option en fin d'année...
    Concernant les bizarreries, comme je maîtrise pas du tout le Python, je vais avoir du mal à établir une liste exhaustive, ce sont des retours d'expérience de professionnels que je relaierais.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Passage par référence versus par pointeur
    Par Seabast888 dans le forum Débuter
    Réponses: 14
    Dernier message: 14/09/2009, 18h17
  2. Réponses: 6
    Dernier message: 31/05/2008, 15h45
  3. Base pointeurs + passage de paramètre
    Par lyes312 dans le forum Langage
    Réponses: 2
    Dernier message: 31/01/2008, 22h47
  4. pointeurs et passage de paramètres
    Par nadsky dans le forum C
    Réponses: 15
    Dernier message: 20/07/2007, 19h56
  5. Passage de paramètre par adresse, pointeur
    Par spileo dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 19/01/2007, 19h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo