Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

ERROR

Alter Hase

  • »ERROR« ist der Autor dieses Themas

Beiträge: 417

Wohnort: Paderborn

Beruf: Informatik Student

  • Private Nachricht senden

1

12.11.2013, 10:36

C++ 2D Array

Wenn es das Ziel ist ein 2D Array zu erstellen und zu zeichen(Beispielweise eine Map für ein 2D RPG).
Die Map soll so aussehen am Ende:
[],[],[],[],[]
[],[],[],[],[]
[],[],[],[],[]
welche Art der Erstellung würdet ihr bevorzugen?

Die Erste:

C-/C++-Quelltext

1
2
3
4
int x=5;
int y=3;

int arr[x][y];


Die Zweite:

C-/C++-Quelltext

1
2
3
4
int x=3;
int y=5;

int arr[y][x];


Es ist ja klar, dass man je nach Methode dabei bleiben muss und beim Zeichen usw die gleiche Art bei behalten muss.

Das Problem dabei ist, dass ich diese immer mit [x][y] befülle, unser Lehrer aber sagt er macht es genau andersrum.

2

12.11.2013, 10:43

Ich bevorzuge letztere, da ich eigentlich immer im Kopf habe, dass zuerst die rows und dann die columns kommen. Einen wirklichen Standard gibt es da meines Wissens auch nicht, hauptsache du wirfst es nicht zwischen durch durcheinander.

Fireball

Alter Hase

Beiträge: 415

Wohnort: Werne

Beruf: Dipl. Inf.

  • Private Nachricht senden

3

12.11.2013, 10:59

Hallo,

es passt zwar nicht so ganz, aber ich werfe es trotzdem mal in den Raum.

Da man 2D Arrays in C++ so schlecht übergeben kann, bin ich irgendwann dazu über gegangen alles in einem 1D Array abzubilden.

C-/C++-Quelltext

1
2
3
4
5
6
int array[width * height];

 int SetElement(int row, int col, int value)
 {
    array[width * row + col] = value;  
 }


Schöne Grüße

fb

4

12.11.2013, 11:30

Arrays will man eh nicht mehr benutzen. Eigentlich möchte man std::vector benutzen, wenn die Größe statisch sein soll, ist auch std::array eine gute Idee (wobei ich es für wahrscheinlich halte, dass man unterschiedliche Größen unterstützen möchte, d.h. eher std::vector).

Hier hat man jetzt auch wieder die Wahl, ob man 2 vectoren verschachtelt, oder einen eindimensionalen benutzt. Der Vorteil des eindimensionalen ist der, dass man nur ein vector Objekt mit einem großen Speicherblock hat. Man kann dann ähnliche Zugriffsfunktionen wie die von Fireball benutzen. Ein wenig Cooler ist aber so etwas:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// stark gekürzt, nur damit man die Idee bekommt
class NdVector
{
  std::vector<int> Data;
  int Width; int Height;

  int& Access(int x, int y)
  {
    return Data[x+y*Width];
  }
}

NdVector blub;
blub.Access(5, 7)=27

Man gibt also eine Referenz auf das Objekt zurück, so dass man mit den Elementen ganz normal rechnen kann und sie nicht holen, manipulieren und dann wieder setzen muss.
Wenn man noch cooler sein möchte, kann man sich auch ein Proxy-Objekt bauen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// (wieder auf das wesentliche gekürzt)
class NdVector
{
  class Proxy
  {
    Proxy(std::vector<int>& vec, int Offset)
    int& operator[](int x)
    {  return vec[Offset+x]; }
  }

  std::vector<int> Data;
  int Width; int Height;

  Proxy operator[](int y)
  {
    return Proxy(Data, y*Width);
  }
}

NdVector BLub;
BLub[y][x]=1235623

Das ist ein hübscher Trick, wie man mit etwas mehr Aufwand in der Klasse, ein deutlich angenehmeres Interface hinbekommt. Natürlich würde man das noch erweitern und als Template bauen und so weiter. Das ist eine sehr schöne Übung, allerdings hat das alles natürlich schon jemand gemacht:


http://www.boost.org/doc/libs/1_55_0/lib…y/doc/user.html


(Das waren jetzt einige, sagen wir, fortgeschrittene C++ Techniken, aber es zeigt, was man letztendlich alles so machen kann.)
Lieber dumm fragen, als dumm bleiben!

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

5

12.11.2013, 12:04

Wozu ein std::vector, wenn es ein std::array auch täte?
std::array<std::array<T, y>, x>
Darüber lässt sich aber eindeutig streiten. Letztlich ist Vector intern fast dasselbe und ist flexibler.
T[] zu verwenden ist meist aber nicht mehr state of the art.

Ich bin übrigens für [x][y]. Das entspricht jeder Call-Convention bei Grafik-Bilbiotheken, die immer erst x und dann y übergeben. Alles andere ist verwirrend.
Allerdings muss ich auch sagen, dass Marble Theory genau andersrum mit Map.Colums[y].Row[x] arbeitet ;) Da das aber eindeutig ist, was Rows und was Columns sind, sehe ich hier kein Problem mit Verwirrung.
Falls Deine Algorithmen immer in Loops zeilenweise wie ein Drucker/Monitor arbeiten, ist es für Caching allerdings besser es [y][x] zu bauen.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »BlueCobold« (25.11.2013, 07:17)


Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

6

13.11.2013, 13:43

Ich stimme da BlueCobold zu. Habe da am Anfang selbst teilweise Probleme gehabt, weil ich gehört und gelesen habe dass

C-/C++-Quelltext

1
Map[y][x]

besser sein soll. Die Erklärung ist wie BlueCobold auch anreißt die Variante wie man über die Struktur iteriert. Wenn man nicht groß nachdenkt schreibt man sowas wie:

C-/C++-Quelltext

1
2
3
4
5
6
7
for(int y = 0; y < height; y++)
{
    for(int x = 0; y < width; x++)
    {
        // mach was mit Map[x][y]
    }
}


Das ist nicht sonderlich effizient. Das liegt an der internen Darstellung des 2D Arrays, welches am Ende ja einfach zusammenhängender Speicher ist. Bei dieser Variante der Indizes sollte man nicht zeilen- sondern spaltenweise iterieren. Das ist am Ende auch schon alles. Ich mag die Schreibweise von Map[x][y] da es meinem Verständnis von Koordinatenübergabe entspricht. Erst kommt die X Koordinate, dann die Y Koordinate. Gewöhn dir am besten direkt eine Variante an damit du nicht durcheinander kommt. Welche Datenstruktur man dann am Ende jeweils wählt hängt natürlich vom Problem ab. Das wäre aber auch erst mal unabhängig hiervon.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

ERROR

Alter Hase

  • »ERROR« ist der Autor dieses Themas

Beiträge: 417

Wohnort: Paderborn

Beruf: Informatik Student

  • Private Nachricht senden

7

13.11.2013, 14:36

@Jonathan: Die Sache mit dem Vector benutze ich teilweise schon :)

@Schorsch: Die Methode benutze ich so, also das mit der for, in der erst y dann x kommt. Wieso genau ist das denn so ineffizient?

Also eigentlich benutze ich solche 2 Dimensionalen Sachen vor allem zum Speichern von tiles.

Auch wenn mein Code bei weitem nicht perfekt ist, bin ich natürlich daran interessiert, welche Art die effizienteste ist. Ist da wirklich so ein grosser unterschied, zwischen dem Weg von Jonathan und dem von Schorsch (den ich derzeit benutze)?

Und um beim eigentlichen Thema zu bleiben, ich bevorzuge eigentlich auch die Art [x][y], einfach weil es von den Koordinaten her so richtig ist. Wenn man Jonathans Idee benutzt, kann ich es ja weiterhin mit [x][y] ansprechen, nur habe eine bessere Speichernutzung?

KeksX

Community-Fossil

Beiträge: 2 107

Beruf: Game Designer

  • Private Nachricht senden

8

13.11.2013, 14:50

Grundsätzlich: Verwende immer std container. Die sind, im Zweifelsfall, immer effektiver und auch einfacher zu verwenden. Außerdem wirst du sie später, wenn du mal größere Sachen programmierst, eh nur noch verwenden wollen und da macht es keinen Sinn, sich jetzt andere Sachen anzugewöhnen. Kann mich schon gar nicht mehr daran erinnern, wann ich zuletzt ein normales array verwendet habe.
WIP Website: kevinheese.de

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

9

13.11.2013, 15:58

Das mit den STD Containern ist schon richtig. Die sind um einiges moderner und da solltest du dich wirklich dran gewöhnen. Wirklich komplizierter sind sie für dich eigentlich nicht. Im Endeffekt sind sie eigentlich sogar um einiges leichter zu benutzen. Was den Laufzeitunterschied angeht, so kannst du das selbst ganz leicht testen. Iterier doch einfach mal über ein 2D Array und weise einfach nur einfache Werte zu. Miss die Zeit die dafür gebraucht wird. Dann machst du das selbe mit der anderen Variante:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
startzeit = jetzt
for x
    for y
        array[x][y] = 1;
    endfor y
endfor x
endzeit = jetzt

startzeit2 = jetzt
for y
    for x
        array2[x][y] = 1
    endfor x
endfor y
endzeit2 = jetzt

ausgabe endzeit - startzeit
ausgabe endzeit2 - startzeit2

So in etwa wäre der Pseudocode. Übersetzen kannst du dir das ganze ja relativ einfach. Warum das eine schneller ist als das andere liegt an den einzelnen Speicherzugriffen. In einem Array liegen die Werte direkt hintereinander im Speicher. Wenn du nun ein Array von Arrays machen würdest, so ist der gesamte Speicher auch am Stück. Nur entweder läufst du den gesamten Speicher von vorne durch, oder du springst immer hin und her. Je nachdem in welcher Reihenfolge du durch das Array iterierst. Mal dir das ganze mal auf, dann wirds vielleicht deutlicher. Was das ausmachst kannst du selbst sehen wenn du mal den Code oben als Programm umsetzt. Wichtig ist nur, mach die Arrays groß genug. Je größer, desto deutlicher siehst du den Unterschied.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

10

13.11.2013, 19:12

Der eigentliche Grund ist aber, dass Speicherzugriffe nicht immer gleich lange dauern.

Um den Zugriff auf den Hauptspeicher zu beschleunigen, verfügen Prozessoren über Caches, kleine Zwischenspeicher. Davon kann es auch mehrere Stufen geben, und ganz am Ende stehen Daten in den Registern des Prozessors, wo der Zugriff am schnellsten ist.
Da es wenig Sinn macht, jedes Datum einzeln in den Cache zu laden, und da man beobachtet, dass Daten die im Speicher eng zusammen liegen, auch meistens in ihrer Benutzung eng zusammen liegen, schreibt man sie sozusagen schon auf verdacht in den Cache.
Iterierst du also so über die Daten, wie sie im Speicher liegen, hast du die optimale Performance. Benutzt du einen eher wahlfreien zugriff (so sieht es zumindest aus, wenn du in umgekehrter Reihenfolge iterierst), musst du andauernd andere Daten in den Cache laden, was langsamer ist. Das Stichwort wäre Cache-Miss. Mehr dazu hier:
https://de.wikipedia.org/wiki/Cache

Der einzige Grund, weshalb ich [y][x] geschrieben habe, ist übrigens der, dass mein Proxy-Objekt eben so herum funktionierte. Wer aber das Prinzip verstanden hat, kann den Code natürlich umschreiben, so dass man am Ende [x][y] benutzten kann. Zu beachten ist, dass sich dadurch das Speicherlayout nicht ändert, da einfach nur die Position anders berechnet wird.
Lieber dumm fragen, als dumm bleiben!

Werbeanzeige