[Débutant en C] L'intérêt du pointeur.

15 sujets de 1 à 15 (sur un total de 44)

  • Tiki

      #221906

      Bonjour,

      Il fallait bien que je m’y mette un jour. Et cette fois-ci, c’est parti pour de bon, je me mets à la programmation. Certain me diront que le C n’est peut-être pas le meilleur choix. Ils auront sûrement raison, mais voilà, le cursus que je reprends l’impose.
      Bref, me voici rendu au type pointeur, apparemment très important. Et pourtant cette importance m’échappe. Si j’ai bien compris, il correspond à une zone mémoire dont le contenu est l’adresse d’une autre zone mémoire, et il occupe en mémoire la même place que la variable vers laquelle il pointe. Il permet aussi de passer l’adresse de variable à des fonctions. Bref, ce que je ne comprends pas, c’est pourquoi on donne à la fonction l’adresse du pointeur pour ensuite aller voir ce qu’il y a dans la variable. Pourquoi ne pas allez voir la variable tout de suite ? Ca ne ferait pas économiser de la mémoire ?
      Pardon pour la naïveté de ma question. Faut bien débuter 😀

      Et, merci

      Tiki

      huggyone76

        #221910

        Ah les pointeurs !!! Toute ma jeunesse d’ingénieur développement ! 😀

        Alors… Par exemple, comme un pointeur contient une adresse, il peut contenir l’adresse vers n’importe quel type de variable. Du coup, tu ne t’enquiquines pas à savoir ce qu’il y a derrière, tu manipules juste l’adresse de la variable ! Du coup, passer en paramètre un octet, un mot ou un long… et bien tu ne passeras que l’adresse de ta variable et ça peut faire économiser de la mémoire en fait !

        Ca permet aussi les structures (listes) chaînées… c’est beaucoup utilisé pour les tris par exemple. Et encore une fois ça permet d’avoir une structure dynamique. Ca te permettra par exemple d’agrandir ou diminuer des listes de « n’importe quoi » alors que si tu utilises un tableau, tu auras une structure beaucoup plus rigide…

        Tu peux te faire des tableaux de pointeurs. Et là du coup c’est d’une part dynamique (chaque élément de ton tableau peut pointer vers des variables qui évoluent sans se soucier de la structure du tableau), et en plus tu stockes des adresses, peu importe ce qui se cache derrière ! C’est du coup très « souple ».

        Bon, j’avoue que dans mon esprit c’est assez clair, et dans mon explication… je ne sais pas trop… LOL ! 🙂 Mais en gros : souplesse et dynamisme. Voilà ce que permettent les pointeurs dans mes souvenirs… 😉

        A500+ACA500 - A600+Vampire 2+indivision ECS - A1200+Vampire V2 1200 - Mac Mini 1.42 sous MOS - Just CPC 128k - CPC 6128 - Atari STE 4Mo/CosmosEx - Atari Falcon CT60/SuperVidel 🙂
        C64C + 1541-II + Lecteur K7 + SD - Sharp X68000 CZ-601C 4Mo + CF - Sharp X68000 CZ-611C 10Mo + CF + ext. MIDI

        Joufflu

          #221911

          Hello,

          L’intérêt de ne pas aller voir la variable tout de suite est à chercher dans les performances car le programme dans ce cas va sinon réaliser une copie de la variable dans la stack mémoire : c’est ce qu’on appelle le passage par valeur.

          Évidemment sur un type primitif  (int…)ou une petite chaine de caractère le gain de performance n’est pas visible, mais imagine que tu travaille en traitement d’image  sur une  bitmap (qui n’est en somme qu’un tableau en 2 dimensions d’octets représentant chaque pixel) assez grosse (de quelques méga) , tu va te farcir une copie de la bitmap en mémoire à chaque appel de fonction ou tu la passeras en argument. Dans ce cas, tu as vraiment l’intérêt du pointeur car tu ne passera que 4 octets à la fonction ( la taille du pointeur sur un système 32 bits) et tu accèderas ensuite aux zones de l’image en itérant sur l’adresse (monpointeur+i pour te balader sur une ligne de l’image par exemple).

          Le deuxième gros intérêt des pointeurs en C est de permettre le retour multiple d’une fonction. Tu ne peux retourner qu’une valeur avec « return » à la fin de la fonction, mais tu peux aussi modifier des pointeurs passés en arguments ce qui simule un retour multiple.

           

          hope this help 😉

          corto

            #221913

            Super, Tiki, tu fais le grand saut !

            Pour faire simple : La valeur d’une variable de type pointeur contient une adresse.

            Les pointeurs sont utiles avec les structures de données (tableaux, buffer, listes chaînées, …), on utilise alors l’adresse de ces structures pour les manipuler, les parcourir. Et d’ailleurs, souvent, ces structures sont allouées dynamiquement avec malloc() qui retourne … un pointeur !

            Pour le passage de pointeur en paramètre de fonctions, ce qui est indispensable de comprendre, c’est que les paramètres reçus par une fonction sont des copies des valeurs des variables utilisées par l’appelant.

            Si tu as une fonction « void mafonction(int b) { … } », qu’elle est appelée ainsi « mafonction(v) » avec « v=4 » par exemple et que dans cette fonction b est modifiée, pour l’appelant v vaut toujours 4.

            J’en viens aux pointeurs : comme c’est une variable, quand il est passé en paramètre de fonction, et modifié dans cette fonction, pour l’appelant sa valeur n’a pas changer mais, différence déterminante : dans la fonction, les données changées en mémoire grâce à ce pointeur ont bel et bien été modifiées !

            Bonne découverte ! Et bon courage !

            Jul

              #221914

              « et il occupe en mémoire la même place que la variable vers laquelle il pointe »

              Oulah, pas du tout ! Un pointeur c’est une adresse mémoire, donc sa taille fait toujours celle d’une donnée capable de contenir n’importe quelle adresse mémoire. L’amiga ayant un bus de données de 24 bits (68.000) ou 32 bits (68.020+), un pointeur occupera 32 bits (un mot long).

              Ce n’est pas du tout compliqué, un pointeur. Il suffit de retenir qu’il contient une adresse mémoire. Quand on utilise un pointeur, on utilise son contenu pour accéder ou modifier ce vers quoi il pointe. S’il contient $dff000 on ira lire ou modifier la donnée située en $dff000.

              Non, passer un pointeur est plus économique, car comme ça tu n’as pas besoin de dupliquer la variable. Imaginons :

              int monEntier;

              int *monPointeur;

              monPointeur = &monEntier;

              int maSuperfonction1(int *pointeur) { … }

              int maSuperfonction2(int *pointeur) {… }

              int maSuperfonction3(int *pointeur) { … }

              main {

                int truc;

                truc = maSuperfonction1(monPointeur) + maSuperfonction2(monPointeur) * maSuperfonction3(monPointeur);

              }

              On a pu passer une référence à la variable monEntier sans avoir à la dupliquer, comme ça aurait été le cas si on s’était passé de pointeurs. Mais les pointeurs ont bien d’autres avantages. Pour mon jeu d’échecs je n’aurais jamais pu me passer de pointeurs (surtout pour les tables de transpositions, qui sont des tables de hashage pouvant faire plusieurs giga-octets).

              Prédateur Chess | Amiga 500 + ACA500 | Amiga 1200 + ACA1233

              Tiki

                #221915

                Merci à vous deux 😀
                Il va bien falloir que je relise deux cents fois vos réponses et que je progresse pour en saisir les nuances. Mais au moins, et à la deuxième lecture, l’intérêt du pointeur apparaît un peu plus clair.

                Tiki ( pointe ou tire )

                Tiki

                  #221916

                  Pardon… A vous tous 😀

                  huggyone76

                    #221919

                    Si on peut aider… 😉

                    A500+ACA500 - A600+Vampire 2+indivision ECS - A1200+Vampire V2 1200 - Mac Mini 1.42 sous MOS - Just CPC 128k - CPC 6128 - Atari STE 4Mo/CosmosEx - Atari Falcon CT60/SuperVidel 🙂
                    C64C + 1541-II + Lecteur K7 + SD - Sharp X68000 CZ-601C 4Mo + CF - Sharp X68000 CZ-611C 10Mo + CF + ext. MIDI

                    b0ris747

                      #221920

                      Quand j’avais commencé le C en autodidacte (16 ans, sur Amiga bien sûr!), j’avais un gros problème avec le pointeur.

                      En fait le pointeur, comme on l’a dit, c’est l’adresse de départ de tout ensemble de donnée, un entier, une structure, ou tout autre type.

                      C’est comme un lien hypertexte, qui ne contient pas le contenu de la page, mais le lien vers la bonne adresse.
                      Du coup, si tu « pointes » une structure contenant beaucoup de données, tu alloues juste l’adresse, et c’est la bibliothéque qui a fait l’allocation mémoire, et t’as pas à « recopier », et donc consommer encore plus de RAM, tu utilises la RAM utilisée par la bibliothéque que tu utilises. C’est économique.

                      Et comme le dit @Joufflu, en C, tu retournes qu’une valeur, donc pour un truc long (chaine de caractéres, tableau, structure…) tu emploies un pointeur. C’est plus économique

                      Tout tableau de valeurs, tout chaine (tableau de char), toute structure (tableau formaté par une définition [typedef]) est caractérisé par l’adresse de départ du tableau de machins, pour on avance de sizeof(la taille du machin définit), élément par élément en fonction de la taille qu’occupe le machin.

                      En fait l’adresse du machin, tu t’en fous, c’est comme si tu regardais un sommaire d’un bouquin et que tu voyais que le chapitre 2 commence à la page 56. Ben ton tableau de caractéres commence à l’adresse 56, et contient une certaine taille (la taille du chapitre) qui représente le nombre de caractéres contenus dans le chapitre.

                      Alors un pointeur peut désigner pleins de choses que j’ai déjà dit, mais aussi un tableau de pointeurs (typiquement argv, représentant une liste de pointeurs de chaines [donc pointeur de pointeurs de char], c’est la liste des arguments passés à une commande en CLI). Tant que tu le stocke en RAM, tu peux y faire référence à moindre coùt en l’appelant par son adresse (donc son pointeur).

                      EDIT (parce que le temps que j’écrive, tout le monde avait rajouté des choses bien): L’autre avantage, c’est que quand tu utilises un pointeur, tu as accés à la variable d’origine, pas à une copie, donc tu peux modifier son contenu et faire d’une variable d’entrée (point de vue algorithmique) une variable d’entrée/sortie (t’as modifié la valeur contenue à cette adresse, et les autres fonctions en tiendront compte).

                      Ex: toto=changeMoi(titi); titi n’est pas modifié, c’est toto qui est modifié.
                      changeMoi(*titi); titi est modifiable par changeMoi(int* nomVariable){nomVariable=nouvelleValeur;}

                      35m2 == 35m3 de matos - Membre de l'ART

                      Gilloo

                        #221928

                        Il n’y a aucune obligation d’utiliser un pointeur en C.
                        Rien ne t’empêche de n’utiliser qu’un main() et des variables et tableaux globaux.
                        Là où cela se complique c’est pour l’utilisation de fonctions.
                        Pratiquement toutes les fonctions du C utilisent des pointeurs en paramètres ou rendent un pointeur. On peut passer des variables de n’importe quel type à une fonction, mais ces variables prennent de la place sur la pile, et comme chaque process n’a qu’une quantité limité de pile (la fameuse stack de 8Ko par défaut de toute icône tool du workbench, ou de 4Ko depuis un CLI) il est plus logique de n’utiliser que des pointeurs pour les structures ou les chaines des fonctions.
                        Il est rare qu’un programme ne comporte qu’un seul fichier source. Il est plus courant de découper son application en plusieurs fichiers, pour gérer chacun une partie de l’application. Ce découpage modulaire implique que chaque module gère ses variables localement. Pour passer d’un module à un autre, le plus simple est d’allouer une structure par le module principal et d’utiliser un pointeur sur cette structure par les autres fonctions des autres modules.

                         

                        mala04

                          #221930

                          Pas d’obligation d’utiliser des pointeurs en C Gilloo??? Faut être un peu BDSM tout de même alors! Lol

                          C’est pas Gainsbourg qui a fait une chanson sur les pointeurs d’ailleurs?!? Ha non pardon, c’était sur des petits trous! Mais bon c’est pareil, y en a partout… Lol

                          Pour le reste, je crois que tout est dit.  🙂

                          huggyone76

                            #221931

                            Il a raison Gilloo, on n’est pas OBLIGES d’utiliser les pointeurs… ce serait juste dommage de s’en passer ! 😉

                            C’est comme écrire en C++ sans utiliser l’objet. Ca marche aussi… C’est juste dommage !

                            Et c’est comme ça pour plein de langages. Les « trucs » qui font gagner du temps, de la place mémoire, de l’efficacité (aussi bien pour le fonctionnement du programme que pour le temps de programmation), c’est ce qui fait la différence entre un bon développeur et un développeur « lambda ». En entreprise si tu fais du simplement traitement de données, tu ne verras parfois pas la différence car ce qui compte c’est le résultat en sortie (tant que 1+1 = 10, tout le monde est content ! Quoi une faute ??? Non non, mon calcul est correct ! 😉 ). Sauf que parfois, ce qui compte, c’est aussi le temps qui passe sur le traitement. Et là déjà, tu vois de sacrées différences ! Et évidemment, il y a ensuite « l’industrie » de la 3D, du jeu vidéo, et tout ce qui touche au calcul ou au « temps réel ». Dans ces cas-là, pas de souci, tu vois tout de suite ceux qui codent bien (et parfois proprement…), et les « autres »… Le problème c’est qu’en DUT, à la fac, etc… faut tomber sur les bons profs qui savent eux-mêmes coder (et là t’as une sacrée veine !)… ou alors apprendre par toi-même…

                            Du coup, en programmation, y a un paquet de mauvais !!! 😉 Moi j’ai choisi… je ne code plus que pour moi-même et ma femme !!! Comme ça, même si c’est mauvais, ça n’emm…de personne ! (et même si j’ai changé de boulot depuis, il parait que mes programmes C et mes shells fonctionnent toujours depuis 15 ans que je suis parti de la boîte où je développais… Je devais pas être si nul finalement… 😀 ).

                            A500+ACA500 - A600+Vampire 2+indivision ECS - A1200+Vampire V2 1200 - Mac Mini 1.42 sous MOS - Just CPC 128k - CPC 6128 - Atari STE 4Mo/CosmosEx - Atari Falcon CT60/SuperVidel 🙂
                            C64C + 1541-II + Lecteur K7 + SD - Sharp X68000 CZ-601C 4Mo + CF - Sharp X68000 CZ-611C 10Mo + CF + ext. MIDI

                            Anonyme

                              #221942

                              On n’est pas obligé d’utiliser des pointeurs, sauf si on tape dans un API existante dont les fonctions demandent des pointeurs comme paramètres.

                              Bon courage pour la suite 🙂

                              thellier

                                #221953

                                Une adresse mémoire est un chiffre
                                Imaginons un ordinateur avec 512k il pourrait avoir des adresses de 0 à 524287 = des chiffres
                                Si tu alloue un bloc mémoire de 100×100 octets cad 10000 ainsi
                                x=allocmem(10000);
                                alors x est une variable , c’est un pointeur qui contient l’adresse de ton bloc de 10000 octets
                                Tu as pu obtenir cette fois un bloc de 10000 octets à l’adresse (par ex.) 200000 donc x=200000
                                mais la prochaine fois que ton prog tournera x vaudra peut être 300000 ou n’importe quelle valeur de 0 à 524287

                                Les adresse changent mais si dans ton prog tu fais
                                x[100]=10;
                                ça marchera toujours

                                C’est là qu’intervient aussi le type de ton pointeur
                                Si toute les pointeurs contiennent fondamentalement la même chose = une adresse en mémoire
                                On peut avoir différents types
                                Ainsi
                                UBYTE* x;
                                UWORD* y;
                                ULONG* z;
                                etc…
                                Leur seule différence est la taille des données sur lesquels il pointent (UBYTE 1 octet, UWORD 2 octets, ULONG 4 octets,etc….)
                                Ainsi meêm si on mettait la même adresse dedans
                                x=y=z=allocmem(10000); qui vaudra peut être 200000 cette fois ci alors

                                x[100]=10; ira écrire en 200100 un UBYTE cad un seul octet
                                y[100]=10; ira écrire en 200200 un UWORD cad deux octets
                                z[100]=10; ira écrire en 200400 un ULONG cad quatres octets

                                l’opérateur ++ va à la case mémoire suivante
                                x++ donc x désormais vaut 200001
                                y++ donc y désormais vaut 200002
                                z++ donc z désormais vaut 200004 etc…
                                Idem avec — vers case précédente

                                On peut avancer de plusieurs cases
                                x=x+100; cad x vaut 200100 désormais
                                y=y+100; cad x vaut 200200 désormais
                                z=z+100; cad x vaut 200400 désormais

                                L’opérateur -> permet de « suivre » le pointeur
                                Par exemple la structure d’un écran AmigaOS contient un pointeur nommé FirstWindow vers la première fenêtre de l’écran
                                Alors NotreEcran->FirstWindow permet d’obtenir cette adresse

                                L’opérateur & donne l’adresse de quelque chose par exemple &(x[100]) vaut 200100

                                L’opérateur * donne le contenu désigné par le pointeur cad que var=*x; est fondamentalement la même chose que var=x[0];
                                Cela sert à lire/écrire dans la zone mémoire désignée par le pointeur var=*x; mais aussi *x=var;

                                frost

                                  #221954

                                  Sinon, c’est tout con, mais j’ai compris ce qu’était un pointeur en apprenant l’assembleur 😉
                                  Et ça permet aussi d’avoir les idées un peu plus au clair sur la syntaxe à la con du C (je ne fais pas de C à longueur de journée, je m’y perds tout le temps).

                                15 sujets de 1 à 15 (sur un total de 44)

                                • Vous devez être connecté pour répondre à ce sujet.

                                Forums AmigaOS, MorphOS et AROS Développement [Débutant en C] L'intérêt du pointeur.

                                Amiga Impact