C pointeur vers un tableau bidimensionnel

Je sais qu'il y a plusieurs questions sur ce qui donne de bonnes solutions (et de travail), mais aucun à mon humble avis qui dit clairement quelle est la meilleure façon d'y parvenir. Donc, supposons que nous ayons un tableau 2D:

int tab1[100][280];

, Nous voulons faire un pointeur qui pointe vers ce tableau 2D. Pour y parvenir, nous pouvons faire:

int (*pointer)[280]; // pointer creation
pointer = tab1; //assignation
pointer[5][12] = 517; // use
int myint = pointer[5][12]; // use

, Ou, alternativement :

int (*pointer)[100][280]; // pointer creation
pointer = &tab1; //assignation
(*pointer)[5][12] = 517; // use
int myint = (*pointer)[5][12]; // use 

OK, les deux semblent bien fonctionner. Maintenant, je voudrais savoir :

  • Quelle est la meilleure façon, la 1ère ou la 2ème ?
  • sont les deux égaux pour le compilateur ? (la vitesse, la perf...)
  • l'une de ces solutions mange-t-elle plus de mémoire que l'autre ?
  • quel est le plus fréquemment utilisé par les développeurs ?
25
demandé sur Chrysotribax 2013-02-11 13:08:04

3 réponses

//defines an array of 280 pointers (1120 or 2240 bytes)
int  *pointer1 [280];

//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280];      //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers

En utilisant pointer2 ou pointer3 produire le même binaire sauf manipulations que ++pointer2comme indiqué par WhozCraig.

Je recommande d'utiliser typedef (produisant le même code binaire que ci-dessus pointer3)

typedef int myType[100][280];
myType *pointer3;

Remarque: Depuis C++11, vous pouvez également utiliser le mot-clé using au lieu de typedef

using myType = int[100][280];
myType *pointer3;

Dans votre exemple:

myType *pointer;                // pointer creation
pointer = &tab1;                // assignation
(*pointer)[5][12] = 517;        // set (write)
int myint = (*pointer)[5][12];  // get (read)

Remarque: Si la matrice tab1 est utilisé dans un corps de fonction => ce tableau sera placé dans la pile d'appel de la mémoire. Mais la taille de la pile est limitée. L'utilisation de tableaux plus grands que la pile de mémoire libre produit un la pile débordement crash .

L'extrait complet est compilable en ligne à gcc.godbolt.org

int main()
{
    //defines an array of 280 pointers (1120 or 2240 bytes)
    int  *pointer1 [280];
    static_assert( sizeof(pointer1) == 2240, "" );

    //defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
    int (*pointer2)[280];      //pointer to an array of 280 integers
    int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers  
    static_assert( sizeof(pointer2) == 8, "" );
    static_assert( sizeof(pointer3) == 8, "" );

    // Use 'typedef' (or 'using' if you use a modern C++ compiler)
    typedef int myType[100][280];
    //using myType = int[100][280];

    int tab1[100][280];

    myType *pointer;                // pointer creation
    pointer = &tab1;                // assignation
    (*pointer)[5][12] = 517;        // set (write)
    int myint = (*pointer)[5][12];  // get (read)

    return myint;
}
22
répondu olibre 2017-05-23 12:02:17

int *pointer[280]; //Crée 280 pointeurs de type int.

Dans le système d'exploitation 32 bits, 4 octets pour chaque pointeur. donc 4 * 280 = 1120 octets.

int (*pointer)[100][280]; // crée un seul pointeur qui est utilisé pour pointer un tableau de[100] [280] ints.

Ici seulement 4 octets.

En venant à votre question, int (*pointer)[280]; et int (*pointer)[100][280]; sont différents bien qu'ils pointent vers le même tableau 2D de [100] [280].

Parce que si {[2] } est incrémenté, alors il pointe vers le tableau 1D suivant, mais où int (*pointer)[100][280]; traverse tout le tableau 2D et les points à l'octet suivant. L'accès à cet octet peut causer des problèmes si cette mémoire n'appartient pas à votre processus.

9
répondu Jeyaram 2013-02-11 10:19:10

Vos deux exemples sont équivalents. Cependant, le premier est moins évident et plus "hacky", tandis que le second indique clairement votre intention.

int (*pointer)[280];
pointer = tab1;

pointer pointe vers un tableau 1D de 280 entiers. Dans votre affectation, vous affectez en fait la première ligne de tab1. Cela fonctionne puisque vous pouvez implicitement convertir des tableaux en pointeurs (vers le premier élément).

Lorsque vous utilisez pointer[5][12], C traite pointer comme un tableau de tableaux ({[6] } est de type int[280]), donc il y a un autreimplicite jeté ici (au moins sémantiquement).

Dans votre deuxième exemple, vous créez explicitement un pointeur vers un tableau 2D:

int (*pointer)[100][280];
pointer = &tab1;

La sémantique est plus claire ici: *pointer est un tableau 2D, vous devez donc y accéder en utilisant (*pointer)[i][j].

Les deux solutions utilisent la même quantité de mémoire (1 pointeur) et fonctionneront probablement aussi vite. Sous le capot, les deux pointeurs pointeront même vers le même emplacement de mémoire (le premier élément du tableau tab1), et c'est il est possible que votre compilateur génère même le même code.

La première solution est "plus avancée" car il faut une compréhension assez profonde du fonctionnement des tableaux et des pointeurs en C pour comprendre ce qui se passe. Le deuxième est plus explicite.

8
répondu Ferdinand Beyer 2013-02-11 15:36:21