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 :

Premières galères avec les tableaux et les pointeurs


Sujet :

C

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 3
    Points : 0
    Points
    0
    Par défaut Premières galères avec les tableaux et les pointeurs
    Bonjour, bonsoir,

    ayant depuis peu commencé à me familiariser avec le C, je suis arrivé récemment au sujet le plus "casse-tête" d'après les dires, à savoir les pointeurs. Et après quelques temps de galère, je dois avouer que je n'arrive toujours pas à trouver le bon de mon chemin. Mon problème est le suivant : j'aimerais créer une fonction qui va, à partir d'un argument, générer un tableau unidimensionnel, que j'aimerais ensuite pouvoir injecter à une autre fonction pour qu'elle puisse le modifier.

    Voici le schéma général (sans les include et tout ça) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    void fonction1 (int taille);
    void fonction2 (tab);
    
    int main (void)
    {
        int taille = 9;
        fonction1 (taille);
        
        fonction2 (tab);
        
        return EXIT_SUCCESS;
    }
    
    void fonction1 (int taille)
    {
        int tab[taille], i;
        for (i = 0; i < taille, i++)
        {
            tab[i] = i+1;
        }
    }
    
    void fonction2 (tab)
    {
        (...)
    }
    Voilà ce que ça donnerait schématiquement. J'ai mis en gras les zones du code qui me posent problème. En fait, l'élément qui me perturbe est la manière de retourner un tableau généré à partir d'une fonction (dans cet exemple, tab généré par fonction1), afin qu'une fonction que j'appelle ultérieurement à l'intérieur du main puisse y accéder et le modifier. De ce que j'ai compris de mes lectures sur le web, la solution est d'utiliser des pointeurs, mais là encore tout est confus dans ma tête : devrais-je créer un pointeur sur le tableau tab et le retourner ensuite, afin de pouvoir le glisser comme argument de fonction2 ? Ou bien est-ce que la démarche est complètement différente ?

    Si quelqu'un pouvait me donner une piste à ce sujet, je lui en serais éternellement reconnaissant.

    Cordialement

  2. #2
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 291
    Points : 4 941
    Points
    4 941
    Billets dans le blog
    5
    Par défaut
    Bonjour.

    La première chose a bien avoir en tête est que toute variable initialisée dans une fonction sera détruite de la fin d'exécution de celle-ci. Cette précision initiale étant écrite quelle solution ai-je à ma disposition ?

    • allouer de la mémoire en dehors de celle de la fonction.

    En partant de là les pointeurs font leur apparition pour pouvoir manipuler des adresses. Le prototype sera :

    1. void fonction1(int **tab);
    2. int *fonction1();

    Remarque : le prototype de la solution 1 fait appel à un double pointeur. L'explication est la suivante :un tableau en C est toujours transmis par son adresse. Ainsi les deux lignes de code suivantes pour afficher une donnée d'un tableau sont équivalentes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int tab[1];
     
    tab[0] = 10;
     
    printf("valeur de l'emplacement tab[0] : %d\n", tab[0]);
    printf("valeur de l'emplacement tab[0] : %d\n", tab);
    Donc si je désire transmettre une adresse d'un futur tableau il me faut transmettre l'adresse de l'adresse du futur tableau. Ca devient tordu .

    Cette première solution à l'avantage de pouvoir retourner par exemple un état de la création du tableau. Un retour de type booléen peut être une bonne idée.

    Pour la deuxième solution c'est plus facile à comprendre. On renvoie simplement l'adresse du bloc alloué.

    L'inconvénient, quelque soit la solution du prototype choisie, est qu'il faudra bien penser à libérer cette mémoire par free();. On touche ici au plus gros problème de ce langage : la gestion mémoire.

    Pour l'instant je ne te donne aucune ligne de code pour mettre en pratique la solution finale. N'hésites pas à revenir ici si les ennuis persistent .

    Voila un début de piste pour ton problème. En espérant t'aider un peu.

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 678
    Points
    13 678
    Billets dans le blog
    1
    Par défaut
    Je pense que la première chose à comprendre, c'est qu'un tableau et un pointeur, ce ne sont pas les mêmes choses. Les tableaux ressemblent aux pointeurs car le langage apporte beaucoup de confusion par des similarités d'écriture. Cela mène à des incompréhensions profondes tant qu'on a pas compris en quoi ils sont différents. Pour y voir plus clair, je te conseille de lire cet excellent article, écrit par Jean-Marc Bourget, membre (très) éclairé de ce forum : http://www.bourguet.org/v2/clang/arrayptr. Tu pourras aussi lire cet article (mais il est moins axé débutant que le premier), écrit par un autre membre (éclairé ?) : http://gradot.wordpress.com/2012/08/...ointeurs-en-c/

    Pour ton problème, la solution n'est pas un "vrai" tableau : c'est une zone allouée avec malloc. Tu renvoies le pointeur vers le début de cette zone, allouée dans ta première fonction. Dans la second, tu t'en sers.

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Gargok Voir le message
    Mon problème est le suivant : j'aimerais créer une fonction qui va, à partir d'un argument, générer un tableau unidimensionnel, que j'aimerais ensuite pouvoir injecter à une autre fonction pour qu'elle puisse le modifier.
    Bonjour

    Voici un exemple correspondant à ce que tu veux faire
    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
    int *create(int taille)
    {
        int *tableau;
        tableau=malloc(taille * sizeof (int));
        return tableau;
    }
     
    void remplir(int *tableau, int taille)
    {
        int i;
        for (i=0; i < taille; i++) tableau[i]=i*i;
    }
     
    int main()
    {
        int *tableau;
        int i;
        tableau=create(10);
        remplir(tableau, 10);
        for (i=0; i < 10; i++) printf("tableau[%d]=%d\n", i, tableau[i]);
        free(tableau);
    }

    Le vrai problème, évoqué par gerald3d, c'est que toute variable locale à une fonction disparait quand la fonction s'arrête. Et bien entendu on résiste à la tentation d'utiliser des globales qui, utilisées en vrac, apportent plus de soucis qu'elles n'en résolvent.

    Donc la fonction de création va commencer par réserver (malloc) de la mémoire dans ce qu'on nomme "le tas", c'est à dire une grosse zone de mémoire en vrac. Mais cette zone est une zone disponible pour tout le programme (et pas seulement pour une fonction particulière). Un peu comme quand chaque personne habitant une rue a le droit de garer sa voiture où il veut dans la rue. La "rue" c'est "le tas" et "les habitants" ce sont "les fonctions de ton programme".
    Et cette réserve allouée consistera en "n" éléments tous taillés pour stocker un int.

    malloc renvoie alors l'adresse du début de cette zone réservée et l'appelant la stocke précieusement. Et dans quoi la stocke-t-il ? Dans une variable taillée pour recevoir une adresse c'est à dire un pointeur. Et comme chaque élément pointé est un int, alors le pointeur est un "pointeur sur int" autrement dit un "int étoile" (ne pas oublier le mot "étoile" car il fait partie du type).
    Ta fonction "create()" a fini son travail, elle se contente alors de renvoyer elle-même cette adresse. Cette fonction étant du type qu'elle renvoie, elle est aussi de type "int étoile".

    Passons maintenant à ta fonction "remplir". Cette fonction devra recevoir le début de la zone à remplir, et aussi le nombre d'éléments à remplir. A toi de t'assurer que ce nombre ne dépasse pas le nombre initialement alloué car le C ne le vérifiera pas à ta place (sa philosophie c'est "pour aller le plus vite possible, il ne vérifie rien et c'est donc au programmeur de gérer").
    Et c'est là que la confusion évoquée par Bktero entre en jeu. Parce que comme la zone réservée est composée de cases contigües, tu as le droit d'utiliser cette adresse comme un tableau. C'est pour ça qu'on fait souvent amalgame entre "pointeur" et "tableau". Pour résumer, un pointeur est utilisable comme un tableau ; et l'adresse de départ d'un tableau pourra être conservée dans un pointeur... mais rien de plus (et surtout ne pas dire qu'un tableau est un pointeur sinon ).
    Donc cette fonction "remplir" a tout ce dont elle a besoin pour remplir les "n" cases de la zone réservée. Et comme cette fonction n'a pas besoin de renvoyer quoi que ce soit, elle est alors de type "void" (certains en profitent pour lui faire renvoyer une valeur indiquant que le travail a bien été fait mais ça ça reste libre choix du programmeur).

    Ton main, lui, se contente de commencer par créer le tableau et récupérer la zone créée. Théoriquement quand on commence à programmer de façon plus "professionnelle" on s'assure ici que la réservation a bien été effectuée et sinon on gère le soucis (généralement on envoie un message d'erreur et comme il n'y a rien à faire on quitte mais au-moins on quitte "proprement !!!).
    Ensuite il passe l'adresse de cette zone à la fonction de remplissage qui fait son travail. une fois ce travail fini, le main n'a plus qu'à afficher les valeurs remplies. Et il le fait de la même manière c'est à dire qu'il utilise l'adresse de départ comme un tableau.
    Et en final, il faut indiquer que la mémoire est redevenue utilisable pour d'autres en donnant l'ordre de la libérer via free(). Et c'est très sérieux. Sur certains vieux OS, la mémoire non libérée reste perdue jusqu'au reboot (un peu comme si le proprio d'une voiture garée dans la rue perdait la clef de la voiture => la place reste occupée définitivement).

    Accessoirement tu remarqueras que la fonction "create()" ne faisant rien de plus qu'un malloc(), on peut alors l'oublier et remplacer tableau=create(10) par tableau=malloc(10 * sizeof (int)).

    Bon courage. Quand tu auras compris qu'un pointeur ce n'est qu'une simple adresse et que tu seras capable, pendant que tu écris ton code, de garder à l'esprit à quoi correspond cette adresse, t'auras réglé ton soucis de compréhension

    PS: tu remarqueras peut-être que certaines variables ont le même nom (tableau dans main(), tableau dans create(), i dans remplir(), i dans main()). Cela ne pose absolument aucun soucis car chaque variable est "isolée" dans sa fonction.
    Bien entendu la valeur de la variable "tableau" du main() qui sera stockée dans la variable "tableau" de "remplir()" est la même (et ça ce n'est pas un hasard) mais justement l'avantage des variables "locales" est de pouvoir 1) leur donner le même nom dans diverses fonctions et 2) pouvoir leur donner des noms en rapport avec leur but ou leur utilisation ; et l'avantage de ne pas avoir de "globales" est de ne pas avoir à se soucier des noms qu'on donne aux locales.
    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]

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 678
    Points
    13 678
    Billets dans le blog
    1
    Par défaut
    J'aime bien l'analogie du tas avec la rue pour les voitures ^^

    A noter quand même que la variable s'appelle "tableau", mais elle n'est pas de type "tableau". Ca reste un pointeur contenant l'adresse d'une zone allouée par malloc() et qu'on manipule comme un tableau.

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    A noter quand même que la variable s'appelle "tableau", mais elle n'est pas de type "tableau". Ca reste un pointeur contenant l'adresse d'une zone allouée par malloc() et qu'on manipule comme un tableau.
    et 2) pouvoir leur donner des noms en rapport avec leur but ou leur utilisation
    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
    Nouveau Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 3
    Points : 0
    Points
    0
    Par défaut
    Woaw, merci beaucoup pour vos réponses très détaillées ! J'ai essayé de m'esquisser tout ce que vous m'avez dit sur un petit schéma et tout semble enfin se mettre en ordre !

    Dernière petite question, par curiosité : est-ce que cette démarche s'applique aussi aux tableaux de dimension supérieure à 1 ? Dans le cas d'une matrice par exemple, est-ce que l'allocation de mémoire via malloc ou le passage en argument de fonctions se déroule de la même manière ?

    Encore merci !

  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 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Gargok Voir le message
    Dernière petite question, par curiosité : est-ce que cette démarche s'applique aussi aux tableaux de dimension supérieure à 1 ? Dans le cas d'une matrice par exemple, est-ce que l'allocation de mémoire via malloc ou le passage en argument de fonctions se déroule de la même manière ?
    Plus ou moins. Imaginons qu'on veuille stocker des int sur une matrice de nb_lig et nb_col. On commencera alors par définir un int **tableau (adresse non pas d'un int mais d'une case contenant, elle, l'adresse d'un int) qu'on alloue de cette façon: tableau=malloc(nb_lignes * sizeof(int *)). Ensuite, chaque tableau[i] devra être alloué pour stocker les nb_colonnes de int ce qui donnera tableau[i]=malloc(nb_colonnes * sizeof(int)). Tout en devant en plus toujours gérer le cas où ne serait-ce qu'un seul malloc() échouerait. Et en fin de travail on libère dans l'ordre inverse: d'abord chaque tableau[i] puis enfin tableau. Le même schéma peut ensuite s'appliquer à 3, 4, 5, 10 dimensions mais je n'ai jamais vu au delà de 3 (après ça devient autant difficile à conceptualiser qu'à programmer)...

    Ou alors on part dans une optique complètement différente: on considère la matrice comme une seule et unique suite d'int. Un peu comme dans un jeu d'échecs où les cases de A1 jusqu'à H8 peuvent se numéroter aussi de 1 à 64. Dans ce cas, on reste en une dimension avec un int *tableau auquel on ira allouer tableau=malloc(nb_lig * nb_col * sizeof (int)) ce qui nous donne seul et unique malloc() à gérer. Ensuite suffira de convertir chaque position théorique [i][j] en index [i * nb_col + j] et inversement, c'est à dire que si on a un index [x], alors [i] sera égal à x / nb_col et [j] sera égal à x % nb_col (à noter par ailleurs que c'est comme ça que fonctionne la mémoire qui, elle, reste toujours en une dimension). Et en fin de travail, un seul et unique free(). D'un coté on simplifie la gestion des allocations mais on complexifie la gestion des accès.
    Et bien entendu on peut étendre le principe à 3, 4, n dimensions.
    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
    Nouveau Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 3
    Points : 0
    Points
    0
    Par défaut
    Et lorsque l'on veut accéder à un élément précis d'un tableau à deux dimensions définis ainsi, faut-il aussi s'y prendre avec des pointeurs ?

    Si je prends par exemple la fonction suivante :

    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
    int main()
    {
        int i, j;
        int nbL, nbC;
        int tab[nbL][nbC];
     
       for (i = 0; i < nbL; i++)
       {
           for (j = 0; j < nbC; j++)
           {
               tab[i][j] = i + j;
           }
       }
     
       return 0;
    }
    Dans ce cas-là, si les choses sont bien claires dans mon esprit, tab sera composé de pointeurs pointant sur d'autres pointeurs qui, à leur tour, pointent sur des int (pas facile, tout ça ). Dans ce cas-là, dois-je considérer mes variables compteurs (à savoir i et j) comme des pointeurs qui vont successivement être dirigés sur une ligne / colonne ?

    Merci beaucoup pour toutes vos réponses, cette histoire de pointeurs prend une forme de plus en plus précise dans mon esprit !

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Gargok Voir le message
    Dans ce cas-là, si les choses sont bien claires dans mon esprit, tab sera composé de pointeurs pointant sur d'autres pointeurs qui, à leur tour, pointent sur des int (pas facile, tout ça ).
    Hé justement non !!! C'est là le piège !!! Tu as écrit int tab[nbL][nbC] donc là tu as un tableau. Et comme on te l'a dit, un tableau ce n'est pas un pointeur même si tab (qui équivaut à l'adresse de son premier élément) peut être stockée dans un pointeur...

    Citation Envoyé par Gargok Voir le message
    Dans ce cas-là, dois-je considérer mes variables compteurs (à savoir i et j) comme des pointeurs qui vont successivement être dirigés sur une ligne / colonne ?
    Ah là non. i et j sont des nombres qui servent d'indice dans les coordonnées. Mais tu peux remplacer ces indices par des pointeurs...
    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
    #include <stdio.h>
    #include <string.h>
     
    int main()
    {
    	int i, j;
    	int nbL=5, nbC=6;
    	int tab[nbL][nbC];
    	int (*ptL)[nbC];
    	int *ptC;
     
    	for (i=0, ptL=tab; i < nbL; i++, ptL++)
    	{
    		for (j=0, ptC=(*ptL); j < nbC; j++, ptC++)
    			*ptC = i + j;
    	}
     
    	for (i=0, ptL=tab; i < nbL; i++, ptL++)
    	{
    		for (j=0, ptC=(*ptL); j < nbC; j++, ptC++)
    			printf("%d ", *ptC);
    		fputc('\n', stdout);
    	}
    	return 0;
    }

    Et les performances sont meilleures car taper dans un pointeur pointant vers la bonne case va plus vite que demander au C de recalculer à chaque itération la case correspondant à la position [i][j] surtout si cette écriture tab[i][j] revient plusieurs fois dans le code...
    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
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Plus ou moins. Imaginons qu'on veuille stocker des int sur une matrice de nb_lig et nb_col. On commencera alors par définir un int **tableau (adresse non pas d'un int mais d'une case contenant, elle, l'adresse d'un int) qu'on alloue de cette façon: tableau=malloc(nb_lignes * sizeof(int *)). Ensuite, chaque tableau[i] devra être alloué pour stocker les nb_colonnes de int ce qui donnera tableau[i]=malloc(nb_colonnes * sizeof(int)). Tout en devant en plus toujours gérer le cas où ne serait-ce qu'un seul malloc() échouerait. Et en fin de travail on libère dans l'ordre inverse: d'abord chaque tableau[i] puis enfin tableau. Le même schéma peut ensuite s'appliquer à 3, 4, 5, 10 dimensions mais je n'ai jamais vu au delà de 3 (après ça devient autant difficile à conceptualiser qu'à programmer)...

    Ou alors on part dans une optique complètement différente: on considère la matrice comme une seule et unique suite d'int. Un peu comme dans un jeu d'échecs où les cases de A1 jusqu'à H8 peuvent se numéroter aussi de 1 à 64. Dans ce cas, on reste en une dimension avec un int *tableau auquel on ira allouer tableau=malloc(nb_lig * nb_col * sizeof (int)) ce qui nous donne seul et unique malloc() à gérer. Ensuite suffira de convertir chaque position théorique [i][j] en index [i * nb_col + j] et inversement, c'est à dire que si on a un index [x], alors [i] sera égal à x / nb_col et [j] sera égal à x % nb_col (à noter par ailleurs que c'est comme ça que fonctionne la mémoire qui, elle, reste toujours en une dimension). Et en fin de travail, un seul et unique free(). D'un coté on simplifie la gestion des allocations mais on complexifie la gestion des accès.
    Et bien entendu on peut étendre le principe à 3, 4, n dimensions.
    On peut aussi faire une combinaison des deux, avec une matrice contigüe de nb_lig*nb_col entiers, et un tableau de nb_lig pointeurs
    qu'on fait pointer aux bons endroits de la matrice. Cela combine l'avantage de simplifier la gestion des allocations (on passe à une allocation par dimension) tout en gardant les accès faciles par pointeur.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Ca ne m'était jamais venu à l'idée
    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]

  13. #13
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    J'ai un exemple pour ce genre d'allocation, dans le cas d'une matrice de doubles:
    AutreAllocTable2D()
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2014
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 4
    Points : 5
    Points
    5
    Par défaut
    Citation Envoyé par gerald3d Voir le message
    Bonjour.

    La première chose a bien avoir en tête est que toute variable initialisée dans une fonction sera détruite de la fin d'exécution de celle-ci. Cette précision initiale étant écrite quelle solution ai-je à ma disposition ?

    • allouer de la mémoire en dehors de celle de la fonction.

    En partant de là les pointeurs font leur apparition pour pouvoir manipuler des adresses. Le prototype sera :

    1. void fonction1(int **tab);
    2. int *fonction1();

    Remarque : le prototype de la solution 1 fait appel à un double pointeur. L'explication est la suivante :un tableau en C est toujours transmis par son adresse. Ainsi les deux lignes de code suivantes pour afficher une donnée d'un tableau sont équivalentes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int tab[1];
     
    tab[0] = 10;
     
    printf("valeur de l'emplacement tab[0] : %d\n", tab[0]);
    printf("valeur de l'emplacement tab[0] : %d\n", tab);
    Donc si je désire transmettre une adresse d'un futur tableau il me faut transmettre l'adresse de l'adresse du futur tableau. Ca devient tordu .

    Cette première solution à l'avantage de pouvoir retourner par exemple un état de la création du tableau. Un retour de type booléen peut être une bonne idée.

    Pour la deuxième solution c'est plus facile à comprendre. On renvoie simplement l'adresse du bloc alloué.

    L'inconvénient, quelque soit la solution du prototype choisie, est qu'il faudra bien penser à libérer cette mémoire par free();. On touche ici au plus gros problème de ce langage : la gestion mémoire.

    Pour l'instant je ne te donne aucune ligne de code pour mettre en pratique la solution finale. N'hésites pas à revenir ici si les ennuis persistent .

    Voila un début de piste pour ton problème. En espérant t'aider un peu.
    Bonsoir .
    Je suis un débutant en langage C mais si je ne m'abuse l'instruction : printf("valeur de l'emplacement tab[0] : %d\n", tab[0]); N'est pas la même que printf("valeur de l'emplacement tab[0] : %d\n", tab); Puisque tab[0] correspond à la valeur de l'élément d'indice 0 du tableau tab , n'aurait il pas fallut écrire &tab[0] plutôt ?

  15. #15
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 678
    Points
    13 678
    Billets dans le blog
    1
    Par défaut
    J'ai envie de dire "oui"

  16. #16
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Et le bon format pour afficher une adresse mémoire est %p, et non pas %d.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Paddington Voir le message
    l'instruction : printf("valeur de l'emplacement tab[0] : %d\n", tab[0]) N'est pas la même que printf("valeur de l'emplacement tab[0] : %d\n", tab).
    Tout à fait exact. tab[0] n'est pas du tout la même chose que tab...

    Citation Envoyé par Paddington Voir le message
    n'aurait il pas fallut écrire &tab[0] plutôt ?
    Encore exact. Si on veut l'adresse de son premier élément (et l'intitulé du message laisse supposer que c'est le cas), c'est ce qu'il aurait fallu écrire. Mais t'as aussi le droit d'utiliser l'équivalence &tab[x] = tab + x et écrire alors tab...
    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]

  18. #18
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Une autre adresse intéressante serait &tab, qui aura la même valeur que tab ou &tab[0] mais pas le même type (&tab est de type int (*)[1], ce qui signifierait int[1]* si le compilo acceptait une telle syntaxe).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  19. #19
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 631
    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 631
    Points : 30 865
    Points
    30 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    &tab, qui aura la même valeur que tab
    Je ne sais pas si &tab a vraiment la même valeur que tab (indépendamment de ce que printf() affiche).
    Il me semble qu'on en a déjà parlé sur le forum et que l'une des réponses avait été que "&tab" n'ayant aucune signification était alors automatiquement rectifié par le compilo en "tab" et qu'un printf("%d", &tab) était alors automatiquement rectifié en printf("%d", tab) donnant alors l'illusion que les valeurs sont les mêmes alors qu'en fait ce n'est pas le cas. Mais c'est très lointain dans ma mémoire et je ne suis pas certain de reproduire ici l'exactitude de ce qui avait été dit à ce moment là...
    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]

  20. #20
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    La faille dans ce raisonnement, c'est que &tab a une signification, puisque je peux faire ceci:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int tab[10];
    int (*pTab)[10] = &tab; //équivaudrait à int[10]* pTab = &tab;
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

Discussions similaires

  1. les tableaux et le pointeur !prob
    Par t_zano dans le forum Débuter
    Réponses: 10
    Dernier message: 28/07/2009, 21h09
  2. Problème avec les tableaux et les structures
    Par al-khawarrizmi dans le forum Débuter
    Réponses: 2
    Dernier message: 10/06/2008, 17h17
  3. [Listeners] Les listeners et les tableaux d'objets
    Par TheDoci dans le forum AWT/Swing
    Réponses: 5
    Dernier message: 29/06/2007, 10h43
  4. les tableaux et les colonnes
    Par imsse dans le forum C#
    Réponses: 4
    Dernier message: 22/05/2007, 17h45

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