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 :

Questions d'un novice sur les « infâmes » pointeurs


Sujet :

C

  1. #1
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut Questions d'un novice sur les « infâmes » pointeurs
    Bonsoir,

    J'ai eu quelques cours sur les bases de la programmation en C, où l'on a abordé de manière succincte le thème concernant les pointeurs. Je souhaite en fait enrichir mes connaissances en la matière et je me suis vite rendu compte que les pointeurs, aussi infâmes puissent-ils être, sont en réalité des outils indispensables. Toutefois j'ai beau consulter différentes sources mais quelques zones d'ombre persistent, et je n'aime pas ça. C'est pourquoi je fais appel à votre aide. Avant de poser les questions que me viennent en tête je précise que, à chaque fois que j'aurai une question à propos des pointeurs, je m'amuserai à faire l'archéologue et je déterrerai ce sujet. En revanche, si d'autres novices sont aussi embêtés que moi en ce qui concerne ce thème, ils peuvent bien évidemment poser leurs questions dans ce sujet.

    Assez parlé, voici les questions :

    [1] - Comment choisir le type du pointeur ? Est-ce qu'il doit concorder avec le type de la variable à pointer ? Est-ce que ça dépend du processeur (par exemple, long *pTruc; pour les processeur 32 bits) ? Est-ce que ça dépend d'autre chose ?

    [2] - Comment pointer un tableau multidimensionnel ?

    C'est tout pour aujourd'hui, mais ne soyez pas déçus car il y en aura d'autres.

    Merci d'avance.

    Adishatz !
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  2. #2
    Membre confirmé
    Inscrit en
    Juillet 2005
    Messages
    512
    Détails du profil
    Informations forums :
    Inscription : Juillet 2005
    Messages : 512
    Points : 641
    Points
    641

  3. #3
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Bonsoir,

    Citation Envoyé par VivienD Voir le message
    Assez parlé, voici les questions :

    [1] - Comment choisir le type du pointeur ? Est-ce qu'il doit concorder avec le type de la variable à pointer ? Est-ce que ça dépend du processeur (par exemple, long *pTruc; pour les processeur 32 bits) ? Est-ce que ça dépend d'autre chose ?
    En fait, les pointeurs ne sont pas un concept abstrait propre au C. Il s'agit en fait d'une variable qui contient une adresse en mémoire. Si le concept d'adresse en mémoire ne t'est pas familier, dis-le nous, on commencera par là mais, a priori, tu as l'air de connaître quand même les grandes lignes du fonctionnement de ta machine.

    Ceci posé, on comprend que n'importe quel objet, lorsqu'il est instancié quelque part en mémoire, a donc forcément une adresse et, donc, tu peux pointer n'importe quoi, quelque soit sa taille. Le format du pointeur, lui, va effectivement varier d'une architecture à l'autre mais c'est ton compilateur qui va gérer cela pour toi. À dire vrai, c'est même pour cela que l'on a créé un type « pointeur » : il décrit de manière implicite le format de l'adressage de la machine-cible. Le programmeur n'a donc plus à s'en soucier et son code devient portable.

    [2] - Comment pointer un tableau multidimensionnel ?
    Question pas facile. :-) Surtout pour commencer !

    En fait, on le déclare suivant le même principe qu'un pointeur de fonction. Les pointeurs de tableaux et de fonctions sont ce qu'il y a de plus compliqué dans ce domaine. Je te les montre donc simplement pour répondre à ta question et satisfaire ta curiosité. Il va sans dire que commencer par là n'a rien de pédagogique.

    En gros, tu peux indexer un pointeur comme s'il s'agissait d'un tableau puisque l'on peut aisément y déduire la position d'un élément de rang n : il suffit de partir de l'adresse donnée par le pointeur et d'y ajouter n fois la taille de cet élément.

    D'autre part, tu n'es pas obligé de connaître la taille de ce tableau pour faire ces calculs et le parcourir. C'est nécessaire, en revanche, pour éviter de le dépasser mais ça, c'est un autre problème. Par contre, dans un tableau multidimensionnel, tu peux ignorer la longueur de la première dimension (la plus grande), mais tu dois obligatoirement connaître celle des suivantes puisque ce sont elles qui vont définir le « pas ».

    Tu vas donc faire de même : pour un tableau tridimensionnel, par exemple, tu vas définir un pointeur avec « * », que tu vas ensuite qualifier avec les deux dernières dimensions dûment renseignées. Tu affectes ensuite l'adresse de ton tableau à ce pointeur, et le fait de l'indexer formera naturellement ta dimension principale, comme si tu utilisais le tableau original.

    Ça a l'air très savant dit comme cela, mais ça passe beaucoup mieux avec un exemple :

    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
    #include <stdio.h>
     
    int main (void)
    {
        int i,j,k;
        int (*ptr)[3][4];
        int tab [2][3][4] = { { {  1, 2, 3, 4 } , {  5, 6, 7, 8 } , {  9,10,11,12 } },
                              { { 13,14,15,16 } , { 17,18,19,20 } , { 21,22,23,24 } } };
     
        for (i=0;i<2;++i)
        for (j=0;j<3;++j)
        for (k=0;k<4;++k) printf ("%d %d %d : %d\n",i,j,k,tab[i][j][k]);
     
        ptr = tab;
     
        for (i=0;i<2;++i)
        for (j=0;j<3;++j)
        for (k=0;k<4;++k) printf ("%d %d %d : %d\n",i,j,k,ptr[i][j][k]);
     
        return 0;
    }

  4. #4
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    À Lucien63 : j'avais déjà consulté le lien que tu proposes.

    À Obsidian :
    [1] - Alors, si j'ai bien compris cette affaire-ci, je peux mettre short *pTruc;, int *pTruc;, long *pTruc;, voire long long *pTruc;, quelle que soit l'architecture du processeur, sans que ça n'occasionne de pertes de données.

    [2] - Est-il alors possible de créer un pointeur qui vaudra &tab[0][0][0]...[0], et d'accéder aux autres éléments du tableau multidimensionnel par une simple incrémentation de la valeur du pointeur, comme avec les vecteurs ?
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  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 685
    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 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par VivienD Voir le message
    À Obsidian :
    [1] - Alors, si j'ai bien compris cette affaire-ci, je peux mettre short *pTruc;, int *pTruc;, long *pTruc;, voire long long *pTruc;, quelle que soit l'architecture du processeur, sans que ça n'occasionne de pertes de données.
    Exactement. En fait, en mettant par exemple int *pt, tu indiques au compilateur que l'élément pointé (*pt) est un int. Ainsi, si plus tard tu demandes affiche *pt, le compilo ira à l'adresse pt et récupèrera 4 octets pour avoir la valeur exacte de l'int. Sans ça, il ne saurait pas combien d'octets récupérer. C'est tout...

    Citation Envoyé par VivienD Voir le message
    [2] - Est-il alors possible de créer un pointeur qui vaudra &tab[0][0][0]...[0], et d'accéder aux autres éléments du tableau multidimensionnel par une simple incrémentation de la valeur du pointeur, comme avec les vecteurs ?
    Exactement. Voici un court exemple d'affichage de tableau
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int tab[10]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int i;
    int *pt;
     
    // Affichage des éléments par leur indice
    for (i=0; i < 10; i++)
        printf("L'élément %d vaut %d\n", i, tab[i]);
     
    // Affichage des éléments en passant par un pointeur contenant leur adresse successive
    for (i=0, pt=tab; i < 10; i++, pt++)
        printf("L'élément %d vaut %d\n", i, *pt);

    La seconde boucle est un poil plus rapide car chaque fois que tu invoques tab[i], le compilo se place au début du tableau pour décaler ensuite de i positions. Alors que dans la seconde boucle, j'ai simplement stocké l'adresse du début que je décale. Bien sûr pour un simple tableau d'entiers c'est pas très flagrant mais cela commence à être intéressant quand on doit parcourir des tableaux de structures.

    Toutefois, un petit bémol: il est facile et instantané de passer d'un tableau 1D vers un pointeur, cela devient un peu plus difficile dans le cas de tableaux multi dimensionnels. C'est dû au fait que la mémoire, elle, reste sous forme de liste d'adresses en 1 dimension. Donc pour l'instant, tant que t'es pas trop à l'aise, il vaut mieux faire tes tests avec des tableaux 1D...
    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
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par VivienD Voir le message
    À Obsidian :
    [1] - Alors, si j'ai bien compris cette affaire-ci, je peux mettre short *pTruc;, int *pTruc;, long *pTruc;, voire long long *pTruc;, quelle que soit l'architecture du processeur, sans que ça n'occasionne de pertes de données.
    Absolument.

    Mais il est quand même important de souligner que ce n'est pas le pointeur qui va contenir la donnée dont tu énumères les types ici. Ton pointeur ne contient qu'une indication de l'emplacement de la donnée pointée dans la mémoire. Dès lors, tu peux pointer n'importe quoi, que ta donnée tienne en un seul octet ou qu'elle en occupe mille.

    Dans tout les cas, l'adresse délivrée par le pointeur est celle du premier octet de ta donnée (celui dont l'adresse est la plus petite, donc).

    À noter également qu'au départ, un pointeur ne contient aucune adresse valide. C'est à toi de l'initialiser avec l'adresse de quelque chose qui existe déjà.

    [2] - Est-il alors possible de créer un pointeur qui vaudra &tab[0][0][0]...[0], et d'accéder aux autres éléments du tableau multidimensionnel par une simple incrémentation de la valeur du pointeur, comme avec les vecteurs ?
    Il n'y a pas de « vecteur » en C. Ça dépend donc de ce que tu entends par ce mot (comprendre : au sens mathématique ou au sens « C++ ») mais oui, tu peux incrémenter le pointeur, soit avec les opérateurs arithmétiques (ex : « ptr + 2 »), soit avec les opérateurs de pré/post in/dé-crémentation, tels que « ptr++ ».

    Encore une fois, sur un tableau tridimensionnel, c'est un peu délicat, mais sur tous les tableaux à une seule dimension — et spécialement les chaînes de caractères — c'est très largement employé.

    Dans le cas d'un tableau multidimensionnel, ton pointeur représente la plus grande dimension. Chaque « élément » pointé est alors en fait un sous-tableau privé de la première dimension. Cela forme donc une expression de type « tableau », que tu exploites comme un tableau ordinaire.

    Essaie de compiler et d'exécuter le programme que je t'ai donné en exemple. Tu peux aussi essayer d'y ajouter les lignes suivantes, juste avant l'instruction « return » :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        printf ("%d\n",(*ptr)[0][0]);
        ptr++;
        printf ("%d\n",(*ptr)[0][0]);

  7. #7
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    [1] - Maintenant j'ai exactement compris comment il faut dimensionner mes pointeurs. Merci.

    [2] -
    Citation Envoyé par Sve@r Voir le message
    Exactement. Voici un court exemple d'affichage de tableau
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int tab[10]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int i;
    int *pt;
     
    // Affichage des éléments par leur indice
    for (i=0; i < 10; i++)
        printf("L'élément %d vaut %d\n", i, tab[i]);
     
    // Affichage des éléments en passant par un pointeur contenant leur adresse successive
    for (i=0, pt=tab; i < 10; i++, pt++)
        printf("L'élément %d vaut %d\n", i, *pt);

    La seconde boucle est un poil plus rapide car chaque fois que tu invoques tab[i], le compilo se place au début du tableau pour décaler ensuite de i positions. Alors que dans la seconde boucle, j'ai simplement stocké l'adresse du début que je décale. Bien sûr pour un simple tableau d'entiers c'est pas très flagrant mais cela commence à être intéressant quand on doit parcourir des tableaux de structures.

    Toutefois, un petit bémol: il est facile et instantané de passer d'un tableau 1D vers un pointeur, cela devient un peu plus difficile dans le cas de tableaux multi dimensionnels. C'est dû au fait que la mémoire, elle, reste sous forme de liste d'adresses en 1 dimension. Donc pour l'instant, tant que t'es pas trop à l'aise, il vaut mieux faire tes tests avec des tableaux 1D...
    Citation Envoyé par Obsidian Voir le message
    Il n'y a pas de « vecteur » en C. Ça dépend donc de ce que tu entends par ce mot (comprendre : au sens mathématique ou au sens « C++ ») mais oui, tu peux incrémenter le pointeur, soit avec les opérateurs arithmétiques (ex : « ptr + 2 »), soit avec les opérateurs de pré/post in/dé-crémentation, tels que « ptr++ ».

    Encore une fois, sur un tableau tridimensionnel, c'est un peu délicat, mais sur tous les tableaux à une seule dimension — et spécialement les chaînes de caractères — c'est très largement employé.

    Dans le cas d'un tableau multidimensionnel, ton pointeur représente la plus grande dimension. Chaque « élément » pointé est alors en fait un sous-tableau privé de la première dimension. Cela forme donc une expression de type « tableau », que tu exploites comme un tableau ordinaire.

    Essaie de compiler et d'exécuter le programme que je t'ai donné en exemple. Tu peux aussi essayer d'y ajouter les lignes suivantes, juste avant l'instruction « return » :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        printf ("%d\n",(*ptr)[0][0]);
        ptr++;
        printf ("%d\n",(*ptr)[0][0]);
    J'ai la (mauvaise) habitude de nommer « vecteur » toute matrice et tout tableau n'ayant qu'une dimension.
    En revanche, je vais étudier la question posément et en attendant de maîtriser ce point j'utiliserai des tableaux unidimensionnels (je me demande si c'est bien français, ce que je dis là). En tout cas, merci encore.

    J'allais dire « à plus pour d'autres questions » mais deux questions me viennent à l'esprit.

    [3] - Qu'est-ce qu'un pointeur de fonction ? À quoi est-ce que ça sert concrètement ?

    [4] - Quelle(s) particularité(s) a le pointeur **pMachin ?
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  8. #8
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    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 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par VivienD Voir le message
    [3] - Qu'est-ce qu'un pointeur de fonction ?
    Une fonction a aussi une adresse. L'adresse de la zone mémoire où elle commence. On peut donc stocker cette adresse dans un pointeur. Et ensuite, on peut l'appeler en passant par le pointeur.

    Citation Envoyé par VivienD Voir le message
    À quoi est-ce que ça sert concrètement ?
    Ca sert chaque fois que tu veux faire un traitement automatisé mais pas forcément connu au moment où tu le programmes (ça peut arriver)

    Par exemple il existe dans la librairie une fonction universelle "qsort()" permettant de trier un tableau de n'importe quoi (selon les désirs de celui qui utilise cette fonction). Mais "trier" quelque chose implique de savoir comment comparer deux "quelque chose". Et celui qui a écrit qsort() ne savait évidemment pas ce que celui qui va l'utiliser va vouloir trier. Donc il ne pouvait pas écrire le code permettant de comparer puisqu'il ne sait pas ce qui sera à trier.
    Donc c'est à l'utilisateur qui désire utiliser cette fonction (et qui sait donc ce qu'il manipule et comment les comparer) de le faire. Il n'a qu'à écrire une fonction permettant de comparer 2 éléments selon une directive simple
    - la fonction doit être prévue pour recevoir 2 adresses (les adresses des choses à comparer)
    - la fonction doit renvoyer -1 si l'élément 1 est plus petit que l'élément 2, 1 s'il est plus grand ou 0 s'ils sont égaux.
    Ensuite, l'utilisateur n'a plus qu'à passer l'adresse de cette fonction à la fonction qsort() qui (ayant stocké cette adresse dans un pointeur à elle) pourra l'appeler et saura ainsi comment comparer 2 éléments et pourra faire le tri du tableau.

    Autre exemple: on te demande de programmer un traitement encore inconnu (et pouvant changer ensuite) sur un tableau de nombres. Chaque nombre du tableau devra être affiché puis ensuite traité selon le bon vouloir de l'utilisateur et le résultat du traitement devra être affiché.
    Ben tu fais ça avec un pointeur de fonction

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Le moteur de travail - Reçoit l'adresse du tableau, le nombre d'éléments et un pointeur sur la fonction de traitement (cette fonction recevant elle-même un int et renvoyant aussi un int)
    void moteur (int *tab, int nb, int (*ptfct)(int))
    {
         int i;
     
        // Balayage du tableau
        for (i=0; i < nb; i++)
        {
             // Affichage de l'élément et du résultat du calcul
             printf("Le calcul de %d donne %d\n", tab[i], (*ptfct)(tab[i]));
        }
    }

    Maintenant que le moteur est fait, si tu veux le carré des 10 premiers nombres pairs
    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
    // Il faut d'abord définir comment calculer le carré d'un nombre
    int carre(int n)
    {
        return n * n;
    }
     
    int main()
    {
         // Le tableau de nombres
         int tab[10]={2, 4, 6, 8, 10, 12, 14, 16, 18, 20};
     
         // Le traitement
         moteur(tab, 10, carre);
    }

    Ou alors si tu veux le cube des 15 premiers nombres impairs
    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
    // Il faut d'abord définir comment calculer le cube d'un nombre
    int cube(int n)
    {
        return n * n * n;
    }
     
    int main()
    {
         // Le tableau de nombres
         int tab[15]={1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29};
     
         // Le traitement (toujours le même)
         moteur(tab, 15, cube);
    }

    Comme tu vois, tu n'as pas besoin de toucher au moteur pour faire des traitements divers...

    Citation Envoyé par VivienD Voir le message
    [4] - Quelle(s) particularité(s) a le pointeur **pMachin ?
    Il faut un type devant **. On va dire "int **pMachin". Ben c'est un pointeur contenant l'adresse d'un pointeur contenant lui-même l'adresse d'un int...
    Exemple
    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
    int var;
    int *pt_simple;
    int **pt_double;
     
    pt_simple=&var;
    pt_double=&pt_simple;
     
    var=123;
    printf("La valeur au bout du pt_double vaut %d\n", **pt_double);
    printf("La valeur au bout du pt_simple vaut %d\n", *pt_simple);
    printf("La valeur de la variable vaut %d\n", var);
     
    var=456;
    printf("La valeur au bout du pt_double vaut %d\n", **pt_double);
    printf("La valeur au bout du pt_simple vaut %d\n", *pt_simple);
    printf("La valeur de la variable vaut %d\n", var);
    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]

  9. #9
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    [3] - D'accord, Sve@r, je comprends ce que c'est et en quoi ça peut être utile. Toutefois, j'ai remarqué dans ton exemple que tu as le nom de la fonction en tant que pointeur de la dite fonction ; est-ce que le nom, seul, d'une fonction est en réalité un pointeur ?
    Par ailleurs tu définis la fonction void moteur comme ci-dessous :
    Citation Envoyé par Sve@r Voir le message
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    void moteur (int *tab, int nb, int (*ptfct)(int))
    Est-ce que int (*ptfct)(int) veut dire que *ptfct pointe sur une fonction qui admet un argument du type int et qui renvoie une valeur du même type ?

    [4] - Alors si j'ai bien compris ton exemple, int **pt_double y pointe int var par l'intermédiaire de int *pt_simple.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Bonsoir,

    Citation Envoyé par VivienD Voir le message
    [3] - D'accord, Sve@r, je comprends ce que c'est et en quoi ça peut être utile.
    On peut citer plusieurs cas :

    • D'abord, le code de ta fonction se trouve bien quelque part en mémoire et tu dois pouvoir savoir où. Il est donc nécessaire de pouvoir retrouver le point d'entrée de ta fonction, ne serait-ce que pour pouvoir l'appeler, ou le transmettre à d'autres langages ;
    • L'exemple que t'a donné sve@r est une fonction de rappel ou call back. 99% des pointeurs de fonction sont utilisés à cela. Il s'agit de passer un pointeur sur une de tes fonctions à une procédure qui, elle, la rappelle dans le cadre de son processus.

      L'exemple le plus démonstratif est celui de qsort(), qui trie un ensemble d'éléments. L'algorithme utilisé n'est pas précisé par la norme mais le nom de la fonction laisse à penser qu'il s'agissait, au moins au départ, de quick sort. Or, trier des listes est un problème fondamental en informatique, mais il s'agit là d'un travail purement algorithmique. La seule donnée « externe » qu'a besoin de connaître la fonction est le fait qu'un élément donné soit ou non inférieur à un autre. Ainsi, si tu transmets à qsort() l'adresse d'une fonction qu'elle puisse appeler et qui peut comparer deux éléments, tu peux trier virtuellement n'importe quoi.
    • Sur le plan formel, tu ne peux pas passer une fonction en argument par recopie, comme tu le ferais avec un entier (ce qui reviendrait à passer une copier de l'intégralité de son code, ce qui serait complètement inutile) ;
    • Le fait de disposer d'un pointeur te permet de le stocker dans un tableau. Tu peux ainsi faire un tableau de structure dont chaque entrée contient une clé et une procédure associée. Par exemple, si tu fais une calculatrice, tu peux directement la procédure à appeler en vis à vis de l'opérateur, sans avoir à passer par un gros switch :
      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
       
      struct { char c; int (*procedure)(int,int); } array[] = 
      {
          { '+', Addition },
          { '-', Soustraction },
          { '*', Multiplication },
          { '/', Division },
      };
       
      int Addition (int a,int b)
      {
          return a+b;
      }    
       
      int Soustraction (int a,int b)
      {
          return a-b;
      }
    • Un tel tableau a l'avantage d'être modifiable dynamiquement et à l'exécutable, contrairement à un switch codé en dur ;
    • Le C, dans l'absolu, est conçu pour te permettre de générer des exécutables autonomes sur n'importe quelle architecture. Tu dois donc pouvoir charger ton code toi-même, depuis un fichier, voire même l'auto-générer. Et donc l'appeler juste derrière ;
    • Ça devient nécessaire lorsque tu utilises dlopen(), dlsym(), etc. Ces fonctions sont faites pour charger les ressources d'une bibliothèque dynamique pendant l'exécution. Elles doivent donc pouvoir te renvoyer un pointeur vers une fonction fraîchement chargée, que ce soit pour l'exécuter, ou simplement pour en lire le contenu ;
    • Enfin, si tu peux passer un pointeur de fonction en paramètre, tu peux également en renvoyer un en sortie d'une fonction. Ceci t'ouvre donc les portes des fonctions anonymes et du lambda-calcul.


    Toutefois, j'ai remarqué dans ton exemple que tu as le nom de la fonction en tant que pointeur de la dite fonction ; est-ce que le nom, seul, d'une fonction est en réalité un pointeur ?
    Oui.

    Plus précisément, invoquer le nom seul d'une fonction te renvoie son adresse, ce qui est une valeur de type « pointeur de fonction ». Note que c'est également ce qui se passe lorsque tu invoques le nom d'un tableau.

    Par ailleurs tu définis la fonction void moteur comme ci-dessous :

    Est-ce que int (*ptfct)(int) veut dire que *ptfct pointe sur une fonction qui admet un argument du type int et qui renvoie une valeur du même type ?
    C'est cela.

    [4] - Alors si j'ai bien compris ton exemple, int **pt_double y pointe int var par l'intermédiaire de int *pt_simple.
    Également.

    À noter que cela s'appelle une « indirection » et que tu peux en ajouter autant que tu veux.

  11. #11
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    [3] -
    Citation Envoyé par Obsidian Voir le message
    • Le fait de disposer d'un pointeur te permet de le stocker dans un tableau. Tu peux ainsi faire un tableau de structure dont chaque entrée contient une clé et une procédure associée. Par exemple, si tu fais une calculatrice, tu peux directement la procédure à appeler en vis à vis de l'opérateur, sans avoir à passer par un gros switch :
      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
       
      struct { char c; int (*procedure)(int,int); } array[] = 
      {
          { '+', Addition },
          { '-', Soustraction },
          { '*', Multiplication },
          { '/', Division },
      };
       
      int Addition (int a,int b)
      {
          return a+b;
      }    
       
      int Soustraction (int a,int b)
      {
          return a-b;
      }
    • Un tel tableau a l'avantage d'être modifiable dynamiquement et à l'exécutable, contrairement à un switch codé en dur ;
    C'est un point (très !) intéressant. J'aimerais bien qu'on me l'explique avec force détails.

    [4] -
    Citation Envoyé par Obsidian Voir le message
    À noter que cela s'appelle une « indirection » et que tu peux en ajouter autant que tu veux.
    Qu'est-ce que tu entends par « en ajouter autant que tu veux » ?
    D'autre part, dans mes recherches préalables, j'ai lu qu'un double pointeur peut servir avec les vecteurs de pointeurs simples. Info ou intox ?
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  12. #12
    Expert éminent sénior
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Points : 13 926
    Points
    13 926
    Par défaut
    D'autre part, dans mes recherches préalables, j'ai lu qu'un double pointeur peut servir avec les vecteurs de pointeurs simples. Info ou intox ?
    Info. C'est sans doute dans cette situation qu'on rencontrera le plus souvent des "double" pointeurs.

    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void fonc(int ** p)
    {
        ....
    }
    /*---------------------------------*/
      ....
      int * tab[....];   // un tableau de pointeur
      ....
      fonc(tab);
      ....
    Mais ce n'est pas obligatoire. Par exemple (avec la même fonction fonc()) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      int * p ;   // un pointeur
      ....
      fonc(&p); // on passe l'adresse du pointeur
      ....
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    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 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par VivienD Voir le message
    [3] -

    C'est un point (très !) intéressant. J'aimerais bien qu'on me l'explique avec force détails.
    Ok. Je reprends mon exemple initial (le tableau) et je le mixe avec l'exemple d'Obsidian. Donc on te donne un tableau de nombres. Et on te dit qu'il va falloir y effectuer, pour chaque nombre, une suite de traitements encore inconnus (et évolutifs).
    Donc tu te dit qu'un traitement c'est quoi ? C'est un libellé (pour l'affichage) et une action. Tu as donc ta structure de base
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef struct {
    	char *libelle;
    	int (*ptfct)(int);
    } t_traitement;

    Ensuite, tu crées les traitements sur ce que tu connais. On te demande au départ carre et cube. Tu as donc tes fonctions
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int carre(int n)
    {
    	return n*n;
    }
     
    int cube(int n)
    {
    	return n*n*n;
    }

    Maintenant, tu définis ton moteur de travail. Ce moteur recevra bien évidemment le tableau de nombres et le nombre d'éléments du tableau. Et le moteur aura, lui, la liste des traitements à accomplir.
    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
    void moteur(int *tab, int n)
    {
    	int i, j;
     
    	t_traitement action[]={
    		{"carré", carre},
    		{"cube", cube},
    	};
     
    	// Boucle sur chaque élément du tableau
    	for (i=0; i < n; i++)
    	{
    		// Boucle sur chaque traitement
    		for (j=0; j < 2; j++)
    		{
    			// L'élément i du tableau passe par le traitement j
    			printf("Le %s de tab[%d]=%d\n", action[j].libelle, tab[i], (*action[j].ptfct)(tab[i]));
    		}
     
    		// Petit saut de ligne pour aérer l'affichage
    		printf("\n");
    	}
    }

    Et maintenant le petit test
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #include <stdio.h>
    int main()
    {
    	int tab[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    	moteur(tab, 10);
    }

    Le tout dans un toto.c (avec le #include au début) et ça fonctionne. Mais c'est pas encore vraiment optimisé. Parce que, dans le moteur, la boucle j va de 0 à 2 (puisqu'il y a 2 traitements). Mais si tu en rajoutes un 3°, il faudra modifier l'indice de fin. Pas super tiptop (oubli possible).

    Pour éviter ce pb, on va donc s'arranger pour que le tableau de traitements sache lui-même où s'arrêter. Pour ça, on lui fait emporter en plus un élément lui indiquant qu'il est fini. Cet élément ce sera un pointeur NULL (ou plutôt autant de pointeurs que de membres dans la structure).

    Donc le tableau "action" devient maintenant ceci
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    t_traitement action[]={
    	{"carré", carre},
    	{"cube", cube},
    	{NULL, NULL},
    };

    Et dans le moteur, au lieu de boucler de 0 à 2, on fera évoluer un pointeur et on exécutera le traitement tant qu'on n'est pas arrivé sur le NULL final
    Ce qui donne ceci
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    t_traitement *pt;
    // Boucle sur chaque traitement
    for (pt=action; pt->ptfct != NULL; pt++)
    	printf("Le %s de tab[%d]=%d\n", pt->libelle, tab[i], (*pt->ptfct)(tab[i]));

    Et le programme complet
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <stdio.h>
     
    typedef struct {
    	char *libelle;
    	int (*ptfct)(int);
    } t_traitement;
     
    int carre(int n)
    {
    	return n*n;
    }
     
    int cube(int n)
    {
    	return n*n*n;
    }
     
    void moteur(int *tab, int n)
    {
    	int i;
     
    	t_traitement action[]={
    		{"carré", carre},
    		{"cube", cube},
    		{NULL, NULL},
    	};
    	t_traitement *pt;
     
    	// Boucle sur chaque élément du tableau
    	for (i=0; i < n; i++)
    	{
    		// Boucle sur chaque traitement
    		for (pt=action; pt->ptfct != NULL; pt++)
    		{
    			// L'élément i du tableau passe par le traitement "pt"
    			printf("Le %s de tab[%d]=%d\n", pt->libelle, tab[i], (*pt->ptfct)(tab[i]));
    		}
     
    		// Petit saut de ligne pour aérer l'affichage
    		printf("\n");
    	}
    }
     
    int main()
    {
    	int tab[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    	moteur(tab, 10);
    }

    Et voilà. Maintenant on te demande de rajouter le traitement "gloubiboulga" qui, pour chaque nombre, multiplie le nombre par le nombre suivant

    Tu rajoutes simplement la fonction elle-même
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int gloubiboulga(int n)
    {
    	return n*(n+1);
    }

    Et tu complètes le tableau des traitements
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    t_traitement action[]={
    	{"carré", carre},
    	{"cube", cube},
    	{"gloubiboulga", gloubiboulga},
    	{NULL, NULL},
    };
    Et tu recompiles...

    Citation Envoyé par VivienD Voir le message
    Qu'est-ce que tu entends par « en ajouter autant que tu veux » ?
    Ben autant que tu as besoin. Tu veux gérer un rubik's cube te faudra gérer la longueur, la largeur et la profondeur. Tu auras donc un tableau en 3D que tu pourras traiter par un <type> ***pt. Tu veux faire des expérimentations dans la théorie des cordes (donc avec 7 dimensions) tu pourras les gérer avec des <type> *******pt. La seule limite est celle que ton cerveau supporte. Mais généralement, on évite de dépasser 2D. Au delà, on remplace les dimensions par des structures qui en intègrent d'autres. Ainsi chaque niveau n'a qu'une étoile qui permet d'accéder au niveau suivant...

    Citation Envoyé par VivienD Voir le message
    D'autre part, dans mes recherches préalables, j'ai lu qu'un double pointeur peut servir avec les vecteurs de pointeurs simples. Info ou intox ?
    Pas de pb
    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
    #include <stdio.h>
     
    int main()
    {
    	char *semaine[]={
    		"lundi",
    		"mardi",
    		"mercredi",
    		"jeudi",
    		"vendredi",
    		"samedi",
    		"dimanche",
    		NULL,
    	};
    	char **ptD;
    	int i;
     
    	// Affichage
    	for (ptD=semaine, i=1; *ptD != NULL; ptD++, i++)
    	{
    		printf("Le jour %d de la semaine est %s\n", i, *ptD);
    	}
    }
    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]

  14. #14
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par VivienD Voir le message
    [3] - C'est un point (très !) intéressant. J'aimerais bien qu'on me l'explique avec force détails.
    Tu as un pointeur qui est réputé pointer une fonction. Il faut donc que tu puisses l'appeler, cette fonction. Cela se fait en ajoutant les paramètres à lui passer, entre parenthèses, à la suite.

    Voici le programme ci-dessus au complet :

    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
    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
    #include <stdio.h>
     
    int addition (int a,int b)
    {
        return a+b;
    }
     
    int soustraction (int a,int b)
    {
        return a-b;
    }
     
    int multiplication (int a,int b)
    {
        return a*b;
    }
     
    int division (int a,int b)
    {
        return a/b;
    }
     
    int erreur (int a,int b)
    {
        fprintf (stderr,"Erreur : opérateur inconnu (paramètres : %d et %d)\n",a,b);
     
        return 0;
    }
     
    int main (void)
    {
        char buffer [256];
        struct { char operateur; int(*procedure)(int,int); } *ptr, tableau[] =
        {
            {   '+', addition       },
            {   '-', soustraction   },
            {   '*', multiplication },
            {   '/', division       },
            {   0,   erreur         }
        };
     
        int a,b;
        char c;
     
     
        printf ("Veuillez saisir un entier, un opérateur et un autre entier séparés par des espaces, puis valider avec Entrée.\n");
     
        while (fgets(buffer,sizeof buffer,stdin)!=NULL)
        {
            ptr = tableau;
            c   = 0;
     
            if (sscanf (buffer,"%d %c %d",&a,&c,&b)==3)
            {
                while (ptr->operateur != 0 && *ptr->operateur != c) ptr++;
                printf ("Résultat : %d.\n",(ptr->procedure)(a,b));
            }
            else fprintf (stderr,"Erreur de saisie.\n");
        }
     
        return 0;
    }

    Une fois compilé, ce programme te donne le résultat d'expressions telles que « 5 * 9 », « 12 / 4 », « 3 * 2 », etc. Maintenant, si j'ai envie de l'étendre, par exemple pour prendre en charge « % », soit pour calculer un vrai pourcentage, soit pour me renvoyer le modulo comme le fait le C, il suffit d'écrire la fonction, d'une part :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int modulo (int a,int b)
    {
        return a % b;
    }

    et d'insérer la ligne suivante dans le tableau juste avant la dernière, qui sert de sentinelle :


    Recompiles le programme et il prendra automatiquement en charge cette nouvelle fonctionnalité. Note également que ce tableau de procédure peut lui-même t'être renvoyé à l'exécution par une fonction, en fonction des droits et privilèges de l'utilisateur, par exemple.

    C'est génial, le C. Tu ne trouves pas ? :-)

    Qu'est-ce que tu entends par « en ajouter autant que tu veux » ?
    Un pointeur est, encore une fois, l'adresse mémoire de quelque chose. C'est pour cela que cela s'appelle « pointeur ». C'est une entité qui « pointe » un objet en mémoire. Donc, ce pointeur peut très bien pointer… un autre pointeur ! Ce pointeur, à son tour, peut très bien pointer un objet de type ordinaire ou… encore un autre pointeur, etc. Tu rajoutes donc autant d'étoiles que nécessaires jusqu'à arriver au bout de l'enfilade. Ex : « int ******** ptr ».

    Sais-tu bien ce qu'est une adresse mémoire ou ce concept est-il abstrait pour toi ? (C'est une vraie question, pas un jugement de valeur).

    D'autre part, dans mes recherches préalables, j'ai lu qu'un double pointeur peut servir avec les vecteurs de pointeurs simples. Info ou intox ?
    Info.
    L'exemple le plus simple est un tableau de chaînes. Imaginons que je veuille traduire un chiffre numérique en sa forme littérale :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
     
    int main (void)
    {
        const char * chiffres[] = { "Zero", "Un", "Deux", "Trois", "Quatre", "Cinq", "Six", "Sept", "Huit", "Neuf" };
     
        printf ("%s\n",chiffre[5]);
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ ./chiffre
    Cinq

    Un chaîne de caractères est en fait un tableau de caractère : une suite consécutive de caractères du même type en mémoire. Pour passer cette chaîne, on passe en fait un pointeur vers le début de la chaîne, soit le premier caractère. C'est pour cela que les chaînes de caractères sont de type « char * » en C.

    Maintenant, ici, j'ai un tableau de chaînes : c'est en fait une liste de pointeurs « char * », donc un tableau. Je peux donc parcourir ce tableau avec un pointeur, de la même façon que je parcours mes chaînes. Étant donné que chaque entrée est un « char * », ce pointeur est donc un « pointeur vers un char * », donc un « char ** ».

    Et si tu peux monter d'un degré de la sorte, tu peux continuer. On peut imaginer que tu veuilles représenter ces chiffres dans toutes les langues du monde, par exemple. Dans ce cas, tu vas définir n tableaux de dix chaînes, un par langue, et tu vas les réunir sous une « super-liste » qui te permettra de récupérer la bonne avec l'index de la langue choisie, puis l'entrée idoine avec celui du chiffre.

    En ce sens c'est donc un tableau (de chaînes) à deux dimensions que tu exploites. Donc « char * tab [][] ». Et si tu passes ce tableau via un pointeur, c'est donc « char *** ptr ».

    On en est à trois étoiles. Tu vois que cela monte vite. En général, on ne dépasse jamais ce niveau, mais rien ne t'empêche de continuer.

  15. #15
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Hum, apparemment nous avons été plusieurs à prendre plusieurs heures pour rédiger un message complet et, du coup, on fait tous double emploi. :-)

    Tant mieux, on pourra marquer cette discussion pour la re-sortir en référence, plus tard.

  16. #16
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    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 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Hum, apparemment nous avons été plusieurs à prendre plusieurs heures pour rédiger un message complet et, du coup, on fait tous double emploi. :-)
    Ouais mais si on l'a fait, c'est parce qu'on aime ça...

    Et puis nos exemples sont différents donc peuvent se compléter dans la compréhension du pt de fonction

    Citation Envoyé par Obsidian Voir le message
    L'exemple le plus simple est un tableau de chaînes. Imaginons que je veuille traduire un chiffre numérique en sa forme littérale :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
     
    int main (void)
    {
        const char * chiffres[] = { "Zero", "Un", "Deux", "Trois", "Quatre", "Cinq", "Six", "Sept", "Huit", "Neuf" };
     
        printf ("%s\n",chiffre[5]);
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ ./chiffre
    Cinq
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
     
    int main (int argc, char *argv[])
    {
    	const char * chiffres[] = { "Zero", "Un", "Deux", "Trois", "Quatre", "Cinq", "Six", "Sept", "Huit", "Neuf" };
    	char **ptArg;
     
    	for (ptArg=argv + 1; *ptArg; ptArg++)
    		printf ("%s\n",chiffres[atoi(*ptArg)]);
     
       	return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $ ./chiffre 1 3 5 2 4
    Un
    Trois
    Cinq
    Deux
    Quatre
    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]

  17. #17
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Merci pour votre assiduité. Le fait que vos messages fassent «doublons» n'est pas dérangeant en soi, car vous expliquez de différentes manières une même notion ce qui offre une meilleure compréhension de ce thème.
    Malheureusement, comme je suis en période d'examens, je ne peux pas être aussi assidu que vous sur ce sujet et je vais devoir attendre un peu pour pouvoir étudier calmement vos réponses, tester vos codes et les modifier à ma guise (histoire de voir si j'ai vraiment bien compris la chose).

    Obsidian, tu m'as demandé si je savais ce que c'est qu'une adresse-mémoire. Alors je te réponds que, oui, je sais ce que c'est, vu que j'ai eu à faire avec eux en cours de Mikroprozessortechnik (je ne sais pas comment traduire ce nom, désolé) et de programmation de micro-contrôleur en langage assembleur.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  18. #18
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    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 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par VivienD Voir le message
    Merci pour votre assiduité. Le fait que vos messages fassent «doublons» n'est pas dérangeant en soi, car vous expliquez de différentes manières une même notion ce qui offre une meilleure compréhension de ce thème.
    Malheureusement, comme je suis en période d'examens, je ne peux pas être aussi assidu que vous sur ce sujet et je vais devoir attendre un peu pour pouvoir étudier calmement vos réponses, tester vos codes et les modifier à ma guise (histoire de voir si j'ai vraiment bien compris la chose).
    Pas grave. C'est ça l'avantage d'un forum => on n'est pas obligé d'être là en permanence.

    Citation Envoyé par VivienD Voir le message
    Mikroprozessortechnik (je ne sais pas comment traduire ce nom, désolé)
    Je dirais "technologie de microprocesseur". En tout cas tu as une orthographe irréprochable. Beaucoup d'intervenants francophones devraient prendre exemple sur toi...
    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]

  19. #19
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    [...]
    En tout cas tu as une orthographe irréprochable. Beaucoup d'intervenants francophones devraient prendre exemple sur toi...
    Je suis perfectionniste en ce qui concerne mon orthographe, et ce, aussi bien en français, qu'en anglais ou en allemand.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  20. #20
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par VivienD Voir le message
    Malheureusement, comme je suis en période d'examens, je ne peux pas être aussi assidu que vous sur ce sujet et je vais devoir attendre un peu pour pouvoir étudier calmement vos réponses, tester vos codes et les modifier à ma guise (histoire de voir si j'ai vraiment bien compris la chose).
    Tu le sais probablement déjà, mais dans le menu « Outils de la discussion » puis « S'abonner à cette discussion », tu peux demander à être prévenu par e-mail lorsqu'il y a du changement sur ce fil.

    Citation Envoyé par VivienD Voir le message
    Je suis perfectionniste en ce qui concerne mon orthographe, et ce, aussi bien en français, qu'en anglais ou en allemand.
    C'est une vertu qu'on aimerait rencontrer plus souvent (spécialement ces derniers temps).

Discussions similaires

  1. Réponses: 4
    Dernier message: 31/01/2012, 17h36
  2. Question d'ordre général sur les macros sur excel
    Par tzehani dans le forum Macros et VBA Excel
    Réponses: 14
    Dernier message: 29/08/2007, 05h16
  3. [Portlet] Questions d'ordre général sur les portlets
    Par Chabin dans le forum Portails
    Réponses: 1
    Dernier message: 25/06/2007, 23h20
  4. Questions d'ordre general sur les design CSS
    Par Clorish dans le forum Mise en page CSS
    Réponses: 20
    Dernier message: 19/06/2007, 13h20
  5. question (peut-être idiote) sur les vues
    Par LadyArwen dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 26/03/2003, 10h35

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