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 :

Forme (*tableau)[indice] - Comprendre


Sujet :

C

  1. #1
    Pgs
    Pgs est déconnecté
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    482
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 482
    Par défaut Forme (*tableau)[indice] - Comprendre
    Bonjour,

    Voici mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    typedef int* TabInt;
    TabInt tabintok;
    tabintok= (TabInt)calloc(10, sizeof(int));
    Si je passe ensuite &tabintok en paramètre d'une fonction, cette dernière ne peut pas le modifier avec *tabintok[indice] mais peut le faire avec Je ne comprends pas pourquoi *tabintok[indice] ne fonctionne pas et surtout je ne comprends pas la forme (*tabintok)[indice].

    Pouvez-vous m'expliquer ?

    Merci

  2. #2
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Octobre 2019
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : .

    Informations forums :
    Inscription : Octobre 2019
    Messages : 21
    Par défaut
    Comme expliqué ici, la précédence des opérateurs est définie de telle sorte que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    *tabintok[indice] = *(tabintok[indice])
    puisque la précédence de l'opérateur [] est supérieur à celle de *. Donc, afin de changer la précédence, on doit mettre des parenthèses comme on le fait de même dans les expressions arithmétiques.

    Mais si tu veux modifier les éléments de tabintok, passer tabintok et non &tabintok est suffisant. Seulement si l'on veut modifier la variable elle-même on passe un pointeur.

    En plus, la conversion explicite entre void* et d'autres types pointeur est redondante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tabintok= calloc(10, sizeof(int));
    fonctionne très bien.

  3. #3
    Pgs
    Pgs est déconnecté
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    482
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 482
    Par défaut
    Bonjour et merci pour cette réponse claire et rapide.
    Pour être certain d'avoir bien compris, peux-tu me confirmer que ne fonctionne pas car, au lieu de prendre l'adresse de tabintok[indice] pour l'indirection, il prend sa valeur ?

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 776
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 776
    Par défaut
    Déjà faire un typedef qui cache un pointeur c'est 1 mauvaise pratique

    Pour preuve , lorsque tu parles de &tabintok, tu manipules un pointeur de pointeur ... qui n'est utile que si tu veux le créer (l'allouer, notamment avec calloc effectivement)
    L'autre technique pour éviter de passer un pointeur de pointeur en paramètre, c'est de retourner l’adresse pointeur fraîchement créé avec malloc (ou petits frères)

    De plus, avec un pointeur la syntaxe [] n'est pas recommandé. Effectivement, le nom d'1 variable tableau est 1 pointeur sur la première case, mais il faut utiliser l'arithmétique des pointeurs

    • tabintok[X] -> *(tabintok + X)
    • (*tabintok) -> tabintok[0]
    • *(tabintok[indice]) -> *(*(tabintok + indice)), donc si à la case indice tu n'as pas un pointeur valide tu plantes


    Et comme l'a rappelé @Corluk, tu as la précédence des opérateurs

  5. #5
    Pgs
    Pgs est déconnecté
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    482
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 482
    Par défaut
    Merci !

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 851
    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 851
    Billets dans le blog
    1
    Par défaut
    Bonsoir
    Citation Envoyé par Pgs Voir le message
    Citation Envoyé par foetus Voir le message
    Déjà faire un typedef qui cache un pointeur c'est 1 mauvaise pratique
    Et en plus, le terme "TabInt" semble indiquer que tu assimiles un pointeur à un tableau (tableau de int ?) ce qui n'est pas du tout vrai.
    Certes la mémoire est composée de cases. Certes si tu pointes à un endroit de la mémoire tu peux alors te ballader à droite et à gauche de cet endroit comme quand tu te ballades dans un tableau. Certes la syntaxe "crochets" fonctionne pour un pointeur tout comme pour un tableau (en tableau 1D tout du moins). Certes quand tu passes un tableau à une fonction celle-ci ne récupère qu'une adresse qu'elle stockera dans un pointeur.

    Mais malgré toutes ces ressemblances, un pointeur ce n'est pas un tableau et si tu continues à les confondre, tu te planteras à plus ou moins long terme (notemment quand tu commenceras à travailler avec des tableaux en plusieurs dimensions)...

    Donc déjà rappelle-toi que ce n'est pas la même chose, et surtout comme dit foetus, ne cache pas les pointeurs car là aussi c'est un coup à se planter grave. Force-toi à les manipuler, à les comprendre, à les voir et petit à petit l'étoile ne te posera plus de souci.
    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]

  7. #7
    Pgs
    Pgs est déconnecté
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    482
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 482
    Par défaut
    Salut,
    Merci pour tes conseils.
    Je reconnais que je ne vois pas assez la différence entre les deux.
    Il me semblait que mon code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    typedef int* TabInt;
    TabInt tabintok;
    tabintok= (TabInt)calloc(10, sizeof(int));
    était une approche "pointeur" :
    - le processeur m'alloue une plage de mémoire et me donne l'adresse pour y accéder,
    - le type de mon pointeur détermine, à l'intérieur de cette plage, l'adresse des différents élements de même type qui y seront stockés.
    Est-ce bien une gestion " par pointeur" ?
    Et si oui, quelle est vraiment la différence avec un tableau ? Car mis à part le fait que j'ai réalisé manuellement les opérations, c'est pour moi comme si j'avais déclaré un tableau...

  8. #8
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 776
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 776
    Par défaut
    Citation Envoyé par Pgs Voir le message
    Est-ce bien une gestion " par pointeur" ?
    Dans un autre sens en C, tu as des pointeurs partout. Mais par contre, on peut éviter quelques allocations dynamiques ... mais l'allocation dynamique devient nécessaire dans les projets "sérieux"


    Citation Envoyé par Pgs Voir le message
    Et si oui, quelle est vraiment la différence avec un tableau ?
    Le type incomplet - "le langage C permet de déclarer un type sans indiquer explicitement son contenu"

    Lorsqu'on déclare/ définit un tableau, il faut que le type des éléments du tableau soit défini. Ce qui n'est pas le cas des pointeurs (<- dans un sens, avec un tableau tu as un sizeof qui intervient alors qu'1 pointeur aura toujours 1 taille de soit 4 octets (en 32 bits) soit 8 octets (en 64 bits)

  9. #9
    Pgs
    Pgs est déconnecté
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    482
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 482
    Par défaut
    Bien reçu.
    Est-ce pour cette raison que, si je passe un pointeur et non un tableau, je dois aussi passer sa "taille", qui ne peut être déduite du seul pointeur ?

  10. #10
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 851
    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 851
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Pgs Voir le message
    Je reconnais que je ne vois pas assez la différence entre les deux.
    Parce qu'elle est très subtile. En fait, en résumant très grossièrement, int tab[10] c'est un tableau et tant que tu restes dans le corps du bloc où est défini ce tableau ça reste un tableau ; mais si jamais tu passes ce tableau à une fonction (ex fct(tab)) alors la fonction ne reçoit plus qu'un pointeur. Le souci (souci qui contribue à cacher les différences et non souci d'utilisation) c'est que dans la fonction tu as le droit de continuer à l'utiliser "comme si" c'était un tableau
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main() {
        int tab[10];
        fct(tab);
    }
     
    void fct(int *p) {
        for (size_t i=0; i < 10; i++)
            p[i]=0;    // hé oui, "comme si" c'était un tableau !!!
    }

    Citation Envoyé par Pgs Voir le message
    Il me semblait que mon code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    typedef int* TabInt;
    TabInt tabintok;
    tabintok= (TabInt)calloc(10, sizeof(int));
    était une approche "pointeur" :
    Oui. Et justement pour continuer à "voir" les pointeurs il ne faut pas les cacher
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int *tabintok;
    tabintok= (int*)calloc(10, sizeof(int));
    Citation Envoyé par Pgs Voir le message
    - le processeur m'alloue une plage de mémoire et me donne l'adresse pour y accéder,
    Totalement exact. Et comme un tableau c'est aussi une plage de mémoire, la confusion persiste.

    Citation Envoyé par Pgs Voir le message
    - le type de mon pointeur détermine, à l'intérieur de cette plage, l'adresse des différents élements de même type qui y seront stockés.
    Exact (enfin ce n'est pas "l'adresse des éléments" mais "les éléments")

    Citation Envoyé par Pgs Voir le message
    Et si oui, quelle est vraiment la différence avec un tableau ? Car mis à part le fait que j'ai réalisé manuellement les opérations, c'est pour moi comme si j'avais déclaré un tableau...
    Tu l'as dit "comme si". Et dans 99,999% des cas, tu t'en serviras exactement de la même façon.

    Là où on peut voir les différences, c'est déjà avec sizeof
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main() {
        int tab[10];
        printf("%d\n", sizeof(tab));           // 40 (10 int de 4 octets)
        fct(tab);
    }
     
    void fct(int *p) {
        printf("%d\n", sizeof(p));              // 4 sur un OS 32 bits, et 8 sur un OS 64 bits (taille d'une adresse quoi)
    }

    Ensuite, c'est quand on quitte la 1D. Là le truc explose littéralement

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main() {
        int tab[5][10];
        tab[2][3]=123;                         // Pas de souci
        fct(tab);
    }
     
    void fct(int **p) {
        p[2][3]=456;                           // Marche pas => parce qu'avec juste un pointeur, le C n'a aucun moyen de savoir qu'il faut taper dans la case située à 2*10 + 3 positions du début => erreur de segmentation
    }

    Toutefois avec une approche 100% pointeur, ça se remet à fonctionner
    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
    int main() {
        int **tab;
        tab=malloc(5 * sizeof(int*));
        for (size_t i=0; i < 5; i++)
            tab[i]=malloc(10 * sizeof(int));
     
        tab[2][3]=123;                         // Pas de souci
        fct(tab);
     
        // Et (partie chiante du malloc) ne pas oublier de libérer ce qui a été alloué (et quand tu fais une gestion réelle tu dois tester chaque malloc et gérer quoi faire si un seul échoue)
        for (size_t i=0; i < 5; i++)
            free(tab[i]);
        free(tab);
    }
     
    void fct(int **p) {
        p[2][3]=456;
       // Là ça fonctionne; Toutefois la représentation en mémoire n'a plus rien à voir avec un beau tableau bien contigü
       // Dans cette approche tu n'as plus que des pointeurs qui pointent dans tous les azimuths
    }

    Et ça peut aussi fonctionner avec les tableaux mais alors il faut bien gérer ce que reçoit la fonction
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main() {
        int tab[5][10];
        tab[2][3]=123;                         // Pas de souci
        fct(tab);
    }
     
    void fct(int (*p)[10]) {
        p[2][3]=456;                           // ok car "p" est maintenant déclaré comme un pointeur sur un tableau de 10 int donc p[2][3] peut être calculé sans souci
    }

    Pour résumer, un tableau équivaut à un pointeur uniquement pour sa première dimension qui peut alors être transformée en pointeur (dans mon exemple tab[5] est devenu (*p) et avec les parenthèses obligatoires suite aux priorités des opérateurs dans lesquelles les crochets sont plus prioritaires que l'étoile). C'est pour ça que beaucoup préfèrent coder les tableaux nD en 1D et où on se tape nous-même la conversion des indices nD en index 1D. On se fait chier mathématiquement mais on se fait plus chier au niveau de ce que voit le C.
    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]

  11. #11
    Pgs
    Pgs est déconnecté
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    482
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 482
    Par défaut
    Je te remercie d'avoir pris le temps de m'expliquer tout cela. J'y vois plus clair maintenant !

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

Discussions similaires

  1. Mise en forme tableau word avec VB6
    Par alexxx69 dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 03/04/2007, 12h50
  2. [Tableaux] Renverser un tableau indicé
    Par ciel65 dans le forum Langage
    Réponses: 2
    Dernier message: 22/12/2006, 10h24
  3. [2.0][C#] Comment créer un tableau indicé
    Par LE NEINDRE dans le forum C#
    Réponses: 3
    Dernier message: 13/11/2006, 16h38
  4. [Tableaux] Tableau à indices non continus
    Par TNorth dans le forum Langage
    Réponses: 9
    Dernier message: 03/05/2006, 15h18
  5. [C#] Form ShowDialog pas comprendre :p
    Par betaphp dans le forum Windows Forms
    Réponses: 2
    Dernier message: 19/12/2005, 14h37

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