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:
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.
Ü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 :)
Umvollständigzu verstehen,müssendie folgenden Konzepte verstehen:
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 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.
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]
_
Lassen Sie uns überprüfen:
Kurz gesagt: Mehrdimensionale Arrays zerfallen in Adressen, die die Fähigkeit besitzen, ihren Inhalt zu verstehen.
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.
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.
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,
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.
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 ...
Sie können es so machen:
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
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
}
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;