Hallo und Frohe Weihnachten,
ich kämpfe mal wieder mit den Zeiger in C und der Syntax. Weiss zwar was
ich will aber nicht wie ich das dem Compiler begreiflich machen soll.
Ich habe zwei Felder, die ich aus lauter Not schon in einen Struct
zusammengefasst habe:
Typdefintion
struct feld_type {
char last[MAX_X+1][MAX_Y+1];
char next[MAX_X+1][MAX_Y+1];
};
Funktion 2 {
muss feld bearbeiten
}
Funktion 1 (Feldübergabe)
{
Funktion 2 (Feld durchreichen)
}
später in main()
void main(void)
{
struct feld_type feld;
.....
Funktion_1(Feldübergabe)
...
}
Nun ist aber noch Funktion 1 Aufrufer von Funktion 2 und auch Funktion
2 braucht das Feld zum Bearbeiten, d.h. es muss nochmal durchgereicht
werden.
An dem trip ohne struct, also Zeiger auf mehrdimensionale Arrays zu
übergeben bin ich gescheitert, keine Ahnung ob void* oder void**, ob
&feld[0] oder &feld[0][0] oder nur &feld. Daher wollte ich den struct
drumherum lassen
Könnte das mal jemand mit Beispiel gut verständlich erklären wie ich die
beiden Felder im struct durchreiche (keine Kopie!).
Müsste so gehen.
1 | typedef struct _feld_t |
2 | {
|
3 | char last[MAX_X+1][MAX_Y+1]; |
4 | char next[MAX_X+1][MAX_Y+1]; |
5 | }feld_t; |
6 | |
7 | |
8 | Funktion_2 (feld_t* pFeld) |
9 | {
|
10 | z=pFeld->last[x][y]; |
11 | } |
12 | |
13 | Funktion 1 (feld_t* pFeld) |
14 | {
|
15 | Funktion_2(pFeld); //Pointer übergeben |
16 | } |
17 | |
18 | void main(void) |
19 | {
|
20 | feld_t feld; |
21 | |
22 | |
23 | Funktion_1(&feld); //Adresse von "feld" übergeben |
24 | } |
Bingo! Läuft durch und ergibt das richtige! Allerdings bläst sich mein Stack jetzt gewaltig auf wegen der dynamischen Speicherallokation durch die Arrays und nähert sich bedrohlich nahe dem Code Ende. Ist bei mir ehg so ne Frage: Was ist besser? Speicher vom Stack holen? Speicher vom Heap holen? Oder globale Vars hier verwenden, die dann im Datensegment erzeugt werden, statt auf dem Stack? Ist dem Mikrocontroller nämlich wumpe, der hat nur 40k und wie man die aufteilt ist dem egal.
Christian J. schrieb: > Ist bei mir ehg so ne Frage: Was ist besser? Speicher vom Stack > holen? Speicher vom Heap holen? Oder globale Vars hier verwenden, > die dann im Datensegment erzeugt werden, statt auf dem Stack? Ist > dem Mikrocontroller nämlich wumpe, der hat nur 40k und wie man die > aufteilt ist dem egal. Damit beantwortest du die Frage ja schon selbst. Wie du deine Arrays anlegst, ist "wumpe", denn sie werden durch andere Art des Anlegens nicht plötzlich weniger Platz brauchen. Einzig das dynamische Erzeugen braucht ein paar Bytes mehr Platz, da die dazugehörigen Verwaltungsinformationen noch irgendwo gespeichert werden müssen.
Was würdest du denn als "geschwindigkeitsoptimale" Lösung sehen? Ich
codiere grad aus Spass "Game of Life" auf einem 4 Mhz Z80 und da ist es
nunmal etwas langsamer unterwegs.
Würdest du cell_status eleminieren und in den Aufrufer rein packen? Der
Code wird dadurch etwas schwerer lesbar aber die Funktion wird nur
einmal aufgerufen von einem einzigen Aufrufer.m
Unterschied beim Kompileren sind nur 20 Bytes mehr, wenn es eine
einzelne Funktion bleibt.
Im Kommentarfeld ist die Funktion einzeln reinkopiert, d.h. sie kann
gelöscht werden.
/ Berechne den neue Zellstatus
static enum cell_stat cell_status(struct feld_t *feld, char xpos, char
ypos)
{
char y,x;
uint8_t livecells; // Zustand der 8 Nachbarn
livecells = 0;
// Ermittle den Zustand der 8 Nachbarn
for (y = ypos-1;y <= (ypos+1);y++) {
for(x = xpos-1;x <= (xpos+1);x++)
if (feld->last[x][y] == ALIVE)
if (++livecells > 4)
return (DEAD);
}
// Sich selbst als Nachbar abziehen
if (feld->last[xpos][ypos] == ALIVE)
livecells--;
// Lebt + 2 Nachbarn = lebt weiter, Tot/Lebt + 3 Nachbarn = Lebt
(wieder)
return((feld->last[xpos][ypos] && (livecells == 2)) || (livecells ==
3));
}
// Berechne die nächste Generation
uint16_t calc_nextgen(struct feld_t *feld)
{
uint8_t x,y,ypos,xpos;
uint16_t mods;
uint8_t livecells; // Zustand der 8 Nachbarn
mods = 0;
n_cells = 0;
// Raender des Feldes nicht betrachten
for (y=1;y < MAX_Y;y++) {
for (x=1;x < MAX_X;x++) {
// Berechne neuen Status einer Zelle
feld->next[x][y] = cell_status(feld, x,y);
/*
// Ermittle den Zustand der 8 Nachbarn
livecells = 0;
for (ypos = y-1;ypos <= (y+1);ypos++) {
for(xpos = x-1;xpos <= (x+1);xpos++)
if (feld->last[xpos][ypos] == ALIVE)
livecells++;
}
// Sich selbst als Nachbar abziehen
if (feld->last[x][y] == ALIVE)
livecells--;
// Lebt + 2 Nachbarn = lebt weiter, Tot/Lebt + 3 Nachbarn =
Lebt (wieder)
feld->next[x][y] = (feld->last[x][y] && (livecells == 2)) ||
(livecells == 3);
*/
// Lebende Zellen zaehlen
if (feld->next[x][y]==ALIVE)
n_cells++;
// Bei Veränderung neu zeichnen
if (feld->next[x][y] != feld->last[x][y])
{
move(MAX_Y-y,x);
if (feld->next[x][y] == ALIVE)
addch('O');
else
addch(' ');
mods++;
}
}
}
// Altes Feld = Neues Feld
memcpy(feld->last,feld->next,sizeof(feld->next));
// Anzahl Veränderungen zurück
return (mods);
}
Evtl. mal das komplette Programm posten.
Wumpe schrieb: > Evtl. mal das komplette Programm posten. Hat sich schon erledigt.... es ist effizienter aus allem eine Funktion zu machen, da der Übergabekram wegfällt. Das war übrigens der ganze Code.
Christian J. schrieb: > Das war übrigens der ganze Code. Ich hatte den anderen Thread nicht gesehen.
Wumpe schrieb: > Christian J. schrieb: >> Das war übrigens der ganze Code. > > Ich hatte den anderen Thread nicht gesehen. Weisst Du trotzdem vielleicht wie man mehrdim Array als Zeiger übergibt? Kommt vielleicht bald mal wieder vor. Die Kapselung in einen Struct, nur weil ich nicht wusste wie es sonst geht ist ja sicher keine Dauerlösung sondern nur aus der Not geboren.
1 | void foo(char *pArray) |
2 | {
|
3 | char x = 4; |
4 | char y = 5; |
5 | //grenzen von array überprüfen
|
6 | pArray[x][y]; |
7 | }
|
8 | char last[MAX_X+1][MAX_Y+1]; |
9 | |
10 | foo(last); |
Der Compiler weiss aus dem Zeigertyp (hier: zeiger auf typ char) wie die Adressen im arras zu inkrementieren sind.
icke schrieb: > Der Compiler weiss aus dem Zeigertyp (hier: zeiger auf typ char) wie die > Adressen im arras zu inkrementieren sind. Das ist so nicht kompilierbar. Gleich 2 Fehlermeldungen im Aufruf und der Funktion.
1 | void foo(char (*par)[20]) |
2 | {
|
3 | /*...*/
|
4 | par[9][19] = 42; |
5 | /*...*/
|
6 | }
|
7 | |
8 | void bar() |
9 | {
|
10 | char a[10][20]; |
11 | foo(a); |
12 | }
|
http://ideone.com/AEohFt
Christian J. schrieb: > Weisst Du trotzdem vielleicht wie man mehrdim Array als Zeiger übergibt? > Kommt vielleicht bald mal wieder vor. Weitaus besser ist es, sich erst einmal klarzumachen, daß es in C keine mehrdimensionalen Felder gibt - und so etwas künftig besser zu vermeiden. Rate mal, was der Ausdruck char x[a][b]; eigentlich beinhaltet. x ist nämlich ein Array von Zeigern, die jeweils auf ein Array von Chars zeigen. Es ist eben KEIN mehrdimensionales Feld von Chars. Wenn du das verstanden hast, dann wird dir damit auch klar, was du mit deinem Versuch unter der Decke des Compilers eigentlich anrichtest. Man kann das tun, aber es ist nicht wirklich das Beste. mein Vorschlag: arbeite lieber mit eindimensionalen Feldern und adressiere dort per Berechnung, also eher #define am 100 #define bm 50 char x[am*bm]; und if (x[12+34*bm]==125) blabla(); W.S.
W.S. schrieb: > Rate mal, was der Ausdruck > char x[a][b]; > eigentlich beinhaltet. > x ist nämlich ein Array von Zeigern, die jeweils auf ein Array von Chars > zeigen. Das ist nicht wahr. x ist ein Array von a Arrays von b chars. Keine Zeiger weit und breit.
Christian J. schrieb: > Ist dem > Mikrocontroller nämlich wumpe, der hat nur 40k Christian J. schrieb: > Ich > codiere grad aus Spass "Game of Life" auf einem 4 Mhz Z80 Dann wäre die allererste Massnahme, nicht char sondern einzelne Bits zu verwenden - das reduziert den Speicherbedarf schon mal um den Faktor 8. Dann muss man die Zugriffe sowieso neu überdenken. Und was die Geschwindigkeit angeht, für die Überprüfung einer Zelle wäre eine Assemblerfunktion unschlagbar schnell. Besonders wenn die Arraygrössen 2er Potenzen sind. Georg
Georg schrieb: > Dann wäre die allererste Massnahme, nicht char sondern einzelne Bits 64kb sollten genug sein... und Bits mag er nicht, eher Bytes, dafür ist der Befehlssatz ausgelegt worden. Inline Asm hat leider das problem, dass du nicht auf die lokalen Vars zugreifen kannst, weil niemand weiss wie die im Stack grad liegen. Alptraum sich mit dem Frame Pointer anzulegen..... Die Idee mit dem 1D Feld werde ich mal angehen, erfordert dann nur Hirnschmalz sich das Ganze trotzdem 2D vorzustellen, 2D Array war so schön einfach das zu kodieren.
Der hier codiert es allerdings auch 2D: https://www.pdc.kth.se/education/tutorials/summer-school/mpi-exercises/mpi-lab-codes/game_of_life-serial.c/view und viele andere Beispiele auch..
Christian J. schrieb: > und Bits mag er nicht, eher Bytes, dafür ist > der Befehlssatz ausgelegt worden. Wenn das alles ist was du über den Z80 weisst ist dir nicht zu helfen. Georg
Georg schrieb: > Wenn das alles ist was du über den Z80 weisst ist dir nicht zu helfen. Ich möchte das aber nicht in Bits codieren, da ich einen C Compiler verwende und keinen Assembler, ok?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.