Du bist nicht angemeldet.

Werbeanzeige

11

21.10.2006, 00:56

sry hatte jetzt nicht viel Zeit ... habs allerdings net ganz so gemacht wie gewünscht ... sry

C-/C++-Quelltext

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
#include <iostream>

const unsigned int dimension = 4;

bool show(int** ppField);

int _tmain(int argc, _TCHAR* argv[])
{
    int** ppField = new int*[dimension];

    for (unsigned int x = 0; x < dimension; ++x)
    {
        ppField[x] = new int[dimension];

        for (unsigned int y = 0; y < dimension; ++y)
            ppField[x][y] = (int)(x+y);
    }
   
    if (show(ppField) == false)
        return -1;

    return 0;
}

bool show(int** ppField)
{
    if (ppField == NULL)
        return false;

    for (unsigned int x = 0; x < dimension; ++x)
        for (unsigned int y = 0; y < dimension; ++y)
            std::cout << x << "::" << y << ": " << ppField[x][y] << std::endl;

    return true;
}

so in etwa ... musst de mal durch compiler jagen ... hatte keine Zeit es zu testen ... sry
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

rewb0rn

Supermoderator

Beiträge: 2 869

Wohnort: Berlin

Beruf: Indie Game Dev

  • Private Nachricht senden

12

21.10.2006, 00:58

Also Doppelzeiger zu verwalten ist echt ganz gut machbar wenn man weiss wie Zeiger funzen ;)

Hab mal ne Klasse geschrieben die Zeichenketten verwaltet hat, die hat mit Dreifachzeigern gearbeitet^^ War aber so umstaendlich, dass ich es dann doch wieder aufgegeben hab hehe..

Hier ein einfaches Feld Mit nem Doppelzeiger:

C-/C++-Quelltext

1
2
3
4
5
6
7
int** a = new int*[10];
for (int i=0; i<10; i++)
{
    a[i] = new int[10];
    for (int j=0; j<10; j++)
        a[i][j] = i*j;
}

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

13

21.10.2006, 10:27

Hmm, irgendwie kommt es immer zu Problemen, wenn die Leute anfangen, in C/C++ mit Zeigern und Arrays zu arbeiten.

Der erste Fehler ist immer, dass sie glauben, ein Array wäre etwas anderes als ein Zeiger. Das ist aber nicht so.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
void foo ()
{
  int array[10];

  int* pointer = new int [10];
  ...
  delete[] pointer;
}


In diesem Fall ist array einer Zeiger auf den Stack, wo der Compiler automatisch Platz für 10 Integer angelegt hat, der bei verlassen der Funktion automatisch wieder freigegeben wird (verlassen in dem Sinn, dass die Funktion komplett durchgelaufen ist). pointer ist ein Zeiger auf den Heap, wo man von Hand Platz für 10 Integer angelegt hat, den man auch selbst wieder freigeben muss (nicht notwendig in der selben Funktion). Ein Array ist einfach ein zusammenhängender Speicherbereich, in dem nacheinander die Elemente des Array stehen. Der Zeiger beschreibt die Speicherstelle, an der das erste Element steht.

Der nächste Fehler ist die Annahme, ein Zeiger wäre irgendwie etwas anderes, als ein char, int oder float. Das ist nicht so, es handelt sich bei einem Zeiger auch um ein Objekt, das Platz im Speicher verbraucht und, was noch wichtiger ist, der irgendwo im Speicher steht; damit kann es einen Zeiger auf die Stelle geben, wo wieder ein Zeiger steht (was hier gerne als Doppelpointer :roll: bezeichnet wird).

Man stelle sich ein Buch vor, die Seiten sind der Speicher, die Seitenzahlen sind die Zeiger. Wenn auf Seite 1 ein Wort steht ist die 1 der Zeiger auf das Wort. Ein Array wären dann die Seiten 5 bis 7, repräsentiert durch die erste Seite des Array, also 5. Ab Seite 10 befindet sich der Index. Dort stehen selbst wieder Seitenzahlen, also zeigt der Zeiger 10 wieder auf Objekte, die eben selbst auch wieder Zeiger sind, mit denen ich dann auf die richtige Seite gelange. Das kann man beliebig so fortsetzen.

Was hat es nun mit 2D Arrays auf sich? Hier bieten sich zwei Optionen, wie man das realisieren kann.

Option 1: Ich kann ein 2D Array wie ein 1D Array interpretieren. Für ein Array mit 3 Zeilen à 5 Elemente bedeutet das, ich habe 15 Seiten in meinem Buch, die ersten 5 Seiten gehören zu Zeile 1, die nächsten zu Zeile 2 und die letzen zu Zeile 3. Der Vorteil ist, ich muss nur einmal Speicher anfordern; der Nachteil ist, ich muss selbst ausrechnen, ab welcher Seite eine Zeile beginnt.

Option 2: Ich benutze einen Index für die Zeilen, also muss ich für mein 3 Zeilen, 5 Spalten Array einen Index mit 3 Seiten haben, auf denen jeweils die Seite steht, an der eine Zeile beginnt. Für jede Zeile brauche ich wieder 5 Seiten, insgesamt also 18 Seiten. Vorteil ist, ich muss nicht selbst Rechnen, wo eine Zeile beginnt, Nachteil ist ich muss selbst sicherstellen, dass für jede Zeile genügend Seiten angefordert werden.

Eigentlich gibt es noch eine dritte Option, als Erweiterung für Option 1. Ich kann mir nämlich auch einmal einen Index berechnen, in dem ich speichere, wo meine Zeilen beginnen und brauche doch nicht selbst rechnen.

Das Prinzip lässt sich beliebig für mehr Dimensionen erweitern, egal welche Option man benutzt.

So, und nun zur eigentlichen Frage:

C-/C++-Quelltext

1
  int array2d[3][5];


In diesem Fall überlasse ich dem Compiler die Entscheidung welche Option benutzt wird und erkaufe mir damit, dass der Compiler für mich die relevanten Berechnungen und Speicheranforderungen durchführt. Wenn ich das Konstrukt also an eine Funktion übergeben will, muss ich dem Compiler auch vollständig mitteilen, wie er den Zugriff zu interpretieren hat.

C-/C++-Quelltext

1
void foo (int array2d_arg[3][5]);


Wenn man Option 1 oder Option 2 benutzt, sieht das wieder ein wenig anders aus.

C-/C++-Quelltext

1
2
void foo_opt1 (int* array_anfang, int zeilen = 3, int spalten = 5);
void foo_opt2 (int** array_index, int zeilen = 3, int spalten = 5);


In beiden Fällen reiche ich die Größe des Array mit in die Funktion, damit ich dort weiss, wie gross das Array tatsächlich ist, und damit ich ggf. auch richtig umrechnen kann (für Option 1).

Version foo_opt1 bekommt einen Zeiger auf den Anfang des Speicherbereichs übergeben, an dem die Daten liegen und muss dann selbst umrechnen, wo ein konkretes Element (x, y) im Array liegt (gewöhlich rechnet man x + (y * spalten)).

Version foo_opt2 bekommt nur den Zeiger auf den Index übergeben und kann über diesen Index dann auch die Zeiger auf das jeweils erste Element der Zeilen bestimmen.

In allen Fällen wird kein einziges Element des Array kopiert, sondern es wird nur die Adresse übergeben, wo die richtigen Informationen zu finden sind. Der Unterschied liegt nur darin, wer und wie man diese Informationen hinterher interpretiert.

So, das ist jetzt eine Menge Text, aber ich hoffe, dass doch noch jemand was daraus lernen kann. ;)

Gruss,
Rainer
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Das Gurke

Community-Fossil

Beiträge: 1 999

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

drakon

Supermoderator

  • »drakon« ist der Autor dieses Themas

Beiträge: 6 526

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

15

21.10.2006, 10:59

@rklaffehn

Vielen Dank für die Mühe!
Ich habe zwar nicht alles zu 100% verstanden, aber wenn ich das jetzt richtig interpretiere, dann kann ich das Beispiel, welches ich weiter oben schon angeführt habe so lassen, da ja nicht das ganze Array hin und her geschoben wird. Es wird auch dort der nur ein Zeiger weitergegeben, und darum muss ich auch noch die Grösse ( im Sinn von : pFeld[2][2] ) mitangeben.

Habe ich das richtig verstanden?

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

16

21.10.2006, 18:52

drakon

Richtig, es wird nur ein Zeiger in der Gegend rumgeschubst, und weil der Compiler alles machen soll, muss man ihm auch sagen, wie's geht.

Compiler sind eben doch dumm :)

Das Gurke

Danke! :D
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

drakon

Supermoderator

  • »drakon« ist der Autor dieses Themas

Beiträge: 6 526

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

17

22.10.2006, 13:14

@rklaffehn

Ok, dann bin ich beruhigt.
Weil auf den ersten Blick, wenn man keine Ahnung hat, sieht es hald so aus, als ob das ganze Array übergeben wird. (Darauf wird eben im Buch C++ für Spieleprogrammierer überhaupt nicht eingegangen.)

Jetzt, da das geklärt ist, darf ich ein wenig Off-Topic gehen, und dich fragen, woher du das so genau weisst?

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

18

22.10.2006, 21:19

Das ist einfach, 20 Jahre Programmiererfahrung. Mit Basic auf dem C64 angefangen, dann Pascal, dann im Studium C/C++ gelernt / angeeignet. Und jetzt verdien ich mein täglich Brot mit Software-Engineering.

Ich bin nur hier, weil mich 3D und Spieleprogrammierung persönlich interessiert. ;)
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

koschka

Community-Fossil

Beiträge: 2 863

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

19

22.10.2006, 21:33

Ab damit ins FAQ!

Man kann auch ein n Dimensionales Array in ein 1D Array pressen. Nur bei der Modifizierung der Daten oder beim auslesen müsste man sich dann mal schnell ein paar Funktionen schreiben.

20

17.06.2016, 23:31

Das mit den mehrdimensionalen Feldern sollte man wieder vergessen.
Hier kann es sehr leicht zu fehlerhaften Verhalten des Programmes kommen.
Als Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
void foo(int *ptr, int zeilen, int spalten);

int array2d[2][2] = { {1, 2}, {3, 4} };
int array3d[2][3] = { {1, 2, 3}, {4, 5, 6} };

foo(&array2d[0][0], 2, 2);
foo(&array3d[0][0], 2, 3);

Was berechnet hier nun die Funktion foo?

Besser ist das ganze in eine Struktur zu packen, das wird übersichtlicher und ist ein wartbarer Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct
{
  int x;
  int y;
}STRUCT_2D;

typedef struct
{
  int x;
  int y;
  int z;
}STRUCT_3D;

typedef struct
{
  int x;
  int y;
  int z;
  DWORD farbe;
}STRUCT_3D_FARBE;

void foo_2d(STRUCT_2D *array, int size);
void foo_3d(STRUCT_3D *array, int size);

Im Programmcode anschließend:

C-/C++-Quelltext

1
2
3
4
5
6
  STRUCT_2D array2d[2] = { {1, 2}, {3, 4}};
  STRUCT_3D array3d[2] = { {1, 2, 3}, {4, 5, 6}};
  foo_2d(array2d, 2);
  foo_3d(array3d, 2);

  foo_3d(array2d, 2);  // << Kompilierungs Fehler!


Hier bekommt man einen Kompilierungsfehler, wenn ein STRUCT_3D erwartet wird, aber eine STRUCT_2D übergeben wird.
Dies kann man allerdings später noch erweitern.
So kann man die Funktion foo_2d Überladen mit einer STRUCT_3D, welche nur die x und y Koordinaten auswertet. Oder man könnte auch die STRUCT_3D mit einem Operator überladen um eine STRUCT_2D zu bekommen.

Werbeanzeige