1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut Bug de code dont je ne trouve pas la source

    Bonjour,

    En désespoir de cause, je me tourne vers vous.
    Cela fait 2 jours que je m'arrache les cheveux pour trouver mon erreur et je ne comprend pas d’où elle peut venir.
    Je ne demande que l'on fasse le code a ma place, mais juste m'aider a trouver d'ou peut bien venir mon erreur..

    J'ai donc un code "tout simple" qui me sert a construire un tableau à deux dimension contenant des structures.
    Jusque la tout va bien, j'ai mon tableau, mes structures, que j'initialise et auquel je peux accéder depuis mon main sans problème.

    Le soucis vient lorsque j'essaie d'y avoir accès dans une autre fonction.
    Pour expliquer brièvement mon intention..
    Les structure contiennent entres autre les coordonnées x et y des cellules du tableau a 2 dimensions, que j'initialise lors de la création du tableau.
    Comme je le disais plus haut, depuis mon main, je n'ai pas de problème pour y accéder, et ma petite boucle de test m'affiche bien les bonnes coordonnées pour chaque cellules.

    Or, dans une autres fonction, où j'essaie aussi d'afficher ces deux valeurs de coordonnée de cellules, mon x et mon y s'inversent.. j'ai beau chercher mon erreur, une "stupide" inversion dans les variables.. je ne vois pas..
    Pour moi le code est bon, mais apparemment il ne l'est pas..

    Plutôt que de copier l’entièreté du code ici, j'ai rendu mon repo sur github public, et vous pouvez y accéder via ce lien
    BatailleNavale (vous tomber directement dans la bonne branche du repo avec le lien, sinon c'est la branche Develop v0.3.1)
    (Le projet s'appelle BatailleNavale, mais ne fait que de s'en inspirer)
    Vous pouvez forker, soumettre des commit ou push dans le repo github si vous le voulez, ou bien directement ici..

    Le fichier incriminer se nomme fct_CoordBateau.c, les lignes qui me posent problème sont la 90 et 91
    Ce fichier contient des lignes en printf qui me permettent de debugger le code et qui ne sont pas destinées a rester.
    Le code est censé me permettre de placer la case de départ d'un bateau et d'extrapoler les 4 positions (droite, haut, gauche, bas) sur le damier, le tout en tenant compte de sa taille.

    Le fichier fct_Crea_tDamier.h contient la déclaration de la structure, et fct_Crea_tDamier.c la construction des tableaux et l'initialisation de la structure avec des valeurs par défaut.
    La fonction fct_Crea_tIndexPos.h fct_Crea_tIndexPos.c ne servent a rien pour le moment.

    Si vous lancer le projet pour test, vous pouvez entrer les valeurs 10 10 5 (pour dans l'ordre, la taille du damier, la taille du bateau le plus grand, et la taille du bateau choisi).

    Si le bug est corrigé, normalement le damier (vide pour le moment) s'affiche..



    Je ne pense pas avoir a expliquer pas avant le projet, mais je peux le faire pour ceux qui voudrait en savoir plus.. il me reste encore pas mal de chose a faire, et le code du projet est encore vide de beaucoup de chose a implémenter.
    Mais je ne peux pas continuer tant que j'ai ce bug :/



    Un tout grand merci a ceux qui prendront la peine de m'aider a trouver se "'&é'"@# bug

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Ton problème est le suivant:

    à l'appel, tu procèdes ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main() {
    	stCaseDamier **tDamier = NULL;
    	tDamier = fct_Crea_tDamier(size_Damier);
    	fct_CoordBateau(dTailleBateau, size_Damier, &tDamier);
    }
     
    void fct_CoordBateau(int dTailleBateau, size_t size_Damier, stCaseDamier ***tDamier) {
    	int dI_XPos_TB1 = tDamier[0][0]->dXPos - dTailleBateau + 1;
    }
    Dans main, tDamier est un pointeur de pointeur de stCaseDamier.
    Sa valeur est donc l'adresse d'une chose (ou d'un tableau) contenant l'adresse d'un (ou d'un tableau de) stCaseDamier.

    (note, l'adresse d'une chose ou d'un tableau de chose sont équivalent, pour les pointeurs)

    Tu donne à la fonction fct_CoordBateau l'adresse de tDamier.
    Dans cet appel de cette fonction, le paramètre tDamier (distinct de la variable de de main) contient l'adresse d'une chose contenant un tableau d'adresse de tableau de stCaseDamier
    Dans cette fonction, tDamier[0][0] est le premier élément du premier élément de ce triple pointeur.

    Sauf, que c'est faux.
    le vrai tableau de tableau, c'est *tDamier, l'élément est (*tDamier)[0][0].

    Pour t'éviter ce problème crée une structure de damier, et les fonctions qui vont avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef unsigned int coordDamier;
    typedef struct {
        stCaseDamier ** cases;
        coordDamier taille;
    } stDamier;
     
    stDamier damier_creer(coordDamier taille)
    void damier_detruire(stDamier *)
    bool/int damier_accepte(stDamier const*, coordDamier ligne, coordDamier colonne)
    stCaseDamier* damier_case(stDamier *, coordDamier ligne, coordDamier colonne)
    stCaseDamier const* damier_case_const (stDamier const*, coordDamier ligne, coordDamier colonne)
    Avec ces fonctions, tu gagne en prime le bonus de pouvoir remplacer facilement la forme interne de stDamier, sans changer le reste du code.
    Notamment pour passer à un pointeur de tableau linéarisé (malloc( sizeof(stCaseDamier)*taille*taille ); et accès via cases[taille*ligne+colonne])
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut

    Déjà un tout grand merci pour avoir pointer mon erreur aussi vite

    Je me doutais que c’était encore ces pointeurs qui me compliquaient la vie.. décidément ces machins sont vraiment délicats et tortueux a comprendre..

    Mais je vais devoir avouer que je ne comprend pas tout ce que tu m'expliques.. est ce que tu pourrais prendre la peine de développer un peu plus ton explications stp??

    Citation Envoyé par ternel Voir le message
    Tu donne à la fonction fct_CoordBateau l'adresse de tDamier.
    Dans cet appel de cette fonction, le paramètre tDamier (distinct de la variable de de main) contient l'adresse d'une chose contenant un tableau d'adresse de tableau de stCaseDamier
    Dans cette fonction, tDamier[0][0] est le premier élément du premier élément de ce triple pointeur.

    Sauf, que c'est faux.
    le vrai tableau de tableau, c'est *tDamier, l'élément est (*tDamier)[0][0].
    Quand tu dis que j'envoie le paramètre tDamier (distinct de la variable de de main)!? Est ce que je n'envoie pas la bonne adresse?? Comment se fait il que j'arrive a accéder a mes valeurs alors??
    Moi qui pensais avoir un peu plus compris les pointeurs dans mon post précédent :/



    Citation Envoyé par ternel Voir le message
    Pour t'éviter ce problème crée une structure de damier, et les fonctions qui vont avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef unsigned int coordDamier;
    typedef struct {
        stCaseDamier ** cases;
        coordDamier taille;
    } stDamier;
     
    stDamier damier_creer(coordDamier taille)
    void damier_detruire(stDamier *)
    bool/int damier_accepte(stDamier const*, coordDamier ligne, coordDamier colonne)
    stCaseDamier* damier_case(stDamier *, coordDamier ligne, coordDamier colonne)
    stCaseDamier const* damier_case_const (stDamier const*, coordDamier ligne, coordDamier colonne)
    Avec ces fonctions, tu gagne en prime le bonus de pouvoir remplacer facilement la forme interne de stDamier, sans changer le reste du code.
    La j'avoue, je suis complétement paumé, et je ne comprend pas


    Quel est l’intérêt de passer par un tableau linéariser?? Il me semble que ca complique l’écriture et l'utilisation dans le code..


    Est ce qu'il ne pourrait pas y avoir une solution ou je corrige "simplement" l'erreur de l'adressage du pointeur??

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    je vais prendre tes questions dans l'ordre inverse


    Est ce qu'il ne pourrait pas y avoir une solution ou je corrige "simplement" l'erreur de l'adressage du pointeur?
    Oui: tu comprends ce qu'est un pointeur, qui est l'adresse de quoi, et pourquoi tu utilises chaque niveau de pointage (chaque étoile).
    en l'occurence, le paramètre de ta fonction prendre l'adresse d'un double pointeur, pour pouvoir le modifier. Si ce n'est pas voulu, autant supprimer la troisième étoile (et la prise d'adresse à l'appel).

    Quel est l’intérêt de passer par un tableau linéariser?
    Un tableau T est une suite contigüe d'éléments.
    Accéder à un tel élément demande au processeur de calculer l'adresse de l'élément, en calculant l'adresse &T + i * sizeof(élément). Cette opération d'adresse peut s'écrire (T + i), car l'arithmétique des pointeurs est toujours fait en tenant compte de la taille du type pointé.
    L'accès à proprement parler à l'élément s'écrit *(T+i) ou T[i].

    Avec un deuxième niveau de pointeur, T+i est l'adresse d'un pointeur, qu'il faut encore décaler de j. L'opération effectuée est concrètement *(*(T +i) + j)Il y a deux accès à la mémoire avant d'atteindre effectivement l'élément.
    Chaque ligne est un tableau en mémoire, situés chacun n'importe où, précisément pas ensemble (puisqu'on a fait un malloc pour chacune).

    Avec un tableau linéarisé, tous les éléments se suivent: on calcule *(T +i *taille_ligne + j) et on n'a qu'une seule indirection.
    Fondamentalement, c'est déjà plus rapide.
    Pire, comme les lignes se suivent, le cache du processeur devrait être mieux utilisé, et les performances pourraient être significativement améliorées.

    La j'avoue, je suis complétement paumé, et je ne comprend pas
    L'idée, c'est que dans ton code utile, tu n'aie pas besoin des détails du damier.
    Exactement comme tu passes par fopen, fclose, fread, etc plutot que d'accéder aux détails de FILE.
    Les opérations de base du damier sont d'accéder à un élément du damier (damier_case et damier_case_const, si c'est juste pour regarder), créer et détruire un damier (qui appelle malloc/free selon les besoins). Et surtout, vérifier efficacement qu'on ne sort pas du damier.

    Quand tu dis que j'envoie le paramètre tDamier (distinct de la variable de de main)!? Est ce que je n'envoie pas la bonne adresse?? Comment se fait il que j'arrive a accéder a mes valeurs alors??
    Moi qui pensais avoir un peu plus compris les pointeurs dans mon post précédent :
    Le paramètre tDamier de la fonction est une variable appartenant à cette fonction.
    La variable locale tDamier de main lui appartient.
    Ces deux variables sont donc forcément distincte.

    Lors de l'appel de la fonction, son paramètre est créée (sur la pile), et initialisée avec l'expression donnée en argument de l'appel de fonction.
    le tDamier de la fonction est donc initialisé avec la valeur &tDamier calculée dans main() donc l'adresse du tDamier de main.
    tDamier de main est un pointeur vers tableau de pointeurs vers des tableaux de cases. C'est un damier.
    En tant que pointeur, le copier ne copie pas les tableaux pointés (c'est l'intérêt et problème des pointeurs)

    tDamier de la fonction est donc l'adresse d'un damier.
    Pour accéder à une case, il faut d'abord accéder au damier (par l'étoile), puis descendre dans le tableau de tableaux
    Donc, dans la fonction, c'est (*tDamier)[i][j] qu'il faut utiliser.

    C'est dans la fonction que tu utilises mal ton pointeur.
    Je n'ai pas regardé, mais si tu n'as pas besoin que la fonction modifie le pointeur en lui même, il est inutile de prendre un triple pointeur, un double pointeur suffit.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Prenons un exemple explicatif, en choisissant des adresses comme le ferait le système, en supposant size_Damier = 2.
    Au vue de ton code, il est très probable que sizeof(stCaseDamier) = 6*sizeof(int) = 24.

    Ton main() se déroule plus ou moins ainsi:

    déclaration de tDamier: une variable est créée sur la pile: son adresse est 32 (arbitrairement) et sa valeur est NULL (=0).

    appel de la fonction de création du damier: (tDamier = fct_Crea_tDamier(size_Damier))
    une variable est créé sur la pile, pour contenir le paramètre de la fonction. Son adresse est 36 (= 32 + sizeof(tDamier)), et sa valeur est size_Damier = 2.

    On entre dans la fonction:
    une variable tDamier est créé sur la pile: Son adresse est 36 + sizeof(size_t) = 40. sa valeur est NULL (tu l'as initialisée ainsi).
    deux autres variables non initialisées sont créées: i et j, d'adresse 44 et 48.

    Allocation du tableau de pointeurs de lignes tDamier = malloc(size_Damier * sizeof(stCaseDamier *));;
    tDamier vaut maintenant une adresse dans le tas, par exemple 1234560000.

    boucle for exécutant principalement tDamier[i] = malloc(size_Damier * sizeof(stCaseDamier)); puis l'initialisation
    tDamier[i] vaut 1234560000 + i * sizeof(*tDamier) c'est à dire 1234560000 puis 1234560000+4

    la première fois, malloc retourne arbitrairement 187365400
    on écrit le nombre 187365400 dans tDamier[0] c'est à dire à l'adresse 1234560000;
    la boucle d'initialisation va donc remplir tDamier[0][0] (@187365400+0) et tDamier[0][1] (@187365400+24)

    Au second passage, malloc retourne (toujours arbitrairement) 78965000
    on écrit le nombre 78965000 dans tDamier[1] c'est à dire à l'adresse 1234560004;
    la boucle d'initialisation va donc remplir tDamier[0][0] (@78965000+0) et tDamier[0][1] (@187365400+24)

    Enfin, return tDamier: on retourne 1234560000.

    on finit l'instruction de main:
    tDamier de main reçoit la valeur 1234560000.

    L'état de la mémoire est donc:
    • tDamier de main: adresse 32, valeur 1234560000
    • adresse 78965000, valeur <une case initialisée>
    • adresse 78965024, valeur <une case initialisée>
    • adresse 187365400, valeur <une case initialisée>
    • adresse 187365424, valeur <une case initialisée>
    • adresse 1234560000, valeur 187365400
    • adresse 1234560004, valeur 78965000

    Note: à chaque fois, plusieurs octets sont occupés, mais l'adresse est celle du premier octet de la variable. Le compilateur et le programme savent quelle taille utiliser.


    Lorsque tu appelles fct_CoordBateau(dTailleBateau, size_Damier, &tDamier);, &tDamier vaut 32.
    La signature de la fonction étant void fct_CoordBateau(int dTailleBateau, size_t size_Damier, stCaseDamier ***tDamier), cela signifie que son paramètre tDamier est initialisé avec la valeur 32.
    Le code de cette fonction commence par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	int i = 0;
    	int j = 0;
    	int dI_XPos_TB1 = tDamier[i][j]->dXPos - dTailleBateau + 1;
    Que vaut tDamier[i][j]->dXPos?
    tDamier vaut 32, i et j valent 0.
    tDamier[i] vaut ce que contient la case mémoire située en 32 + 0, c'est à dire 1234560000.
    tDamier[i][j] vaut ce que contient la case mémoire située en 1234560000 + 0, c'est à dire 187365400
    tDamier[i][j]-> est l'emplacement mémoire désigné par la 187365400, prêt à être décallé par la position de dXPos. c'est à dire une case initialisée.

    Là ou ca pose problème, c'est que pour toute autre valeur de i, tu regardes n'importe ou.
    par exemple, pour tDamier[1][0], on a:
    tDamier[1] vaut la valeur située 32+1*sizeof(*tDamier) = 32 + 4 = 36. Cette valeur n'est pas définie de manière correcte. En fait, il s'agit de l'emplacement de tIndexPos de main.
    tDamier[1][0] est une erreur, mais vaut en fait tIndexPos[0]
    tDamier[1][0]->dXPos est une erreur, mais vaut en fait tIndexPos[0]+le décallage de dXPos dans stCaseDamier, soit a peu près n'importe quoi.

    Plus "amusant", si tIndexPos vaut toujours NULL, tDamier[1][0] provoquera normalement un crash pour erreur de segmentation, car tu essaies d'accéder à l'adresse NULL.



    Remarque, avec un tableau linéarisé, l'initialisation donnerait quelque chose comme ceci:
    • tDamier de main: adresse 32, valeur 1234560000
    • adresse 187365400, valeur <une case initialisée>
    • adresse 187365424, valeur <une case initialisée>
    • adresse 187365448, valeur <une case initialisée>
    • adresse 187365472, valeur <une case initialisée>
    • adresse 1234560000, valeur 187365400
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Comme tu ne modifie pas tDamier directement dans ta fonction, retire la troisième étoile, et remplace tes tDamier[][]-> par tDamier[][]..

    A présent, un gros commentaire (sous forme de question):
    Pourquoi utiliser cette notation hongroise?

    fct_coord_bateau?
    A quel moment peut-on ne pas voir que c'est une fonction? est-ce que la bibliothèque standard contient fct_fopen, fct_malloc, fct_strcmp, fct_abs, etc…
    Que fait coord_bateau? Ce n'est pas clair dans son nom.

    De même pourquoi tDamier plutot que damier?
    tu n'as pas utiliser iI et iJ que je sache?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut

    Citation Envoyé par ternel Voir le message
    je vais prendre tes questions dans l'ordre inverse



    Oui: tu comprends ce qu'est un pointeur, qui est l'adresse de quoi, et pourquoi tu utilises chaque niveau de pointage (chaque étoile).
    en l'occurence, le paramètre de ta fonction prendre l'adresse d'un double pointeur, pour pouvoir le modifier. Si ce n'est pas voulu, autant supprimer la troisième étoile (et la prise d'adresse à l'appel).


    ...


    Le paramètre tDamier de la fonction est une variable appartenant à cette fonction.
    La variable locale tDamier de main lui appartient.
    Ces deux variables sont donc forcément distincte.

    Lors de l'appel de la fonction, son paramètre est créée (sur la pile), et initialisée avec l'expression donnée en argument de l'appel de fonction.
    le tDamier de la fonction est donc initialisé avec la valeur &tDamier calculée dans main() donc l'adresse du tDamier de main.
    tDamier de main est un pointeur vers tableau de pointeurs vers des tableaux de cases. C'est un damier.
    En tant que pointeur, le copier ne copie pas les tableaux pointés (c'est l'intérêt et problème des pointeurs)

    tDamier de la fonction est donc l'adresse d'un damier.
    Pour accéder à une case, il faut d'abord accéder au damier (par l'étoile), puis descendre dans le tableau de tableaux
    Donc, dans la fonction, c'est (*tDamier)[i][j] qu'il faut utiliser.

    C'est dans la fonction que tu utilises mal ton pointeur.
    Je n'ai pas regardé, mais si tu n'as pas besoin que la fonction modifie le pointeur en lui même, il est inutile de prendre un triple pointeur, un double pointeur suffit.

    Le truc, c'est que j'ai besoin de lire et de pouvoir modifier les valeurs de la structure dans cette fonction.
    C'est important pour la suite de l'algo et de mon prg.
    J'avais déjà essayé avec (*tDamier)[i][j] (autant dire que j'ai tester des trucs juste pour voir ce que ca donne a la compil), mais Visual Studio (2017) m'indique que ca n'est pas bon L'expression doit avoir un type pointeur.



    Citation Envoyé par ternel Voir le message
    Un tableau T est une suite contigüe d'éléments.
    Accéder à un tel élément demande au processeur de calculer l'adresse de l'élément, en calculant l'adresse &T + i * sizeof(élément). Cette opération d'adresse peut s'écrire (T + i), car l'arithmétique des pointeurs est toujours fait en tenant compte de la taille du type pointé.
    L'accès à proprement parler à l'élément s'écrit *(T+i) ou T[i].

    Avec un deuxième niveau de pointeur, T+i est l'adresse d'un pointeur, qu'il faut encore décaler de j. L'opération effectuée est concrètement *(*(T +i) + j)Il y a deux accès à la mémoire avant d'atteindre effectivement l'élément.
    Chaque ligne est un tableau en mémoire, situés chacun n'importe où, précisément pas ensemble (puisqu'on a fait un malloc pour chacune).

    Avec un tableau linéarisé, tous les éléments se suivent: on calcule *(T +i *taille_ligne + j) et on n'a qu'une seule indirection.
    Fondamentalement, c'est déjà plus rapide.
    Pire, comme les lignes se suivent, le cache du processeur devrait être mieux utilisé, et les performances pourraient être significativement améliorées.


    L'idée, c'est que dans ton code utile, tu n'aie pas besoin des détails du damier.
    Exactement comme tu passes par fopen, fclose, fread, etc plutot que d'accéder aux détails de FILE.
    Les opérations de base du damier sont d'accéder à un élément du damier (damier_case et damier_case_const, si c'est juste pour regarder), créer et détruire un damier (qui appelle malloc/free selon les besoins). Et surtout, vérifier efficacement qu'on ne sort pas du damier.
    Je comprend la théorie, je ne pense pas avoir encore assez de pratique pour utiliser ca de façon naturelle..
    Quand j'aurai un peu plus l'habitude de voir et comprendre ce que je fais, je comprendrais mieux comment optimisé mon code..
    Pour le moment, j'ai encore besoin de faire les choses plus prosaïquement et de façon plus "visuelle" dans mon esprit.. et le tableau "carré" me parle plus, que le tableau linéaire..


    Comme tu as rajouter une réponse entre le moment ou je répondais, je vais prendre le temps de lire et comprendre ta dernière réponse..

  8. #8
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut

    Citation Envoyé par ternel Voir le message
    A présent, un gros commentaire (sous forme de question):
    Pourquoi utiliser cette notation hongroise?

    fct_coord_bateau?
    A quel moment peut-on ne pas voir que c'est une fonction? est-ce que la bibliothèque standard contient fct_fopen, fct_malloc, fct_strcmp, fct_abs, etc…
    Que fait coord_bateau? Ce n'est pas clair dans son nom.

    De même pourquoi tDamier plutot que damier?
    tu n'as pas utiliser iI et iJ que je sache?
    Pour la notation, c'est mon prof qui exige que l'on nomme les éléments de code avec un suffixe, "d" pour les int, "c" pour les chars, "st" structure, "et" pour les étiquettes de structures, etc..
    le fct_ avant les fonctions sont de moi, je pensais que ca pouvait m'aider a mieux voir et identifié mes fichiers dans l'explorateur de VS (et aussi dans l'explorateur Windows).. J'avoue que pour ca je cherche encore un peu la bonne méthode.. tout comme la façon de nommer mes fonctions (ce qui n'est pas tjrs évident).. ainsi que par exemple les macros dans le fichier que j'appelle "define" puisqu'il contient des define..

  9. #9
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Commence par regrouper dans une même paire .h et .c les fonctions et structures qui vont ensemble.
    par exemple, si tu fais les quelques fonctions que je te recommande, elles iraient dans la même paire.
    Le nom du fichier serait le nom du module que cela représente.
    Ainsi damier.h et damier.c correspondent aux manipulations du damier en lui même
    casedamier.h et casedamier.c pourrait être une autre paire.

    il n'est pas nécessaire de faire un fichier par fonction.

    Pour les noms de fonctions, en C surtout, un préfixe de module est intéressant.
    par exemple, j'ai utilisé damier_ pour les cinq fonctions liées uniquement au damier.
    (note, ton damier est une grille, les cases ne sont pas alternées en couleur, et surtout, ne servent pas à jouer aux dames).

    Quant aux macros, j'ai tendances à la paranoïa, et les préfixer par MACRO_, et bien sur les écrire intégralement en majuscules.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  10. #10
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 026
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 026
    Points : 3 023
    Points
    3 023

    Par défaut

    Petite remarque à côté : lorsque tu constates des répétitions du type de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        printf("1.2 test else if i(%d) == XPos(%d) || j(%d) == dYPos(%d)\n\n", i, dXPos, j, dYPos);
        printf("tDamier[%d][%d].dXPos = %d\n", i, j, tDamier[i][j]->dXPos);
        printf("tDamier[%d][%d].dYPos = %d\n", i, j, tDamier[i][j]->dYPos);
        printf("tDamier[%d][%d].dPoids = %d\n", i, j, tDamier[i][j]->dPoids);
        printf("tDamier[%d][%d].cNomBat = %c\n\n", i, j, tDamier[i][j]->cNomBat);
    ..c'est un indice qu'il faut probablement en faire une fonction (ou une macro), même et surtout lorsqu'il s'agit de code de debug. Le code est plus concis, plus clair, plus sûr (car une fois que tu t'es
    assuré du bon comportement de la fonction, tu n'as plus à t'en soucier). Un exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
     
    #if !defined(NDEBUG)
    /* debug-only helper */
    void cell_dump(const stCaseDamier **board, int x, int y) {
        const stCaseDamier cell = board[i][j];
        printf(
          "damier[%d][%d]:\n"
          "  .dXPos = %d\n"
          "  .dYPos = %d\n"
          "  .dPoids = %d\n"
          "  .cNomBat = %c\n\n",
          i, j, cell->dXPos, cell->dYPos, cell->dPoids, cell->cNomBat
        );
    }
    #else
      #define cell_dump(b, x, y)
    #endif

  11. #11
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut

    Donc comme je le disais plus haut, j'ai besoin de modifier et donc d'avoir accès aux valeurs de mes variables de la structure.





    Citation Envoyé par ternel Voir le message
    Prenons un exemple explicatif, en choisissant des adresses comme le ferait le système, en supposant size_Damier = 2.
    Au vue de ton code, il est très probable que sizeof(stCaseDamier) = 6*sizeof(int) = 24.

    [...]



    Remarque, avec un tableau linéarisé, l'initialisation donnerait quelque chose comme ceci:
    • tDamier de main: adresse 32, valeur 1234560000
    • adresse 187365400, valeur <une case initialisée>
    • adresse 187365424, valeur <une case initialisée>
    • adresse 187365448, valeur <une case initialisée>
    • adresse 187365472, valeur <une case initialisée>
    • adresse 1234560000, valeur 187365400
    Je comprend.. je ne vois pas comment l'utiliser pour régler mon problème, mais je comprend ca..
    Il s'agit donc bien d'un mauvais adressage dans la fonction, mais je ne vois pas comment outrepasser ca..
    le fait de passer par (*tDamier)[i][j] m'affiche une erreur -> L'expression doit avoir un type pointeur (??!!)
    pour que ca fonctionne, je dois rajouter une * dans void fct_CoordBateau(int dTailleBateau, size_t size_Damier, stCaseDamier ****tDamier), et c'est pas bon (évidement).
    Je pensais avoir bien utilisé les pointeurs, et les indirections, mais apparemment, y a encore une subtilité qui m’échappe la..



    En reprenant tes explications du début.
    Citation Envoyé par ternel Voir le message
    Pour t'éviter ce problème crée une structure de damier, et les fonctions qui vont avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef unsigned int coordDamier;
    typedef struct {
        stCaseDamier ** cases;
        coordDamier taille;
    } stDamier;
     
    stDamier damier_creer(coordDamier taille)
    void damier_detruire(stDamier *)
    bool/int damier_accepte(stDamier const*, coordDamier ligne, coordDamier colonne)
    stCaseDamier* damier_case(stDamier *, coordDamier ligne, coordDamier colonne)
    stCaseDamier const* damier_case_const (stDamier const*, coordDamier ligne, coordDamier colonne)
    Avec ces fonctions, tu gagne en prime le bonus de pouvoir remplacer facilement la forme interne de stDamier, sans changer le reste du code.
    Notamment pour passer à un pointeur de tableau linéarisé (malloc( sizeof(stCaseDamier)*taille*taille ); et accès via cases[taille*ligne+colonne])
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    typedef unsigned int coordDamier;
    typedef struct {
        stCaseDamier ** cases;
        coordDamier taille;
    } stDamier;
    On crée une structure du nom de stDamier comprenant deux éléments:
    Une variable de type "coordDamier" (unsigned int) pour y rentrer la taille du Damier, voir les référence de mes lignes et colonnes (coordDamier ligne, coordDamier colonne) dont tu te sers plus bas. (??)
    Un pointeur de pointeur vers une structure de type "stCaseDamier" du nom (d'etiquette) "Case", qui reprendrait les différents éléments que j'avais utilisé (cNomBat, dTypeBat, dXPos, dYPos, dPoids, dPointDepart) (??)

    stDamier damier_creer(coordDamier taille) qui est une fonction de type "stDamier" pour créer le tableau tDamier a la taille de tDamier = malloc(taille * sizeof(stDamier)) (??)
    void damier_detruire(stDamier *) qui est une fonction destinée a détruire/liberer free() le tableau crée précédemment. (??)
    bool/int damier_accepte(stDamier const*, coordDamier ligne, coordDamier colonne) je ne comprend pas l'étoile après le const*, sinon c'est une fonction qui permet de tester si la structure stDamier est valide (??)
    stCaseDamier* damier_case(stDamier *, coordDamier ligne, coordDamier colonne) pareil, je ne comprend pas l'étoile après le stDamier * .. fonction d'initialisation des cases (??)
    [c]stCaseDamier const* damier_case_const (stDamier const*, coordDamier ligne, coordDamier colonne)[/] de nouveau, l’étoile apres le const* .. la fonction qui remplace celle que j'avais nommé fct_CoordBateau (??)



    Citation Envoyé par Matt_Houston Voir le message
    Petite remarque à côté : lorsque tu constates des répétitions du type de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        printf("1.2 test else if i(%d) == XPos(%d) || j(%d) == dYPos(%d)\n\n", i, dXPos, j, dYPos);
        printf("tDamier[%d][%d].dXPos = %d\n", i, j, tDamier[i][j]->dXPos);
        printf("tDamier[%d][%d].dYPos = %d\n", i, j, tDamier[i][j]->dYPos);
        printf("tDamier[%d][%d].dPoids = %d\n", i, j, tDamier[i][j]->dPoids);
        printf("tDamier[%d][%d].cNomBat = %c\n\n", i, j, tDamier[i][j]->cNomBat);
    ..c'est un indice qu'il faut probablement en faire une fonction (ou une macro), même et surtout lorsqu'il s'agit de code de debug. Le code est plus concis, plus clair, plus sûr (car une fois que tu t'es
    assuré du bon comportement de la fonction, tu n'as plus à t'en soucier). Un exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
     
    #if !defined(NDEBUG)
    /* debug-only helper */
    void cell_dump(const stCaseDamier **board, int x, int y) {
        const stCaseDamier cell = board[i][j];
        printf(
          "damier[%d][%d]:\n"
          "  .dXPos = %d\n"
          "  .dYPos = %d\n"
          "  .dPoids = %d\n"
          "  .cNomBat = %c\n\n",
          i, j, cell->dXPos, cell->dYPos, cell->dPoids, cell->cNomBat
        );
    }
    #else
      #define cell_dump(b, x, y)
    #endif
    Je vais en tenir compte..
    Ainsi que les remarques sont les noms des fonctions et de mes variables..

  12. #12
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Ma proposition à base d'une structure et de fonction qui la manipule permet de ne plus avoir aucun problème de pointeur de tableau dans le reste du code.

    Je t'invite très fortement à essayer de la mettre en œuvre.
    Le contenu des fonctions est probablement très simple, et tu les as déjà écrites, en fait.
    • stDamier damier_creer(coordDamier taille) c'est ta fonction d'initalisation.
    • void damier_detruire(stDamier *) c'est ta fonction de destruction.
    • bool/int damier_accepte(stDamier const* damier, coordDamier ligne, coordDamier colonne) {return (ligne >=0 && ligne < damier->taille && colonne >=0 && colonne < damier->taille) ? 1 : 0:}. Tu devrais utiliser true et false de <bool.h> (sauf si ton compilateur est si vieux qu'il n'en dispose pas).
    • stCaseDamier* damier_case(stDamier * damier, coordDamier ligne, coordDamier colonne) { return damier->cases[ligne][colonne]; }
    • stCaseDamier const* damier_case_const(stDamier const* damier, coordDamier ligne, coordDamier colonne) { return damier->cases[ligne][colonne]; }

    Cette dernière variante permet de lire un damier constant, ce qui est toujours une bonne chose de pouvoir faire.

    Avec ca, tes (*tDamier)[i][j] ou (*tDamier)[i][j] deviennent damier_case_const(tDamier, i, j) avec ou sans & à tDamier, selon si tu as sous la main un pointeur ou non.

    Encore une fois, ta difficulté disparaîtra si tu te donne le moyen de le faire.
    Ici, tu n'auras plus qu'un seul niveau de pointeur à utiliser, et c'est très difficile de ne pas savoir qu'en faire.

    On ne joue pas avec les allumettes, on apprend ce qu'elles sont, et quand les gratter et quand ne pas le faire.
    Ca marche aussi avec les pointeurs.

    cf ma signature: un pointeur de moins est une montagne d'erreur en moins.
    Utilise une structure et des fonctions simples: tu obtiendras void fct_CoordBateau(int dTailleBateau, stCaseDamier *tDamier), avec deux ou trois étoiles de moins (et un argument de moins, ce qui fait une source d'erreur en moins).
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  13. #13
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut

    Je vais essayé ta solution.



    Citation Envoyé par ternel Voir le message

    Avec ca, tes (*tDamier)[i][j] ou (*tDamier)[i][j] deviennent damier_case_const(tDamier, i, j) avec ou sans & à tDamier, selon si tu as sous la main un pointeur ou non.
    Par contre, comme je disais plus haut, (*tDamier)[i][j] ne fonctionne pas (visual studio me souligne la parenthèse en rouge et me dit que "L'expression doit avoir un type pointeur") et ne règle pas mon problème..
    Je ne voudrais pas me montrer têtu, mais avant d'essayer ta solution, j'aurais vraiment voulu trouver comment régler mon problème..

  14. #14
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Je t'ai donné les explications.
    Le problème, c'est que tu t'enbrouille sur ce qui est un pointeur, un tableau dynamique (c'est aussi un pointeur, mais [] est envisageable) et une variable.
    Reprends au début.

    si tu prends en argument quelque chose qui est un pointeur, tu peux modifier le pointé.
    Donc, pour modifier les cases d'un tableau 2D (dynamique 2 fois), un double pointeur suffit.
    Une fois que tu as un double pointeur, la bonne notation est tableau[i][j].

    A priori, ton problème, c'est que tu n'as pas remplacé la flèche -> par un point ..
    la fleche permet d'accéder a un champ précise d'une structure pointée par l'argument de gauche.
    Voici plusieurs syntaxes possibles.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef struct { int champ; } Bidule;
     
    void f(Bidule** parametre) {
        (*parametre)->champ = 1;
        (**parametre).champ = 2;
        (*parametre)[2].champ = 3;
        parametre[2]->champ = 4;
        (*(parametre[2])).champ = 5;
    }
    1. syntaxe typique quand une des étoiles sert effectivement de pointeur, et la seconde est utilisé pour "je veux modifier le pointeur".
    2. équivalent de la précédente, mais bien plus rare, à mon avis.
    3. ici, le tableau a été passé par pointeur (on a pris son adresse à l'appel), le tableau effectif est un tableau de Bidule.
    4. le tableau effectif est un tableau de pointeur. On a eu une boucle avec tableau[i] = malloc(...)
    5. équivalent de la précédente, mais tout aussi rare que la 2.

    D'une manière générale, on préfère p->champ à (*p).champ.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  15. #15
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    mars 2013
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mars 2013
    Messages : 26
    Points : 15
    Points
    15

    Par défaut

    Une bonne nuit de sommeil et tout est plus clair

    Je comprend ce qu'est un pointeur, mais je m'embrouille encore dans la logique de manipulation.

    Donc, j'ai trouvé, et compris, mon erreur.

    Dans l'appel de fonction depuis le main(), je faisais ceci fct_CoordBateau(dTailleBateau, size_Damier, &tDamier);En fait je passait l'adresse non pas du tableau (ou plutôt de son premier élément), mais l'adresse de la case mémoire qui contenait l'adresse du tableau (de son premier élément donc).
    Premier élément qui était lui même un pointeur vers le premier élément de mon deuxième tableau (dynamique), contenant lui même ma structure.

    Appel de fonction que je récupérais dans ma fonction comme ceci void fct_CoordBateau(int dTailleBateau, size_t size_Damier, stCaseDamier ***tDamier)Ce qui en soit est logique avec mon appel de fonction depuis le main, mais qui du coup n’était pas bon du tout par rapport a ce vers quoi je voulais pointer pour modifier ma structure dans la deuxième dimension de mon tableau (dynamique).

    Et donc la bonne écriture était bien tableau[i][j], tableau[i][j].structure, dans ma fonction, mais en enlevant le & dans l'appel de fonction depuis le main(), et une * dans le paramètre de ma fonction.
    En fait, il faut le considérer comme un tableau "classique".
    Ma confusion, venait du fait que j'utilisais un pointeur de pointeur pour y stocker l'adresse du tableau crée dans une autre fonction.. ce qui est logique d'utiliser un pointeur de pointeur déclare depuis le main puisque l'on va y stocker un pointeur qui lui même va pointeur sur l'adresse du premier élément du tableau (dynamique). Donc double pointeur déclarer depuis le main().

    Ensuite j'utilisais une structure, et je me suis dis, comme j'utilise une structure, il faut encore que je rajoute une couche de pointeur pour pointer (->) sur les éléments de celle ci.
    D’où le & (dans l'appel de fonction) et la 3ieme étoiles (dans les paramètres de la fonction), et le -> dans la fonction..

    Et je comprend maintenant pourquoi c'est faux.. (enfin, je suis presque sur de.. j'ai bon??)


    Et la mon prg fonctionne et je vais enfin pouvoir passer à la suite
    Mais pas avant d'avoir essayé ta proposition de tableau linéaire

    Merci pour le temps passer et tes explications



    edit : j'avais oublié de préciser d'enlever une etoile

  16. #16
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    4 899
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 4 899
    Points : 16 127
    Points
    16 127

    Par défaut

    Pour un peu plus de précision.
    Supposons un tableau déclaré par T tab[4];.
    • Il contient quatre T, dont le premier est accessible via tab[0].
    • La taille mémoire de tab est sizeof(tab) = 4 * sizeof(T). Attention, ceci n'est pas vrai pour un paramètre de fonction, les tableaux sont convertit en pointeurs, et leur dimension (la plus intérieure (la plus à gauche) est ignorée.
    • Le bloc mémoire contenant tab contient successivement ses éléments.
    • L'adresse du premier élément est la même que celle du tableau (il n'y a pas d'autre information en mémoire)
    • Le nom tab est implicitement convertible en adresse du bloc mémoire qu'il occupe, qui est aussi celle de son premier élément: T *p = tab; et T *p = &tab[0]; sont équivalent.

    Cette dernière propriété permet de transmettre facilement un tableau en argument, comme pointeur (le tableau n'est pas copié, seule l'adresse de son premier élément est transmise).

    Supposons un "tableau dynamique" défini par T* tab = malloc(4*sizeof(T));.
    • tab est un pointeur.
    • tab pointe un bloc mémoire de la taille de 4 T.
    • tab pointe vers des T: l'arithmétique de pointeurs se fera par blocs mémoire de la taille de T.
    • tab[i] est une facilité syntaxique pour *(tab+i). Il s'agit du T situé i T plus loin (en mémoire) que celui désigné par tab.
    • sizeof(tab) vaut la taille d'un pointeur, normal, c'est son type.
    • le 4 n'est disponible nul part pour le langage. (même si le compilateur ruse un peu, ce qui permet de faire un free correct)


    Dans l'appel de fonction depuis le main(), je faisais ceci fct_CoordBateau(dTailleBateau, size_Damier, &tDamier);.
    En fait je passait l'adresse non pas du tableau (ou plutôt de son premier élément), mais l'adresse de la case mémoire qui contenait l'adresse du tableau (de son premier élément donc).
    Premier élément qui était lui même un pointeur vers le premier élément de mon deuxième tableau (dynamique), contenant lui même ma structure.
    &tDamier est l'adresse de la variable tDamier, qui est un pointeur vers des pointeurs vers des structures
    C'est donc l'adresse d'un pointeur vers un bloc mémoire contenant des pointeurs vers des blocs mémoires contenant chacun des structures
    • le premier de &tDamier est le seul qui soit valable.
    • le premier élément de tDamier est bien un pointeur vers des structures
    • le premier élément du premier élément de tDamier est une de tes structures
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

Discussions similaires

  1. etrangeté dont je ne trouve pas la source.
    Par frp31 dans le forum Unix
    Réponses: 16
    Dernier message: 03/08/2011, 08h40
  2. Réponses: 4
    Dernier message: 17/09/2010, 22h37
  3. Réponses: 4
    Dernier message: 24/06/2009, 14h13
  4. [PostgreSQL] erreurs dont je ne trouve pas l'origine
    Par flo78 dans le forum PHP & SGBD
    Réponses: 6
    Dernier message: 20/01/2006, 13h38
  5. utiliser des classes dont on n'a pas le source
    Par kocin dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 28/08/2004, 16h05

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