132 votes

Créer un pointeur vers un tableau bidimensionnel

J'ai besoin d'un pointeur vers un tableau statique à deux dimensions. Comment faire ?

static uint8_t l_matrix[10][20];

void test(){
   uint8_t **matrix_ptr = l_matrix; //wrong idea 
}

Je reçois toutes sortes d'erreurs comme :

  • avertissement : affectation à partir d'un type de pointeur incompatible
  • la valeur souscrite n'est ni un tableau ni un pointeur
  • error : invalid use of flexible array member

0 votes

Lire stackoverflow.com/questions/423823/ cela peut vous aider

1 votes

@JohannesSchaub-litb Cela n'existe plus. (Comment puis-je le visualiser à nouveau... ? I connaître Les membres à faible reps peuvent le voir, mais j'ai oublié comment...)

1 votes

@muntoo : Voici une copie de celui-ci : gist.github.com/sharth/ede13c0502d5dd8d45bd

158voto

Johannes Schaub - litb Points 256113

Ici vous voulez faire un pointeur sur le premier élément du tableau

uint8_t (*matrix_ptr)[20] = l_matrix;

Avec le typedef, cela semble plus propre

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

Vous pourrez alors profiter à nouveau de la vie :)

matrix_ptr[0][1] = ...;

Méfiez-vous de la monde pointeur/rayon en C, une grande confusion règne à ce sujet.


Modifier

Je passe en revue certaines des autres réponses ici, car les champs de commentaires sont trop courts pour le faire ici. De multiples alternatives ont été proposées, mais on n'a pas montré comment elles se comportent. Voici comment elles se comportent

uint8_t (*matrix_ptr)[][20] = l_matrix;

Si vous corrigez l'erreur et ajoutez l'opérateur address-of & comme dans l'extrait suivant

uint8_t (*matrix_ptr)[][20] = &l_matrix;

Ensuite, celui-ci crée un pointeur vers un tableau incomplet d'éléments de type tableau de 20 uint8_t. Puisque le pointeur est un tableau de tableaux, vous devez y accéder avec la commande

(*matrix_ptr)[0][1] = ...;

Et parce que c'est un pointeur vers un tableau incomplet, vous ne peut pas faire comme un raccourci

matrix_ptr[0][0][1] = ...;

Parce que l'indexation exige que la taille du type d'élément soit connue (l'indexation implique l'ajout d'un entier au pointeur, elle ne fonctionne donc pas avec les types incomplets). Notez que cela ne fonctionne que dans C parce que T[] et T[N] sont des types compatibles. Le C++ ne possède pas le concept de types compatibles et donc il rejettera ce code, parce que T[] et T[10] sont de différents types.


L'alternative suivante ne fonctionne pas du tout, car le type d'élément du tableau, lorsque vous le considérez comme un tableau unidimensionnel, est pas uint8_t mais uint8_t[20]

uint8_t *matrix_ptr = l_matrix; // fail

Le texte suivant est une bonne alternative

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

Vous y accédez avec

(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now

Elle présente l'avantage de préserver la taille de la dimension extérieure. Vous pouvez donc appliquer sizeof à cette dimension

sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20

Il existe une autre réponse qui utilise le fait que les éléments d'un tableau sont stockés de manière contiguë.

uint8_t *matrix_ptr = l_matrix[0];

Maintenant, cela ne vous permet formellement que d'accéder aux éléments du premier élément du tableau à deux dimensions. C'est-à-dire que la condition suivante s'applique

matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid

matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior

Vous remarquerez qu'il fonctionne probablement jusqu'à 10*20-1 Mais si vous ajoutez l'analyse des alias et d'autres optimisations agressives, un compilateur pourrait faire une supposition qui pourrait casser ce code. Ceci étant dit, je n'ai jamais rencontré de compilateur qui échoue avec cette technique (mais encore une fois, je n'ai pas utilisé cette technique dans du code réel), et même la FAQ C contient cette technique (avec un avertissement sur son caractère UB'ness), et si vous ne pouvez pas changer le type de tableau, c'est une dernière option pour vous sauver :)

0 votes

+1 - bonne info sur le découpage en int (*)[][20] - on ne peut pas faire ça en C++.

0 votes

@litb, je suis désolé mais c'est faux car votre solution ne prévoit aucune allocation de stockage pour le tableau.

3 votes

@Rob, je ne vous comprends pas bien. Le stockage dans tous ces cas est fourni par le tableau l_matix lui-même. Les pointeurs vers eux prennent le stockage de l'endroit où ils sont déclarés (pile, segment de données statiques, ...).

7voto

Sagar Points 51

Sur

int *ptr= l_matrix[0];

vous pouvez y accéder comme

*p
*(p+1)
*(p+2)

Après tout, les tableaux à deux dimensions sont également stockés à une dimension.

5voto

porneL Points 42805

En C99 (supporté par clang et gcc), il existe une syntaxe obscure pour passer des tableaux multidimensionnels aux fonctions par référence :

int l_matrix[10][20];

void test(int matrix_ptr[static 10][20]) {
}

int main(void) {
    test(l_matrix);
}

Contrairement à un simple pointeur, il donne des indications sur la taille du tableau, théoriquement permettant au compilateur d'avertir du passage d'un tableau trop petit et de repérer les accès hors limites évidents.

Malheureusement, ça ne répare pas sizeof() et les compilateurs ne semblent pas encore utiliser cette information, donc cela reste une curiosité.

1 votes

Cette réponse est trompeuse : Cela ne fait pas de l'argument un tableau de taille fixe, c'est toujours un pointeur. static 10 est une sorte de garantie que au moins 10 éléments sont présents, ce qui signifie à nouveau que la taille n'est pas fixe.

1 votes

@bluss la question portait sur un pointeur, donc je ne vois pas comment répondre avec un pointeur (notant par référence ) est trompeuse. Le tableau est de taille fixe du point de vue de la fonction, car l'accès aux éléments au-delà de ces limites est indéfini.

0 votes

Je ne pense pas que l'accès au-delà de 10 soit indéfini, je ne vois rien qui l'indique.

5voto

Rob Wells Points 21714

Bonjour,

La déclaration

static uint8_t l_matrix[10][20];

a réservé un espace de stockage pour 10 rangées de 20 emplacements unit8_t, soit 200 emplacements de taille uint8_t, chaque élément étant trouvé en calculant 20 x ligne + colonne.

Alors ne

uint8_t (*matrix_ptr)[20] = l_matrix;

vous donne ce dont vous avez besoin et pointe vers l'élément de la colonne zéro de la première ligne du tableau ?

Edit : En y réfléchissant un peu plus, un nom de tableau n'est-il pas, par définition, un pointeur ? Autrement dit, le nom d'un tableau est un synonyme de l'emplacement du premier élément, c'est-à-dire l_matrix[0][0] ?

Edit2 : Comme l'ont mentionné d'autres personnes, l'espace réservé aux commentaires est un peu trop petit pour permettre une discussion plus approfondie. Quoi qu'il en soit :

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

ne fournit aucune allocation de stockage pour le tableau en question.

Comme mentionné ci-dessus, et tel que défini par la norme, la déclaration :

static uint8_t l_matrix[10][20];

a mis de côté 200 emplacements séquentiels de type uint8_t.

Se référer à l_matrice en utilisant des déclarations de la forme :

(*l_matrix + (20 * rowno) + colno)

vous donnera le contenu de l'élément colno'th trouvé dans la rangée rowno.

Toutes les manipulations de pointeurs prennent automatiquement en compte la taille de l'objet pointé. - K&R Section 5.4, p.103

C'est également le cas si le stockage de l'objet en question implique un remplissage ou un décalage de l'alignement des octets. Le compilateur s'adaptera automatiquement à ces éléments. Par définition de la norme C ANSI.

HTH

Santé,

1 votes

Uint8_t (*matrix_ptr)[][20] << les premiers crochets doivent être laissés de côté, le correct est uint8_t (*matrix_ptr)[20]

4voto

gnosis Points 379

Vous pouvez toujours éviter de faire des manipulations avec le compilateur en déclarant le tableau comme linéaire et en effectuant vous-même le calcul de l'indice (row,col) du tableau.

static uint8_t l_matrix[200];

void test(int row, int col, uint8_t val)

{

   uint8_t* matrix_ptr = l_matrix;
   matrix_ptr [col+y*row] = val; // to assign a value

}

c'est ce que le compilateur aurait fait de toute façon.

1 votes

C'est ce que fait le compilateur C de toute façon. Le C n'a pas vraiment de notion de "tableau" - la notation [] est juste un sucre syntaxique pour l'arithmétique des pointeurs.

7 votes

Cette solution présente l'inconvénient de ne jamais trouver la bonne façon de procéder.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X