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!
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?
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
intp=5;// Nur als Beispiel
6
intk=7;// dito
7
int**array;// Achtung: Doppelsternchen, da zweidimensional!
8
intidx;
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
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 :-)
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,intx,inty)// meine Version
3
{
4
returna[x][y];
5
}
6
7
intk=7;
8
9
int
10
value_of_2(int*a,intx,inty)// Deine Version
11
{
12
returna[k*x+y];
13
}
Der produzierte Assembler-Code (test.lss) sieht folgendermaßen aus:
1
int
2
value_of_1(int**a,intx,inty)
3
{
4
76:fb01movwr30,r22
5
78:ee0faddr30,r30
6
7a:ff1fadcr31,r31
7
7c:e80faddr30,r24
8
7e:f91fadcr31,r25
9
80:440faddr20,r20
10
82:551fadcr21,r21
11
84:0190ldr0,Z+
12
86:f081ldr31,Z
13
88:e02dmovr30,r0
14
8a:e40faddr30,r20
15
8c:f51fadcr31,r21
16
returna[x][y];
17
}
18
8e:8081ldr24,Z
19
90:9181lddr25,Z+1;0x01
20
92:0895ret
21
22
00000094<value_of_2>:
23
24
intk=7;
25
26
int
27
value_of_2(int*a,intx,inty)
28
{
29
94:fc01movwr30,r24
30
96:cb01movwr24,r22
31
98:9a01movwr18,r20
32
9a:60916000ldsr22,0x0060
33
9e:70916100ldsr23,0x0061
34
a2:86d1rcall.+780;0x3b0<__mulhi3>
35
a4:280faddr18,r24
36
a6:391fadcr19,r25
37
a8:220faddr18,r18
38
aa:331fadcr19,r19
39
ac:e20faddr30,r18
40
ae:f31fadcr31,r19
41
returna[k*x+y];
42
}
43
b0:8081ldr24,Z
44
b2:9181lddr25,Z+1;0x01
45
b4:0895ret
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:5527eorr21,r21
3
3b2:0024eorr0,r0
4
5
000003b4<__mulhi3_loop>:
6
3b4:80ffsbrsr24,0
7
3b6:02c0rjmp.+4;0x3bc<__mulhi3_skip1>
8
3b8:060eaddr0,r22
9
3ba:571fadcr21,r23
10
11
000003bc<__mulhi3_skip1>:
12
3bc:660faddr22,r22
13
3be:771fadcr23,r23
14
3c0:6115cpr22,r1
15
3c2:7105cpcr23,r1
16
3c4:21f0breq.+8;0x3ce<__mulhi3_exit>
17
3c6:9695lsrr25
18
3c8:8795rorr24
19
3ca:0097sbiwr24,0x00;0
20
3cc:99f7brne.-26;0x3b4<__mulhi3_loop>
21
22
000003ce<__mulhi3_exit>:
23
3ce:952fmovr25,r21
24
3d0:802dmovr24,r0
25
3d2:0895ret
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
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(inta[][5],intx,inty)
3
{
4
returna[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.
Jörg Wunsch schrieb:
> Das geht auch bei linearer Anordnung:>>
1
>int
2
>value_of_3(inta[][5],intx,inty)
3
>{
4
>returna[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
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. :-)
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).