2017-03-31 4 views
1

Ich lerne C und wie ich im Zeiger Kapitel stecken geblieben bin. Particulullary am Zeiger auf Zeiger (** p) Teil, verwendet mit der Verarbeitung von 2-dimensionalen Arrays.C - Zeiger auf Zeigerarray

Die folgende Funktion erzeugt ein 2-dimensionales Array mit allen Elementen, die gleich 0 sind.

double** example(int rows, int cols){ 
    static double tr[rows][cols]; 
    for(int i = 0; i < rows; i++) 
     for(int j = 0; j < cols; j++) 
      tr[j][i] = 0; 
    return tr; 
    } 
int main(void){ 

    double **tr; 
    int m = 2 ; 
    int n = 2 ; 

    tr = transpose(m, n); 
    return 0;} 

Ist der tr durch die Funktion einen "double pointer" auf das erste Element des tr Array zurückgegeben? Oder zu seinem Wert? Wie kann ich nun (nach dem Aufruf der Funktion) auf die Elemente des Arrays in dem Teil des Speichers zugreifen, auf den ** tr zeigt?

Kann mir jemand erklären, wie das alles funktioniert? Oder schlagen Sie mir einen Artikel oder ein Kapitel in einem Buch vor?

+0

'example' ein Doppel zurück Zeiger auf das erste Element. Sie können auf seine Elemente auf die gleiche Weise wie in der Funktion zugreifen, d. H. 'Tr [0] [1]' <- Zeile 0, Spalte 1. – Geoff

+0

Werfen Sie dieses Buch weg, es lehrt Sie * Müll *. Siehe Vlads Antwort unten. 'double **' ist * nicht * der richtige Typ für ein 2D-Array. –

Antwort

2

Das vorgestellte Programm ist völlig inkorrekt. Ein variabler Länge Array nicht statisch Lagerdauer aufweisen kann/Gemäß dem C Standard (6.7.6.2 Array Deklaratoren)

2 Wenn eine Kennung, wie mit einem variabel modifizierten Typ deklariert wird, ist es wird eine gewöhnliche Kennung sein (wie in 6.2.3 definiert), haben keine Verknüpfung, und haben entweder Block Scope oder Funktion Prototyp Umfang. Wenn ein Identifier als Objekt mit statischer oder Thread-Speicher-Dauer deklariert ist, darf er keinen Array-Typ variabler Länge haben.

Darüber hinaus sind die Zeigertypen double **double (*)[cols] und (auf dem die Anordnung vorgesehen, dass es richtig umgewandelt wird in Ausdrücken deklariert wurde) nicht kompatibel sind. Also ist die Funktion falsch.

double** example(int rows, int cols){ 
    static double tr[rows][cols]; 
    for(int i = 0; i < rows; i++) 
     for(int j = 0; j < cols; j++) 
      tr[j][i] = 0; 
    return tr; 
    } 

Hier ist ein Demonstrationsprogramm, das zeigt, wie man mit Arrays variabler Länge umgeht.

#include <stdio.h> 

void init(size_t rows, size_t cols, double a[rows][cols]) 
// or 
// void init(size_t rows, size_t cols, double a[][cols]) 
// or 
// void init(size_t rows, size_t cols, double (*a)[cols]) 
{ 
    for (size_t i = 0; i < rows; i++) 
    { 
     for (size_t j = 0; j < cols; j++) a[i][j] = 0.0; 
    } 
} 

void display(size_t rows, size_t cols, double a[rows][cols]) 
// or 
// void display(size_t rows, size_t cols, double a[][cols]) 
// or 
// void display(size_t rows, size_t cols, double (*a)[cols]) 
{ 
    for (size_t i = 0; i < rows; i++) 
    { 
     for (size_t j = 0; j < cols; j++) printf("%lf", a[i][j]); 
     putchar('\n'); 
    } 
} 

int main(void) 
{ 
    while (1) 
    { 
     size_t m, n; 

     printf("Enter numbers of rows and columns (0 - exit): "); 

     if (scanf("%zu%zu", &m, &n) != 2 || m == 0 || n == 0) break; 

     double a[m][n]; 

     putchar('\n'); 

     init(m, n, a); 
     display(m, n, a); 

     putchar('\n'); 
    } 

    return 0; 
} 

Sein Ausgang könnte wie folgt aussehen

Enter numbers of rows and columns (0 - exit): 2 3 

0.0000000.0000000.000000 
0.0000000.0000000.000000 

Enter numbers of rows and columns (0 - exit): 3 4 

0.0000000.0000000.0000000.000000 
0.0000000.0000000.0000000.000000 
0.0000000.0000000.0000000.000000 

Enter numbers of rows and columns (0 - exit): 0 0 

Innerhalb der beiden Funktionen der dritte Parameter auf den Zeigertyp eingestellt double (*a)[cols]. Es ist nicht dasselbe wie double **a.

Wenn Sie zum Beispiel schreiben, wird das folgende Programm

#include <stdio.h> 

#define M 2 
#define N 3 

int main(void) 
{ 
    int a[M][N] = 
    { 
     { 1, 2, 3 }, 
     { 4, 5, 6 } 
    }; 

    int **p = (int **)a; 

    p[0][0] = 10; 

    return 0; 
} 

dann wird es nicht definiertes Verhalten, weil p[0] den Wert in dem Element gespeichert hält a[0][0] (oder den kombinierten Wert in Elementen gespeichert a[0][0] und a[0][1] auf die je Größe des Zeigers), das ist der Wert 1 als Zeigerwert und versuchen, auf den Speicher an Adresse 1 im Ausdruck p[0][0] zuzugreifen.

2

In C müssen Funktionen einen Wert/eine Variable vom gleichen Typ wie die Funktion zurückgeben. In diesem Beispiel wäre tr ein Doppelzeiger, der auf die erste Zeile zeigt.

Wenn Sie Doppelzeiger verwenden, um 2D-Arrays zu erstellen, erstellen Sie einen Zeiger, der auf ein Array von Zeigern zeigt, wobei die Zeiger innerhalb des Arrays auf Strings/Werte in jeder Zeile verweisen.Der Doppelzeiger würde auf den ersten Zeiger im Zeigerfeld zeigen, der auf das erste Element in der ersten Zeile zeigt.

Sie können auf Werte in 2D-Arrays mithilfe der Klammernotation wie tr[row][column] oder *(*(tr + row) + column) zugreifen. Für die zweite Schreibweise greift *(tr + row) auf den Zeiger zu, der auf die gewünschte Zeile zeigt. Sie können dann auf den Wert verweisen, indem Sie *(found_row + column) verwenden und das Element in der gewünschten Spalte angeben.

+0

Das Problem ist, dass der Typ von 'tr' nicht * double **' ist, es ist 'double (*) [cols]'. –

0

Werfen Sie das Buch, das diesen Code enthält weg - es nicht einmal richtig kompilieren. Ich erhalte die folgenden Fehler:

[[email protected]]~/prototypes/buf: gcc -o stack -std=c99 -pedantic-errors -Wall stack.c 
stack.c: In function âexampleâ: 
stack.c:2: error: storage size of âtrâ isnât constant 
stack.c:6: error: return from incompatible pointer type 

Sie können keine variabler Länge Array mit static Speicherdauer erstellen. Objekte mit static Speicherdauer werden beim Programmstart zugewiesen, so dass ihre Größen zur Kompilierzeit bekannt sein müssen. Ein VLA kann erst instanziiert werden, wenn Sie die Größe seiner Dimensionen zur Laufzeit kennen.

Außer wenn es der Operanden der sizeof oder unäre & Operatoren oder ist ein String literal verwendet, um ein Zeichenfeld in einer Erklärung zu initialisieren, ein Ausdruck vom Typ „N-Element-Array von T“ wird umgewandelt ("Verfall") auf einen Ausdruck vom Typ "Zeiger auf T", und der Wert des Ausdrucks wird die Adresse des ersten Elements des Arrays sein.

Der Ausdrucktr hat "rows -Glied Array von cols -Glied Array von double" Typ; In der return-Anweisung wird dieser Ausdruck "zerfallen", um "Zeiger auf cols -Element-Array von double" oder double (*)[cols] einzugeben. Dies ist ein komplett anderer Typ von double **.

Mehrfache Indirection zeigt viel, aber das ist kein gültiges Beispiel.

Sie würden normalerweise mehrere indirection sehen, wenn Sie eine Funktion auf einen Parameter Zeigertyp schreiben wollen:

void bar(T **p) // for any type T 
{ 
    *p = new_pointer_value(); // write a new value to the thing p points to 
} 

void foo(void) 
{ 
    T *ptr; 

    bar(&ptr); // bar writes a value to ptr 
} 

Sie mehrere indirection verwenden können, eine Struktur zu schaffen, die ein bisschen-sorta sieht aus und verhält sich wie ein 2D Array, aber es ist nicht das gleiche wie ein 2D-Array:

double **arr; 
size_t rows = some_number_of_rows(); 
size_t cols = sime_number_of_cols(); 

arr = malloc(sizeof *arr * rows); // allocates an array of pointers to double 
if (arr) 
{ 
    for (size_t i = 0; i < rows; i++) 
    { 
    arr[i] = malloc(sizeof *arr[i] * cols); // allocates an array of double 
    } 
} 

Wenn Sie fertig sind, haben Sie eine Struktur, die wie folgt aussieht:

+---+  +---+  +---+---+---+  +---+ 
a: | | ---> | | ---> | | | | ... | | 
    +---+  +---+  +---+---+---+  +---+ 
       | | -+ 
       +---+ | +---+---+---+  +---+ 
       ... +-> | | | | ... | | 
       +---+  +---+---+---+  +---+ 
       | | -+ 
       +---+ | +---+---+---+  +---+ 
         +-> | | | | ... | | 
          +---+---+---+  +---+ 

Sie können Elemente zugreifen reguläre Index-Notation (a[i][j]) wie ein 2D-Array, aber der Speicher für diese Struktur verwenden, ist nicht zusammenhängend zugewiesen, wie es für einen 2D-Array sein würde:

+---+---+---+---+---+ 
a: | | | | | | 
    +---+---+---+---+---+ 
    | | | | | | 
    +---+---+---+---+---+ 
    | | | | | | 
    +---+---+---+---+---+ 
    ...