wake-up-neo.net

Erstellen Sie einen Zeiger auf ein zweidimensionales Array

Ich brauche einen Zeiger auf ein statisches 2-dimensionales Array. Wie wird das gemacht?

static uint8_t l_matrix[10][20];

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

Ich bekomme alle möglichen Fehler wie:

  • warnung: Zuweisung von inkompatiblem Zeigertyp
  • indizierter Wert ist weder Feld noch ein Zeiger
  • fehler: ungültige Verwendung des flexiblen Arraymitglieds
102
Dill

Hier möchten Sie einen Zeiger auf das erste Element des Arrays setzen

uint8_t (*matrix_ptr)[20] = l_matrix;

Mit Typedef sieht das sauberer aus

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

Dann kannst du das Leben wieder genießen :)

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

Hüten Sie sich vor der pointer/array-Welt in C, es gibt viel Verwirrung.


Bearbeiten

Überprüfen Sie einige der anderen Antworten hier, da die Kommentarfelder dort zu kurz sind. Es wurden mehrere Alternativen vorgeschlagen, aber es wurde nicht gezeigt, wie sie sich verhalten. So machen sie es

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

Wenn Sie den Fehler behoben haben und den Adresse-von-Operator & wie im folgenden Snippet hinzufügen

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

Dann erzeugt dieser einen Zeiger auf einen unvollständigen Array-Typ von Elementen des Typs Array von 20 uint8_t. Da der Zeiger auf ein Array von Arrays verweist, müssen Sie mit auf dieses Feld zugreifen

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

Und weil es ein Zeiger auf ein unvollständiges Array ist, können Sie kann nicht als Verknüpfung verwenden

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

Da für die Indexierung die Größe des Elementtyps bekannt sein muss (die Indexierung impliziert die Hinzufügung einer Ganzzahl zum Zeiger, sodass er nicht mit unvollständigen Typen funktioniert). Beachten Sie, dass dies nur in C funktioniert, da T[] und T[N] kompatible Typen sind. C++ hat kein Konzept von kompatiblen Typen und wird diesen Code daher zurückweisen, da T[] und T[10] unterschiedliche Typen sind. 


Die folgende Alternative funktioniert überhaupt nicht, weil der Elementtyp des Arrays, wenn Sie es als eindimensionales Array anzeigen, nichtuint8_t ist, aber uint8_t[20]

uint8_t *matrix_ptr = l_matrix; // fail

Das Folgende ist eine gute Alternative

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

Du erreichst es mit 

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

Es hat den Vorteil, dass es die Größe der äußeren Abmessungen beibehält. Sie können sizeof darauf anwenden

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

Es gibt eine weitere Antwort, die die Tatsache ausnutzt, dass Elemente in einem Array zusammenhängend gespeichert werden

uint8_t *matrix_ptr = l_matrix[0];

Nun können Sie formal nur auf die Elemente des ersten Elements des zweidimensionalen Arrays zugreifen. Das heißt, die folgende Bedingung gilt

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

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

Sie werden feststellen, dass es wahrscheinlich bis zu 10*20-1 funktioniert. Wenn Sie jedoch Alias-Analysen und andere aggressive Optimierungen durchführen, könnte ein Compiler die Annahme treffen, dass dieser Code möglicherweise beschädigt wird. Ich habe noch nie einen Compiler getroffen, der darauf fehlschlägt (aber ich habe diese Technik nicht in echtem Code verwendet), und selbst das C FAQ enthält diese Technik (mit einer Warnung über seine UB'ness), und wenn Sie den Array-Typ nicht ändern können, ist dies eine letzte Option, um Sie zu speichern :)

126

Umvollständigzu verstehen,müssendie folgenden Konzepte verstehen:

Arrays sind keine Zeiger!

Erstens (und es wurde genug gepredigt), sind Arrays keine Zeiger . Stattdessen verfallen sie in den meisten Fällen an der Adresse ihres ersten Elements, das einem Zeiger zugewiesen werden kann:

_int a[] = {1, 2, 3};

int *p = a; // p now points to a[0]
_

Ich gehe davon aus, dass dies so funktioniert, dass auf den Inhalt des Arrays zugegriffen werden kann, ohne alle zu kopieren. Das ist nur ein Verhalten von Array-Typen und soll nicht bedeuten, dass sie dasselbe sind.



Mehrdimensionale Arrays

Mehrdimensionale Arrays sind nur eine Möglichkeit, den Speicher so zu partitionieren, dass der Compiler/die Maschine ihn/sie verstehen und bearbeiten kann.

Zum Beispiel ist _int a[4][3][5]_ = ein Array, das 4 * 3 * 5 (60) 'Chunks' von ganzzahligem Speicher enthält.

Der Vorteil gegenüber der Verwendung von _int a[4][3][5]_ im Vergleich zu normalem _int b[60]_ besteht darin, dass sie jetzt "partitioniert" sind (es ist einfacher, bei Bedarf mit ihren "Chunks" zu arbeiten), und das Programm kann jetzt gebundene Prüfungen durchführen.

Tatsächlich wird _int a[4][3][5]_genauwie _int b[60]_ im Speicher gespeichert - DasnurDer Unterschied besteht darin, dass das Programm es jetzt so verwaltet, als wären es separate Entitäten bestimmter Größe (insbesondere vier Gruppen zu drei Gruppen zu fünf).

Beachten Sie: Sowohl _int a[4][3][5]_ als auch _int b[60]_ sind im Arbeitsspeicher identisch, und der einzige Unterschied besteht darin, wie sie von der Anwendung/dem Compiler verarbeitet werden

_{
  {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}
}
_

Daran können Sie deutlich erkennen, dass jede "Partition" nur ein Array ist, das das Programm verfolgt.



Syntax

Jetzt unterscheiden sich Arrays syntaktisch von Zeigern . Konkret bedeutet dies, dass der Compiler/die Maschine sie unterschiedlich behandelt. Dies mag ein Kinderspiel sein, aber sehen Sie sich dies an:

_int a[3][3];

printf("%p %p", a, a[0]);
_

Das obige Beispiel gibt dieselbe Speicheradresse zweimal aus:

_0x7eb5a3b4 0x7eb5a3b4
_

Einem Zeiger kann jedoch nur einer so direkt zugewiesen werden :

_int *p1 = a[0]; // RIGHT !

int *p2 = a; // WRONG !
_

Warum kann nicht a einem Zeiger zugewiesen werden, sondern _a[0]_ kann?

Dies ist einfach eine Folge mehrdimensionaler Arrays, und ich erkläre, warum:

Auf der Ebene von 'a' sehen wir immer noch, dass wir eine andere 'Dimension' haben, auf die wir uns freuen können. Auf der Ebene von '_a[0]_' befinden wir uns jedoch bereits in der obersten Dimension, sodass wir in Bezug auf das Programm nur ein normales Array betrachten.

Möglicherweise fragen Sie:

Warum ist es wichtig, wenn das Array mehrdimensional ist, um einen Zeiger dafür zu erstellen?

Am besten denkst du so:

Ein 'Zerfall' aus einem mehrdimensionalen Array ist nicht nur eine Adresse, sonderneine Adresse mit Partitionsdaten(AKA versteht immer noch, dass die zugrunde liegenden Daten aus anderen Arrays bestehen), Dies besteht aus Grenzen, die durch das Array jenseits der ersten Dimension festgelegt werden.

Diese 'Partitions'-Logik kann in einem Zeiger nur existieren, wenn wir sie spezifizieren:

_int a[4][5][95][8];

int (*p)[5][95][8];

p = a; // p = *a[0] // p = a+0
_

Andernfalls geht die Bedeutung der Sortiereigenschaften des Arrays verloren.

Beachten Sie auch die Verwendung von Klammern um _*p_: int (*p)[5][95][8] - Dies gibt an, dass wir einen Zeiger mit diesen Grenzen erstellen, nicht ein Array von Zeigern mit diesen Grenzen: _int *p[5][95][8]_



Fazit

Lassen Sie uns überprüfen:

  • Arrays verwandeln sich in Adressen, wenn sie im verwendeten Kontext keinen anderen Zweck haben
  • Mehrdimensionale Arrays sind nur Arrays von Arrays. Daher trägt die "verfallene" Adresse die Last von "Ich habe Unterdimensionen".
  • Bemaßungsdaten können nicht in einem Zeigervorhanden sein, es sei denn, Sie geben sie ihm.

Kurz gesagt: Mehrdimensionale Arrays zerfallen in Adressen, die die Fähigkeit besitzen, ihren Inhalt zu verstehen.

24
Super Cat

Im

int *ptr= l_matrix[0];

sie können gerne zugreifen 

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

schließlich werden alle 2 dimensionalen Arrays auch als 1-d gespeichert.

7
Sagar

In C99 (unterstützt von clang und gcc) gibt es eine obskure Syntax, um mehrdimensionale Arrays per Referenz an Funktionen zu übergeben:

int l_matrix[10][20];

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

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

Im Gegensatz zu einem einfachen Zeiger deutet dies auf die Array-Größe und theoretisch an, dass der Compiler warnt, wenn ein zu kleines Array übergeben wird und offensichtlicher Zugriff außerhalb der Grenzen erfolgt.

Leider behebt es nicht sizeof() und Compiler scheinen diese Informationen noch nicht zu verwenden, daher bleibt sie neugierig.

5
Kornel

Tag auch,

Die Erklärung

static uint8_t l_matrix[10][20];

hat Speicher für 10 Zeilen mit 20 unit8_t-Positionen, d. h. 200 uint8_t-großen Positionen, zur Verfügung gestellt, wobei jedes Element durch Berechnen von 20 x Zeile + Spalte gefunden wird.

Also nicht

uint8_t (*matrix_ptr)[20] = l_matrix;

geben Sie Ihnen, was Sie brauchen, und zeigen Sie auf das Element der Spalte Null der ersten Zeile des Arrays?

Edit: Wenn man darüber ein bisschen weiter nachdenkt, ist ein Arrayname nicht per Definition ein Zeiger? Das heißt, der Name eines Arrays ist ein Synonym für den Ort des ersten Elements, d. H. L_matrix [0] [0]?

Edit2: Wie bereits erwähnt, ist der Kommentarraum für eine weitere Diskussion etwas zu klein. Sowieso:

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

bietet keine Speicherzuordnung für das betreffende Array.

Wie oben erwähnt und wie vom Standard definiert, gilt die Anweisung:

static uint8_t l_matrix[10][20];

hat 200 sequentielle Stellen vom Typ uint8_t reserviert.

Verweis auf l_matrix mit Anweisungen des Formulars:

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

gibt Ihnen den Inhalt des colno'th-Elements in Zeile rowno.

Alle Zeigermanipulationen berücksichtigen automatisch die Größe des Objekts, auf das gezeigt wird. - K & R Abschnitt 5.4, S.103

Dies ist auch der Fall, wenn beim Ablegen des vorhandenen Objekts eine Auffüllung oder Verschiebung der Byte-Ausrichtung erforderlich ist. Der Compiler passt sich automatisch an diese an. Nach Definition des C ANSI-Standards.

HTH

prost,

5
Rob Wells

Sie können es immer vermeiden, mit dem Compiler herumzuspielen, indem Sie das Array als linear deklarieren und die Berechnung der Index- (Zeilen-, Spalten-) Indizes selbst vornehmen.

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

}

das hätte der Compiler sowieso getan.

4
gnosis

Die grundlegende Syntax zum Initialisieren eines Zeigers, der auf ein multidimentionales Array zeigt, lautet

type (*pointer)[ 1st dimension size ][2nd dimension size ][..]=&array_name

Die grundlegende Syntax für den Aufruf ist

(*pointer_name)[ 1st index][2nd index][...]

Hier ist ein Beispiel

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
   char balance[5][100] = { {"Subham"},{"Messi"} };//The multidimentional array...

   char (*p)[5][100]=&balance;//Pointer initialization...

   printf("%s\n",(*p)[0]);//Calling...
   printf("%s\n",(*p)[1]);//Calling...

  return 0;
}

Ausgabe ist:

Subham
Messi

Das hat es geschafft ...

1
Subham Debnath

Sie können es so machen:

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

Sie möchten einen Zeiger auf das erste Element.

static uint8_t l_matrix[10][20];

void test(){
   uint8_t *matrix_ptr = l_matrix[0]; //wrong idea 
}
1
Ken Keenan

Sie können auch einen Versatz hinzufügen, wenn Sie negative Indizes verwenden möchten:

uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;

Wenn Ihr Compiler einen Fehler oder eine Warnung ausgibt, können Sie Folgendes verwenden:

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