Forum: Compiler & IDEs dynamisches Array


von Martin Sche. (Gast)


Lesenswert?

Hallo Leute!

Ich muss ein 2D Array dynamisch erzeugen.
Es soll so aussehn:
array[p][k], wobei k eine feste, bekannte Größe ist und p vom Benutzer 
vorher eingeben wird.
Würde das gerne mit alloc machen, aber weiß nicht wie ich dem klar 
mache, dass das Array zweidimensional ist.
Grundsätzlich hät ichs so gemacht:
 int* array;
array=(int*) alloc(p*k,int)

Aber dann hab ich einfach ein eindimensionales Array mit k*p Einträgen, 
oder?

Wäre super, wenn mir da jemand weitrhelfen könnte!

Vielen Dank!

: Verschoben durch User
von Karl H. (kbuchegg)


Lesenswert?

Martin Sche. schrieb:

> Aber dann hab ich einfach ein eindimensionales Array mit k*p Einträgen,
> oder?

Im Computer ist alles einfach nur eine Abfolge von Bytes.
Auch ein 2-dimensionales int Array ist nur eine Abfolge von int im 
Speicher. 2-dimensional wird es erst dadurch, dass die beiden Indizes 
mit einer Formel miteinander verknüpft werden um so die Indexnummer ab 
Beginn der ganzen Geschichte zu ermitteln.

Vielleicht kannst du die Formel ja ermitteln. Dazu malen wir einfach mal 
ein 2 dimensionales Array auf und schreiben in jedes Feld die 1-D 
Indexnummer des int, relativ zum Anfang des Ganzen.



       0   1   2   3   4

     +---+---+---+---+---+
  0  | 0 | 1 | 2 | 3 | 4 |
     +---+---+---+---+---+
  1  | 5 | 6 | 7 | 8 | 9 |
     +---+---+---+---+---+
  2  |10 |11 |12 |13 |14 |
     +---+---+---+---+---+

Das also ist dein 2D Array mit 5 Spalten und 3 Zeilen. Im Speicher liegt 
es so.

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

Wenn du also das Feld mit den 2-d Koordinaten Zeile:1/Spalte:3 suchst, 
dann brauchst du das Feld 8. Das 2-d Feld 2/1 ist an Position 11 zu 
finden, etc.

Frage: Wie muss die Formel aussehen, die die Umrechnung 2-D auf 1-D 
macht?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:

>> Aber dann hab ich einfach ein eindimensionales Array mit k*p Einträgen,
>> oder?
> Im Computer ist alles einfach nur eine Abfolge von Bytes.

Prinzipiell hast Du ja recht, aber eigentlich macht man das anders, als 
Du es andeutest, um ein mehrdimensionales Array zu allokieren.

Nämlich so:
1
#include <stdlib.h>
2
3
main ()
4
{
5
  int p = 5;              // Nur als Beispiel
6
  int k = 7;              // dito
7
  int ** array;           // Achtung: Doppelsternchen, da zweidimensional!
8
  int idx;
9
10
  array = malloc (p * sizeof (int *));  // allokiere p Pointer
11
12
  for (idx = 0; idx < p; idx++)
13
  {
14
      array[idx] = malloc (k * sizeof (int));
15
  }
16
}

Das heisst: Man muss zunächst p Pointer allokieren, denen man im zweiten 
Schritt jeweils ein _ein_dimensionales Array durch weiterere Allokation 
zuweist.

Dann kann man mit der Schreibweise array[x][y] genauso auf das Array 
zugreifen wie auf ein statisch definiertes Array mit festen Größen P und 
K. Klimmzüge (wie Formeln), um auf ein Element des Arrays zuzugreifen, 
sind dann gar nicht erst notwendig.

Gruß,

Frank

von Karl H. (kbuchegg)


Lesenswert?

Frank M. schrieb:
> Karl heinz Buchegger schrieb:
>
>>> Aber dann hab ich einfach ein eindimensionales Array mit k*p Einträgen,
>>> oder?
>> Im Computer ist alles einfach nur eine Abfolge von Bytes.
>
> Prinzipiell hast Du ja recht, aber eigentlich macht man das anders, als
> Du es andeutest, um ein mehrdimensionales Array zu allokieren.

Das kommt drauf an.
Wenn das Array voll besetzt ist, macht man das schon so.
Wenn die einzelnen Zeilen unterschiedliche Länge haben, macht man es auf 
die von dir gezeigte Art. Denn du verbrauchst prinzipiell mehr Speicher 
:-) Wenn das mehrdimensionale Array sehr groß wird, macht man es auf die 
von dir beschriebene Art, denn dann kann einem Speicherfragmentierung 
nicht so leicht einen Strich durch die Rechnung machen.

> Das heisst: Man muss zunächst p Pointer allokieren, denen man im zweiten
> Schritt jeweils ein _ein_dimensionales Array durch weiterere Allokation
> zuweist.

Streng genommen, ist das dann in Summe kein 2D-Array mehr im C-Sinne, 
sondern ein Array von Pointern. Die interne Darstellung ist eine völlig 
andere als bei dem was der C Compiler unter einem 2D Array versteht :-). 
Lediglich durch die clevere Art und Weise, wie Array-Indizierung in C 
definiert ist, geht es, dass für beide Versionen beim Zugriff dieselbe 
Syntax benutzt werden kann.


> Dann kann man mit der Schreibweise array[x][y] genauso auf das Array
> zugreifen wie auf ein statisch definiertes Array mit festen Größen P und
> K. Klimmzüge (wie Formeln), um auf ein Element des Arrays zuzugreifen,
> sind dann gar nicht erst notwendig.

Was nicht unbedingt heißt, das das dann schneller geht :-)

Ob du bei einem echten 2D Array   [x][y]  schreibst, und der Compiler 
erzeugt Code für die eigentliche Indexberechnung, oder ob du das selber 
aufdröselst (weil das Array dynamisch allokiert werden muss), spielt 
laufzeitmässig keine Rolle :-)

Und ausserdem schadet es nicht, wenn man weiß, wie ein echtes 2D Array 
tatsächlich funktioniert und ein wenig nachdenken muss :-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:

> Wenn die einzelnen Zeilen unterschiedliche Länge haben, macht man es auf
> die von dir gezeigte Art. Denn du verbrauchst prinzipiell mehr Speicher

Stimmt, ich "verbrauche" zusätzlich die Pointer.

> Streng genommen, ist das dann in Summe kein 2D-Array mehr im C-Sinne,
> sondern ein Array von Pointern. Die interne Darstellung ist eine völlig
> andere als bei dem was der C Compiler unter einem 2D Array versteht :-).

Richtig, praktisch ist aber der Zugriff über die gewohnte Schreibweise 
"array[x][y]" :-)

> Was nicht unbedingt heißt, das das dann schneller geht :-)

Hm, was zu überprüfen wäre. Du benötigst eine zusätzliche 
Multiplikation, ich einen indirekten Zugriff über Pointer.

Ich habe das mal mit dem AVR-GCC getestet und habe den Array-Zugriff in 
2 Funktionen gepackt, um Optimierungen des Compilers (Wissen über die 
Werte von x, y, k) auszuschließen:
1
int
2
value_of_1 (int ** a, int x, int y)     // meine Version
3
{
4
  return a[x][y];
5
}
6
7
int k = 7;
8
9
int
10
value_of_2 (int * a, int x, int y)     // Deine Version
11
{
12
  return a[k*x + y];
13
}

Der produzierte Assembler-Code (test.lss) sieht folgendermaßen aus:
1
int
2
value_of_1 (int ** a, int x, int y)
3
{
4
  76:  fb 01         movw  r30, r22
5
  78:  ee 0f         add  r30, r30
6
  7a:  ff 1f         adc  r31, r31
7
  7c:  e8 0f         add  r30, r24
8
  7e:  f9 1f         adc  r31, r25
9
  80:  44 0f         add  r20, r20
10
  82:  55 1f         adc  r21, r21
11
  84:  01 90         ld  r0, Z+
12
  86:  f0 81         ld  r31, Z
13
  88:  e0 2d         mov  r30, r0
14
  8a:  e4 0f         add  r30, r20
15
  8c:  f5 1f         adc  r31, r21
16
  return a[x][y];
17
}
18
  8e:  80 81         ld  r24, Z
19
  90:  91 81         ldd  r25, Z+1  ; 0x01
20
  92:  08 95         ret
21
22
00000094 <value_of_2>:
23
24
int k = 7;
25
26
int
27
value_of_2 (int * a, int x, int y)
28
{
29
  94:  fc 01         movw  r30, r24
30
  96:  cb 01         movw  r24, r22
31
  98:  9a 01         movw  r18, r20
32
  9a:  60 91 60 00   lds  r22, 0x0060
33
  9e:  70 91 61 00   lds  r23, 0x0061
34
  a2:  86 d1         rcall  .+780      ; 0x3b0 <__mulhi3>
35
  a4:  28 0f         add  r18, r24
36
  a6:  39 1f         adc  r19, r25
37
  a8:  22 0f         add  r18, r18
38
  aa:  33 1f         adc  r19, r19
39
  ac:  e2 0f         add  r30, r18
40
  ae:  f3 1f         adc  r31, r19
41
  return a[k*x + y];
42
}
43
  b0:  80 81         ld  r24, Z
44
  b2:  91 81         ldd  r25, Z+1  ; 0x01
45
  b4:  08 95         ret

Die Anzahl der Instruktionen sieht erstmal gleich aus, aber bei näherem 
Hinsehen fällt bei Deiner Version der rcall von _mulhi3 auf, der noch 
einiges an zusätzlichem Code kostet:
1
000003b0 <__mulhi3>:
2
 3b0:  55 27         eor  r21, r21
3
 3b2:  00 24         eor  r0, r0
4
5
000003b4 <__mulhi3_loop>:
6
 3b4:  80 ff         sbrs  r24, 0
7
 3b6:  02 c0         rjmp  .+4        ; 0x3bc <__mulhi3_skip1>
8
 3b8:  06 0e         add  r0, r22
9
 3ba:  57 1f         adc  r21, r23
10
11
000003bc <__mulhi3_skip1>:
12
 3bc:  66 0f         add  r22, r22
13
 3be:  77 1f         adc  r23, r23
14
 3c0:  61 15         cp  r22, r1
15
 3c2:  71 05         cpc  r23, r1
16
 3c4:  21 f0         breq  .+8        ; 0x3ce <__mulhi3_exit>
17
 3c6:  96 95         lsr  r25
18
 3c8:  87 95         ror  r24
19
 3ca:  00 97         sbiw  r24, 0x00  ; 0
20
 3cc:  99 f7         brne  .-26       ; 0x3b4 <__mulhi3_loop>
21
22
000003ce <__mulhi3_exit>:
23
 3ce:  95 2f         mov  r25, r21
24
 3d0:  80 2d         mov  r24, r0
25
 3d2:  08 95         ret

Damit nehme ich mal ein, dass meine Version mehr als doppelt so schnell 
auf ein 2-dimensionales Array zugreifen kann - jedenfalls bei AVR-µCs. 
Das kann natürlich bei Prozessoren, für die eine int-Multiplikation ein 
Klacks ist, ganz anders aussehen.

> Und ausserdem schadet es nicht, wenn man weiß, wie ein echtes 2D Array
> tatsächlich funktioniert und ein wenig nachdenken muss :-)

Da hast Du natürlich vollkommen recht :-)

Gruß,

Frank

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Richtig, praktisch ist aber der Zugriff über die gewohnte Schreibweise
> "array[x][y]" :-)

Das geht auch bei linearer Anordnung:
1
int
2
value_of_3 (int a[][5], int x, int y)
3
{
4
  return a[x][y];
5
}

Braucht allerdings auch eine Mulitiplikation, sofern nicht die
letzte Dimension eine Zweierpotenz ist.

Letztlich ist der Funktion egal, ob der Aufrufer den Speicherplatz
als (echtes) zweidimensionales Array oder via malloc() gewonnen hat.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:

> Das geht auch bei linearer Anordnung:
>
>
1
> int
2
> value_of_3 (int a[][5], int x, int y)
3
> {
4
>   return a[x][y];
5
> }
6
>

Stimmt.

> Braucht allerdings auch eine Mulitiplikation, sofern nicht die
> letzte Dimension eine Zweierpotenz ist.

Ja. Und die Funktion muss zusätzlich eine Dimension des Arrays kennen. 
Wenn aber beide Dimensionen dynamisch sind, scheidet diese Variante aus.

Gruß,

Frank

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Ja. Und die Funktion muss zusätzlich eine Dimension des Arrays kennen.
> Wenn aber beide Dimensionen dynamisch sind, scheidet diese Variante aus.

Nein, dann nehmen wir ein C99 variable-length array. :-)
1
int
2
value_of_3 (int x, int y, int n, int m, int a[n][m])
3
{
4
  return a[x][y];
5
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:

> Nein, dann nehmen wir ein C99 variable-length array. :-)

Nett, kommt aber auch nicht um die Plutimikation herum :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Nett, kommt aber auch nicht um die Plutimikation herum :-)

Logisch, aber die würde auch das originale zweidimensionale Array
halt machen, wenn man es statisch im Compiler anlegt (sofern nicht
die zweite Dimension 2^N ist).

Ich wollte ja auch keinesfalls bestreiten, dass der Vektor aus Zeigern
seine Vorteile hat; die hat er vor allem dann, wenn die Länge der
einzelnen Elemente nicht konstant ist (Vektor aus Zeigern, die auf
Zeichenkettenkonstanten zeigen zum Bleistift).

von Klaus W. (mfgkw)


Lesenswert?

geht es hier eigentlich um C auf einem MC oder um etwas größeres?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.