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 :

taille maximale d'un tableau à 3 dimensions?


Sujet :

C

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut taille maximale d'un tableau à 3 dimensions?
    Bonjour cher communauté

    J'essaye d'écrire un bout de code qui "m'extrait" tous les trigrammes (trois lettres consécutives) d'une (longue) liste de mots.
    L'idée c'était de déclarer un tableau à 3 dimension, et que lorsqu'on rencontre par exemple la suite de caractères 'a' 'b' 'c' dans la liste on fait:
    tab['a']['b']['c']++; ainsi en consultant le tableau je peux savoir que "abc" est présent 1 fois (je transforme les caractères en unsigned avant pour ne pas avoir tab[-1][][] par exemple)...
    Mon problème:
    Windows n'aime pas du tout int tab[256][256][256]={{{}}};, j'imagine que c'est une question de taille/mémoire?
    Je débute en C, et je vois pas de solutions (j'ai réduit le tableau à tab[50][50][50] pour stocker uniquement les caractères qui m'intéressent, mais ça n'a rien changé).
    Si vous avez des idées pour résoudre ce problème de mémoire, ou une meilleure façon de stocker les données (structure?, malloc?), je suis preneur!!!

    merci

  2. #2
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Tu ne décris pas précisément ton erreur. Montre du code. Que se passe-t-il ?

    256 int au cube ça fait 4 à 8*16 MiB, c'est trop pour la pile (on peut s'en assurer avec ulimit -s). Ça passe en variable globale mais tu perds de la place : tu n'as besoin que des caractères alphabétiques a-z si tu te limites à l'ASCII. 50 int au cube c'est pas énorme, ça devrait passer sur la pile (et c'est encore un peu large).

    Tu ne feras pas l'économie d'une conversion de char vers size_t, ne serait-ce que pour rejeter les caractères qui ne sont pas dans la plage traitée, convertir les majuscules, etc..

    Ton idée est bonne, c'est probablement sa mise en œuvre qui pèche.

  3. #3
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 : 5 202
    Par défaut
    Un malloc pointeur pourrait convenir, puisque tu déporterais la mémoire utilisée dans le tas.
    Penses toutefois à "linéariser" ton tableau.
    Tu peux allouer malloc(N*N*N*sizeof(int)), et utiliser pointeur résultant ainsi: tab[c1 + N*c2 + N*N*c3]. avec N un const int.

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut
    Merci pour les réponses, je teste ça...

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut
    Bon... ça marche pô

    j'ai essayé ça (pour expérimenter):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        int tab[256][256][256]= {{{}}};
        tab[255][255][255] = 42;
        printf("%d", tab[255][255][255]);
    }
    évidemment ça ne marche pas...
    mais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include <stdio.h>
    #include <stdlib.h>
    int tab[256][256][256]= {{{}}};
    int main()
    {
        tab[255][255][255] = 42;
        printf("%d", tab[255][255][255]);
    }
    marche sans soucis, donc merci pour l'astuce Matt_Houston (j'ai pas bien compris pourquoi par contre...)

    Mais quand j'utilise ça dans mon code ça ne marche pas quand même (peut-être que l'erreur viens d'ailleurs!).
    Je vous mets les lignes concernées: (je pense que vous allez trouvez ça moche et illisible, n'hésitez pas à critiquer )

    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
    int caracteresATraiter(const char c)
    {
        if(c >= 'a' && c <= 'z')
        {
            return TRUE;
        }
        else if(c >= 'A' && c <= 'Z')
        {
            return TRUE;
        }
        else if(    c=='â' || c=='ê' || c=='î' || c=='ô' || c=='û'
                 || c=='ä' || c=='ë'|| c=='ï' || c=='ö' || c=='ü'
                 || c=='Â' || c=='Ê'|| c=='Î' || c=='Ô' || c=='Û'
                 || c=='Ä' || c=='Ë'|| c=='Ï' || c=='Ö' || c=='Ü'
                 || c=='é' || c=='è'|| c=='ù' || c=='à' || c=='ç'
                 || c=='-' || c=='æ' || c=='œ'|| c=='Æ' || c=='Œ')
     
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    et puis ça aussi:
    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    void extraireTrigrammes(FILE *output, FILE *trigra)
    {
        unsigned char conversionAvers0[TAILLE_TAB] = {}; // je remap les caractères qui m'intéressent pour pouvoir réduire mon tableau
        unsigned char conversion0versA[TAILLE_TAB] = {}; 
        unsigned char c = 'a', c1, c2, c3;
        int i, j, k;
     
        // Initialisation des tableaux de conversion: --------------------------
        j=0;
        for(i=0;i<255;i++) // conversion de 'a' vers 0
        {                  
            if(caracteresATraiter(i)) // si on tombe sur un caractère intéressant
            {
                conversionAvers0[i]=j; // conversion['a'] = 0, conversion['b'] = 1...
                j++;
            }
        }
        conversionAvers0['\n'] = j; // je traite \n à part car ça m'embête de le mettre dans caractereATraiter (j'ai d'autres fonctions qui l'utiilisent)
     
        j=0;
        for(i=0;i<256;i++) // conversion de 0 vers 'a'
        {
            if(caracteresATraiter(i) && !isupper(i))
            {
                conversion0versA[j] = i;
                j++;
            }
        }
        conversion0versA[j] = ' '; j++;
        // Fin Initialisation des tableaux de conversion: -----------------------
     
        // EXTRACTION
        int trigrammes[TAILLE_TAB][TAILLE_TAB][TAILLE_TAB];
        fseek(output, 0, SEEK_SET);
        while(c != 255) // On parcourt tout le fichier
        {
            c = (c1=fgetc(output));
            if(c != '\n' && c != 255)
            {
                c = (c2=fgetc(output));
                while(c != '\n' && c != 255)
                {
                    c = (c3=fgetc(output));
                    if(c != '\n' && c != 255)
                    {
                        trigrammes[conversionAvers0[c1]][conversionAvers0[c2]][conversionAvers0[c3]]++;
                        c1 = c2;
                        c2 = c3;
                    }
                }
             }
        }
     
     
        // Stockage dans trigra.txt...
        fseek(trigra, 0, SEEK_SET);
     
        for(i=0;i<TAILLE_TAB;i++)
        {
            fprintf(trigra, "\n");
            for(j=0;j<TAILLE_TAB;j++)
            {
                fprintf(trigra, "\n");
                for(k=0;k<TAILLE_TAB;k++)
                {
                    if(trigrammes[i][j][k])
                    {
                        fprintf(trigra, "%c%c%c:%5d |",conversion0versA[i], conversion0versA[j], conversion0versA[k], trigrammes[i][j][k]);
                    }
     
                }
            }
        }
     
     
    }
    Dans mon .h j'ai ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #define TAILLE_TAB 84 // Elle dépend du nombre de caractère différents qu'on va traiter!
    #define TRUE 1
    #define FALSE 0
     
    extern int trigrammes[TAILLE_TAB][TAILLE_TAB][TAILLE_TAB];
    voila voila, désolé si c'est pas propre, c'est la 1ère fois que je poste un code...

  6. #6
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Citation Envoyé par Janosch89 Voir le message
    Bon... ça marche pô
    [...]
    évidemment ça ne marche pas...
    [...]
    ça ne marche pas quand même
    Mais encore ? Décris précisément les erreurs rencontrées, nous sommes des êtres humains.


    Citation Envoyé par Janosch89 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int tab[256][256][256]= {{{}}};
    Cette syntaxe d'initialisation n'est pas permise en C, compile avec -pedantic -Werror (je suppose GCC / Clang) pour recevoir : error: ISO C forbids empty initializer braces. Remplace cela par {{{ 0 }}}, ou rien du tout si le tableau est global (les variables globales sont initialisées à zéro).

    D'une manière générale active tous les avertissements : -std=c11 -pedantic -Wall -Wextra .


    Citation Envoyé par Janosch89 Voir le message
    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
    int caracteresATraiter(const char c)
    {
        if(c >= 'a' && c <= 'z')
        {
            return TRUE;
        }
        else if(c >= 'A' && c <= 'Z')
        {
            return TRUE;
        }
        else if(    c=='â' || c=='ê' || c=='î' || c=='ô' || c=='û'
                 || c=='ä' || c=='ë'|| c=='ï' || c=='ö' || c=='ü'
                 || c=='Â' || c=='Ê'|| c=='Î' || c=='Ô' || c=='Û'
                 || c=='Ä' || c=='Ë'|| c=='Ï' || c=='Ö' || c=='Ü'
                 || c=='é' || c=='è'|| c=='ù' || c=='à' || c=='ç'
                 || c=='-' || c=='æ' || c=='œ'|| c=='Æ' || c=='Œ')
     
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    Attention, ce programme ne fonctionnera qu'avec des fichiers encodés en 8859-1 (exit l'UTF-8) et encore partiellement : bien qu'il n'occupe qu'un seul octet dans cet encodage le symbole œ par exemple est composé de deux glyphes et œu est donc un trigramme à part entière, contrairement à œuf.

    À ta place j'écrirais une fonction qui prend un char et retourne une valeur d'index de type size_t qu'on pourrait utiliser directement pour adresser les cases du tableau. Quelque chose du style de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #define TABLE_SIZE ((size_t)26)
    size_t occurrences[TABLE_SIZE][TABLE_SIZE][TABLE_SIZE];
     
    #define INDEX_INVALID ~((size_t)0) // 0xffffff..
     
    // ...
     
        if ((a = char_to_index(c[0])) != INDEX_INVALID
         && (b = char_to_index(c[1])) != INDEX_INVALID
         && (c = char_to_index(c[2])) != INDEX_INVALID) {
            ++occurrences[a][b][c];
     
            // ...

    Citation Envoyé par Janosch89 Voir le message
    marche sans soucis, donc merci pour l'astuce Matt_Houston (j'ai pas bien compris pourquoi par contre...)
    Le tableau du premier programme est déclaré au sein d'une fonction, donc dit à portée locale et alloué sur la pile d'exécution dont la taille est limitée (on peut supposer qu'on dispose de 2 MiB ou plus sur un système relativement récent, mais ce n'est pas garanti). Le tableau du second programme est déclaré en tant que variable à portée globale et à ce titre alloué au démarrage du programme à un emplacement immuable pour la durée de l'exécution.

  7. #7
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut
    Cool, pleins d'infos! merci

    J'ai les pâtes qui sont en train de déborder, donc pas trop le temps de répondre, je regarde tout ça ce soir (nuit?)...

    Pour être plus précis sur l'erreur:
    à partir d'une certaine taille pour TAILLE_TAB (autour de 41 je crois...) mon fichier trigra est vide.
    En dessous, tout se passe bien.

    edit: j'utilise la sdl (GUI), donc j'ai pas de console, et donc pas de message d'erreur.
    mais je vais passer en console, ça va être plus simple je pense...

  8. #8
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut
    J'ai activé -std=c11 -pedantic -Wall -Wextra ,merci pour le tuyau, c'est bien pratique.

    Le tableau du premier programme est déclaré au sein d'une fonction, donc dit à portée locale et alloué sur la pile d'exécution dont la taille est limitée (on peut supposer qu'on dispose de 2 MiB ou plus sur un système relativement récent, mais ce n'est pas garanti). Le tableau du second programme est déclaré en tant que variable à portée globale et à ce titre alloué au démarrage du programme à un emplacement immuable pour la durée de l'exécution.
    Ok, mais y'a une limite de taille même pour les variables globales j'imagine?

    1

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #define TABLE_SIZE ((size_t)26)
    size_t occurrences[TABLE_SIZE][TABLE_SIZE][TABLE_SIZE];
     
    #define INDEX_INVALID ~((size_t)0) // 0xffffff..
     
    // ...
     
        if ((a = char_to_index(c[0])) != INDEX_INVALID
         && (b = char_to_index(c[1])) != INDEX_INVALID
         && (c = char_to_index(c[2])) != INDEX_INVALID) {
            ++occurrences[a][b][c];
     
            // ...
    Là j'avoue que je suis un peu perdu (alerte débutant!) ...
    Voila ce que je pense avoir compris:
    #define TABLE_SIZE ((size_t)26) définit la taille d'un tableau d'indice_max 25 ...
    size_t c'est pour pas s'embrouiller avec signed / unsigned .

    #define INDEX_INVALID ~((size_t)0) lô pô compris... INDEX_INVALID vaut oxffffffff, ok mais pour quoi faire?
    Ma fonction char_to_index() doit retourner ~((size_t)0) si le caractère n'est pas valide, c'est ça?

    a b et c ce sont trois caractères

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if ((a = char_to_index(c[0])) != INDEX_INVALID
         && (b = char_to_index(c[1])) != INDEX_INVALID
         && (c = char_to_index(c[2])) != INDEX_INVALID) {
            ++occurrences[a][b][c];
    ça j'ai compris (à part le INDEX_INVALID)
    (me dit pas que toutes mes suppositions sont fausses, sinon je me mets au jardinage ^^)

    Sinon ça résoud toujours pas mon problème j'arrive pas à m'expliquer pourquoi rien n'est écrit dans le fichier trigra quand je fais
    #define TAILLE_TAB 42, alors qu'avec #define TAILLE_TAB 37 ça marche

  9. #9
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Citation Envoyé par Janosch89 Voir le message
    Ok, mais y'a une limite de taille même pour les variables globales j'imagine?
    Une limite bien plus élevée mais qui dépend du système et qu'on ne peut donc garantir. Le souci avec cette méthode est qu'on ne peut en aucun cas traiter une erreur d'allocation : l'OS refusera purement et simplement de démarrer le programme. Il est donc en général préférable de réaliser l'allocation sur le tas (via malloc).

    Le cas présent passe à l'aise sur un ordinateur moderne avec un OS moderne, on ne s'en préoccupe donc pas pour des raisons de simplicité. Cela dit, si cette fonction doit être incluse au sein d'un programme de plus grande envergure (tu causes de SDL) il faudra se reposer la question.



    Citation Envoyé par Janosch89 Voir le message
    Là j'avoue que je suis un peu perdu (alerte débutant!) ...
    [...]
    (me dit pas que toutes mes suppositions sont fausses, sinon je me mets au jardinage ^^)
    Ben non, tu as presque tout saisi.

    On choisit une valeur non atteignable en pratique pour représenter une valeur d'index invalide, que la fonction de conversion peut renvoyer afin de nous informer que l'opération n'a pu avoir lieu (le caractère ne fait pas partie de l'alphabet attendu). On aurait pu utiliser zéro, mais on s'en sert pour adresser les premiers éléments des sous-tableaux, il aurait alors fallu retrancher 1 aux valeurs d'index valides. On aurait tout aussi bien pu choisir 0xfe321acd ou 0xdeadbeef..

    size_t (<- clic !) est un type entier non signé (retourné entre autres par l'opérateur sizeof) qui offre plus de garanties qu'un simple int. C'est le type idéal pour faire de l'adressage et manipuler des buffers.


    Citation Envoyé par Janosch89 Voir le message
    Sinon ça résoud toujours pas mon problème j'arrive pas à m'expliquer pourquoi rien n'est écrit dans le fichier trigra quand je fais
    #define TAILLE_TAB 42, alors qu'avec #define TAILLE_TAB 37 ça marche
    Difficile à dire sans devoir considérer le programme complet. Essaie d'allouer ton tableau sur le tas pour voir si ça change quelque chose. Fais-le comme ternel te le recommande, à une seule dimension : les tableaux multi-dimensionnels sur le tas, c'est mal.

  10. #10
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut
    Merci encore d'avoir pris le temps de répondre!

    J'ai pas encore saisi d'où venait l'erreur, mais je vais réécrire la partie concernée, maintenant que j'ai des pistes
    Il semblerait aussi que je ne peux plus fuir les malloc ...
    Pas sûr qu'attaquer les malloc direct avec un tableau multidimensionnel soit une bonne idée par contre,
    parce que je suis sûr que ternel me donne de bons conseils, par contre je comprends rien lol. Je vais aller rouvrir mes bouquins...
    Si je m'en sors pas, je repasserai ici
    En attendant je mets résolu

  11. #11
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Ce que ternel te fait remarquer, c'est qu'un tableau unidimensionnel de N puissance n éléments (int t[N * N... * N]) est équivalent à un tableau à n (ici 3) dimensions de cardinalité N (int t[N][N]...[N]).

    Le hic c'est que si pour des tableaux statiques, l'arrangement en mémoire et donc l'adressage (la manière d'accéder aux éléments) sont identiques que l'on soit en présence d'une ou plusieurs dimensions, ce n'est pas le cas pour un buffer alloué sur le tas (ou pour l'adresse d'un tableau dégénérée en pointeur). I.e.: on ne peut pas l'adresser via [i][j]...[u] (pas sans réaliser 1 + N^(n - 1) allocations distinctes). Afin de se simplifier la vie (et les perfs), il convient dans ce cas d'oublier l'adressage multi-dimensionnel.

  12. #12
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 : 5 202
    Par défaut
    En version plus légère, machin[1][2] n'est clair que si le compilateur connait le nombre d'élément des machin[...]. Ce qui n'est pas le cas du tout si machin est un int**.

    Pour être tout a fait précis, le compilateur fait lui-même la traduction des différentes indexations (les crochets) en un décalage par rapport au premier élément, et il ne peut y arriver qu'avec les dimensions exactes de chaque niveau de tableau. (sauf éventuellement la première, c'est à dire celle qui contient toutes les autres.

  13. #13
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 485
    Par défaut
    Citation Envoyé par ternel Voir le message
    Un malloc pointeur pourrait convenir, puisque tu déporterais la mémoire utilisée dans le tas.
    Penses toutefois à "linéariser" ton tableau.
    Tu peux allouer malloc(N*N*N*sizeof(int)), et utiliser pointeur résultant ainsi: tab[c1 + N*c2 + N*N*c3]. avec N un const int.
    Citation Envoyé par Matt_Houston Voir le message
    Ce que ternel te fait remarquer, c'est qu'un tableau unidimensionnel de N puissance n éléments (int t[N * N... * N]) est équivalent à un tableau à n (ici 3) dimensions de cardinalité N (int t[N][N]...[N]). Le hic c'est que si pour des tableaux statiques, l'arrangement en mémoire et donc l'adressage (la manière d'accéder aux éléments) sont identiques que l'on soit en présence d'une ou plusieurs dimensions, ce n'est pas le cas pour un buffer alloué sur le tas (ou pour l'adresse d'un tableau dégénérée en pointeur).
    Il y a peut-être quelque chose que j'ai raté mais il me semble que c'est faux : il est tout-à-fait possible de déclarer un pointeur sur un tableau en spécifiant sa géométrie et, de là, allouer dynamiquement son espace avec un malloc(). C'est juste que la syntaxe de la définition est un peu délicate :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <stdlib.h>
    #include <stdio.h>
     
    int main (void)
    {
        int i,j,k;
        int (*tab)[3][4];
     
        tab = (int(*)[3][4])malloc(2*3*4*sizeof (int)); /* On "ignore" ici la plus grande des dimensions [2][x][x] */
     
        printf ("tab = %p ; &tab = %p\n",(void *)tab,(void *)&tab);
        printf ("Taille d'un entier : %lu\n",sizeof (int));
     
        for (i=0 ; i<2 ; i++)
        for (j=0 ; j<3 ; j++)
        for (k=0 ; k<4 ; k++)
        printf ("Adresse de tab[%d][%d][%d]: %p\n",
                i,j,k,
                (void *)&(tab[i][j][k]));
     
        free (tab);
        return 0;
    }

    Ce qui donne :

    Code Shell : 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
    $ ./tableau
    tab = 0x13b0010 ; &tab = 0x7fffcfef0758
    Taille d'un entier : 4
    Adresse de tab[0][0][0]: 0x13b0010
    Adresse de tab[0][0][1]: 0x13b0014
    Adresse de tab[0][0][2]: 0x13b0018
    Adresse de tab[0][0][3]: 0x13b001c
    Adresse de tab[0][1][0]: 0x13b0020
    Adresse de tab[0][1][1]: 0x13b0024
    Adresse de tab[0][1][2]: 0x13b0028
    Adresse de tab[0][1][3]: 0x13b002c
    Adresse de tab[0][2][0]: 0x13b0030
    Adresse de tab[0][2][1]: 0x13b0034
    Adresse de tab[0][2][2]: 0x13b0038
    Adresse de tab[0][2][3]: 0x13b003c
    Adresse de tab[1][0][0]: 0x13b0040
    Adresse de tab[1][0][1]: 0x13b0044
    Adresse de tab[1][0][2]: 0x13b0048
    Adresse de tab[1][0][3]: 0x13b004c
    Adresse de tab[1][1][0]: 0x13b0050
    Adresse de tab[1][1][1]: 0x13b0054
    Adresse de tab[1][1][2]: 0x13b0058
    Adresse de tab[1][1][3]: 0x13b005c
    Adresse de tab[1][2][0]: 0x13b0060
    Adresse de tab[1][2][1]: 0x13b0064
    Adresse de tab[1][2][2]: 0x13b0068
    Adresse de tab[1][2][3]: 0x13b006c

    On obtient donc bien un tableau proprement indexé et qui s'utilise avec la syntaxe habituelle. Il n'y vraiment pas de raison de s'en priver.

  14. #14
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 : 5 202
    Par défaut
    C'est un pointeur de tableau, en effet, dont le type contient effectivement les dimensions internes. Ce qui n'est pas un pointeur de pointeur.

    Cela dit, j'aurais utilisé tab = (int(*)[3][4])malloc(2*sizeof(int[3][4]));.

  15. #15
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Il y a peut-être quelque chose que j'ai raté mais il me semble que c'est faux : il est tout-à-fait possible de déclarer un pointeur sur un tableau en spécifiant sa géométrie et, de là, allouer dynamiquement son espace avec un malloc().
    Tu as tout à fait raison et je dois dire que ça ne m'avait absolument pas traversé l'esprit (à raison, j'explique pourquoi ci-après). Tu n'as même pas besoin de cast :

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
     
    void dump(int (*tab)[3][4]) {
        printf("{\n");
        for (size_t i = 0; i < 2; ++i) {
            printf("    {\n");
            for (size_t j = 0; j < 3; ++j) {
                printf("        { ");
                for (size_t k = 0; k < 4; ++k)
                    printf("%d ", tab[i][j][k]);
                printf("}\n");
            }
            printf("    }\n");
        }
        printf("}\n");
    }
     
     
    typedef int (*tab_t)[3][4];
     
    void dump_typedef(tab_t tab) {
        printf("{\n");
        for (size_t i = 0; i < 2; ++i) {
            printf("    {\n");
            for (size_t j = 0; j < 3; ++j) {
                printf("        { ");
                for (size_t k = 0; k < 4; ++k)
                    printf("%d ", tab[i][j][k]);
                printf("}\n");
            }
            printf("    }\n");
        }
        printf("}\n");
    }
     
     
    int main() {
        int tab0[2][3][4] = {
            {
                { 0, 1, 2, 3 },
                { 3, 4, 5, 6 },
                { 6, 7, 8, 9 }
            },
     
            {
                { 9, 8, 7, 6 },
                { 6, 5, 4, 3 },
                { 3, 2, 1, 0 }
            }
        };
     
        tab_t tab1 = malloc(sizeof tab0);
        assert(tab1);
     
        dump(tab0);
        dump_typedef(tab0);
     
        memcpy(tab1, tab0, sizeof tab0);
        dump(tab1);
        dump_typedef(tab1);
     
        free(tab1);
        return 0;
    }

    Citation Envoyé par Obsidian Voir le message
    On obtient donc bien un tableau proprement indexé et qui s'utilise avec la syntaxe habituelle. Il n'y vraiment pas de raison de s'en priver.
    C'est peut-être subjectif mais il serait sage de s'en priver au moins pour deux raisons :

    • il faudrait se trimballer partout cette syntaxe dégueulasse, quitte à la cacher sous une macro ou un dangereux typedef ;
    • il serait impossible de passer ce pointeur en paramètre const (comme à la fonction dump ci-dessus).

  16. #16
    Membre Expert
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    ...
    C'est peut-être subjectif mais il serait sage de s'en priver au moins pour deux raisons :

    • il faudrait se trimballer partout cette syntaxe dégueulasse, quitte à la cacher sous une macro ou un dangereux typedef ;
    • il serait impossible de passer ce pointeur en paramètre const (comme à la fonction dump ci-dessus).
    Si on utilise C11 et la notation VLA (et pas les vla ... juste la notation) tout se simplifie (dans le code d'exemple je ne vérifie pas le malloc, je prends un tableau «2d» comme exemple, facilement ajustable à plus de dimensions, je devrais mieux prototyper, … c'est juste un petit exemple quick and dirty) :

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    #include <stdio.h>
    #include <stdlib.h>
     
    void a2d_init(size_t d1, size_t d2, int array[d1][d2])
    {
        for(size_t i=0; i<d1; ++i)
            for(size_t j=0; j<d2; ++j)
                array[i][j]=0;
    }
     
    void a2d_rand(size_t d1, size_t d2, int array[d1][d2])
    {
        for(size_t i=0; i<d1; ++i)
            for(size_t j=0; j<d2; ++j)
                array[i][j]=rand()%1000;
    }
     
     
    void a2d_dump(size_t d1, size_t d2, const int array[d1][d2])
    {
        for(size_t i=0; i<d1; ++i) {
            for(size_t j=0; j<d2; ++j)
                printf("%3d ", array[i][j]);
            putchar('\n');
        }
    }
     
    int main(void)
    {
    #define D1 5
    #define D2 3
        int static_array[D1][D2];
        puts("static init+dump");
        a2d_init(D1,D2,static_array);
        a2d_dump(D1,D2,static_array);
     
        puts("static randomize+dump");
        a2d_rand(D1,D2,static_array);
        a2d_dump(D1,D2,static_array);
     
        int (*dynamic_array)[D1][D2] = malloc( sizeof(int [D1][D2] ) );
        puts("dynamic init+dump");
        a2d_init(D1,D2,*dynamic_array);
        a2d_dump(D1,D2,*dynamic_array);
     
        puts("dynamic randomize+dump");
        a2d_rand(D1,D2,*dynamic_array);
        a2d_dump(D1,D2,*dynamic_array);
     
        free(dynamic_array);
     
        return 0;
    }
    Un pointeur sur un tableau est un pointeur comme un autre

    Il ne faut jamais utiliser dans un vrai code un assert pour se prémunir d'une éventuelle erreur runtime ... jamais. assert n'est pas fait pour ça.

    L'allocation dynamique des tableaux «multidimensionnels» peut se résoudre par plusieurs approches :
    • pointeurs sur pointeurs sur pointeurs ...
    • allocation totale sur la première cellule, les autres pointeurs pointant dans cette zone mémoire contigüe
    • allocation unique avec calcul des index
    • allocation unique avec pointeur sur tableau
    • ...

  17. #17
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 485
    Par défaut
    Hello,

    Citation Envoyé par Matt_Houston Voir le message
    C'est peut-être subjectif mais il serait sage de s'en priver au moins pour deux raisons :
    • il faudrait se trimballer partout cette syntaxe dégueulasse, quitte à la cacher sous une macro ou un dangereux typedef ;
    À mon avis, c'est toujours moins sale que de linéariser le tableau, ce qui nous oblige à réécrire les multiplications et à mettre du code explicite à l'intérieur des crochets partout où l'on fait référence au tableau (à moins de passer par une macro, ce qui revient au même).

    • il serait impossible de passer ce pointeur en paramètre const (comme à la fonction dump ci-dessus).
    Pourquoi pas ? J'ai du mal à voir où tu veux en venir…
    Ton code compile en l'état et s'exécute sans problème chez moi. Il fonctionne également avec le prototype suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void dump(int (* const tab)[3][4]) {
    Il est aussi possible de conserver le pointeur en dehors du typedef (parce que ça, c'est effectivement très sale).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    typedef int (*tab_t)[3][4];
     void dump_typedef(tab_t tab) {  …
     
    typedef int (tab_t)[3][4];
     void dump_typedef(tab_t *tab) {
    En fait, cela permet même de faire ce genre de chose très inhabituelle :

    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
    /* Tableau */
     
    #include <stdio.h>
     
    typedef int tableau [5][6];
     
    int main (void)
    {
        tableau t;
     
        printf ("Taille d'un entier : %lu\n",sizeof (int));
        printf ("Taille du tableau : %lu\n",sizeof t);
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ ./tableau
    Taille d'un entier : 4
    Taille du tableau : 120

  18. #18
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2016
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2016
    Messages : 7
    Par défaut
    Je suis content d'avoir lancé le sujet, même si je suis largué ^^
    J'ai pensé à autre chose (vous moquez pas svp):
    Est-ce-que dans le cas de déclaration gourmande en mémoire (comme les tableau multidimensionnels, ou des gros malloc...), est-ce que ça ne peut pas valoir le coup
    de stocker les données directement dans un fichier (et donc pas dans la ram mais sur le disque dur) ?
    soit en stockant carrément sous forme de chaines de caracactères, ou par paquets de bits... voyez?
    J'imagine que c'est une mauvaise solution pour les données qui varient beaucoup et que la vitesse de traitement n'est pas optimale, mais ça pourrait être une solution dans certains cas non?

  19. #19
    Membre Expert
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Par défaut
    Ce qu'il faut surtout est une structure de donnée adaptée. Dans ton cas plutôt que d'utiliser un tableau tu pourrais utiliser ou une table de hachage ou un arbre préfixe. Ta consommation mémoire serait réduite au minimum ou presque.

  20. #20
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Citation Envoyé par picodev Voir le message
    Si on utilise C11 et la notation VLA (et pas les vla ... juste la notation) tout se simplifie (dans le code d'exemple je ne vérifie pas le malloc, je prends un tableau «2d» comme exemple, facilement ajustable à plus de dimensions, je devrais mieux prototyper, … c'est juste un petit exemple quick and dirty) :
    Ce n'est pas vilain..! Probablement la solution qui me plaît le plus personnellement.


    Citation Envoyé par picodev Voir le message
    Il ne faut jamais utiliser dans un vrai code un assert pour se prémunir d'une éventuelle erreur runtime ... jamais. assert n'est pas fait pour ça.
    Okaaay.. no stress, on n'est pas dans la FAQ C. C'est simplement pour s'assurer que l'allocation du programme de test a réussi (sinon on ne peut rien en déduire).


    Citation Envoyé par Obsidian Voir le message
    À mon avis, c'est toujours moins sale que de linéariser le tableau, ce qui nous oblige à réécrire les multiplications et à mettre du code explicite à l'intérieur des crochets partout où l'on fait référence au tableau (à moins de passer par une macro, ce qui revient au même)
    Alors nous ne sommes pas du même avis. Je considère que la notation multi-dimensionnelle est une couche d'abstraction malvenue dans nombre d'applications où l'arrangement mémoire est d'une importance capitale et doit être exposé explicitement.

    Je ne dis pas que cela doit être banni, c'est parfois utile : notamment dans le cas présenté par l'OP sur ce fil. Je pense simplement que cette syntaxe est trop souvent utilisée à tort et à travers.


    Citation Envoyé par Obsidian Voir le message
    Pourquoi pas ? J'ai du mal à voir où tu veux en venir...
    Ton code compile en l'état et s'exécute sans problème chez moi. Il fonctionne également avec le prototype suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void dump(int (* const tab)[3][4]) {
    Mais c'est const int (*tab)[3][4] (ou int const (*tab)[3][4]) qu'il faudrait écrire pour que le contenu du tableau soit constant. Dans la signature que tu proposes c'est la valeur du paramètre pointeur qui l'est.


    Citation Envoyé par Janosch89 Voir le message
    Je suis content d'avoir lancé le sujet, même si je suis largué ^^
    Oui c'est rien, on aime bien faire les language lawyers (les pinailleurs de norme), ça nous détend.


    Citation Envoyé par Janosch89 Voir le message
    Est-ce-que dans le cas de déclaration gourmande en mémoire (comme les tableau multidimensionnels, ou des gros malloc...), est-ce que ça ne peut pas valoir le coup
    de stocker les données directement dans un fichier (et donc pas dans la ram mais sur le disque dur) ?
    C'est une possibilité. Sous Linux par exemple, il existe l'appel système mmap qui peut servir entre autres à mapper virtuellement un fichier sur disque vers une zone de mémoire. Le programme accède à un bloc mémoire indiscernable de n'importe quel buffer normalement alloué via malloc et le kernel s'occupe de maintenir l'interface et réaliser les accès disques nécessaires à l'« illusion ».

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

Discussions similaires

  1. Taille maximale d'un tableau de char
    Par ickis69 dans le forum Débuter
    Réponses: 33
    Dernier message: 06/04/2012, 10h38
  2. [XHTML 1.0] Taille maximale d'un tableau
    Par ne2sbeal dans le forum Balisage (X)HTML et validation W3C
    Réponses: 1
    Dernier message: 05/03/2010, 11h23
  3. [Tableaux] Taille maximale d'un tableau
    Par estacado dans le forum Langage
    Réponses: 7
    Dernier message: 25/07/2007, 21h20
  4. [Tableaux] Taille maximale d'un tableau
    Par Bisûnûrs dans le forum Langage
    Réponses: 3
    Dernier message: 15/02/2007, 18h53
  5. [langage] taille maximale d'un tableau ?
    Par Jasmine80 dans le forum Langage
    Réponses: 10
    Dernier message: 10/11/2006, 09h41

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