Forum: Mikrocontroller und Digitale Elektronik Structs in Struct packen, Menü-Aufbau


von Tueftler (Gast)


Lesenswert?

Guten Morgen, werte Bastlerfreunde und Profis!

Ich beschäftige mich momentan mit dem Thema MenuStruktur und damit 
einhergehend auch mit Strukturen in C.
Bin noch relativ neu im gesamten Themenkreis, besonders Structs.

Nun stehe ich vor dem Problem, dass ich gerne per Variable auf eine 
Struktur zugreifen will:
Ich habe zwei Strukturen von folgendem Aufbau (in Anlehnung an die 
MC-FAQ)
1
struct Menueintrag
2
{
3
  char   Text[17];
4
  VoidFnct Function;
5
};
6
7
struct Menueintrag Hauptmenu[] = {
8
{"++Haupt-Menue++"},    
9
{"Einstellungen", settings},
10
{"    Start", begin},
11
{"   Abbruch", abort},
12
{"Sel Up Down Info"}
13
};
14
15
struct Menueintrag Einstellungen[]={
16
  {"+Einstellungen+"},
17
  {"Set Frequency", setFreq},
18
  {"  Set Amount", setAmount},
19
  {" Set Interval", setInterval},
20
  {"Sel Up Down Info"}
21
};

Nun möchte ich gerne in einer Funktion namens
1
void RefreshScreen(struct Menueintrag Menu[])
das Aktuelle Menü auf eineme Display ausgeben. die Ausgabe funktioniert 
auch schon.
Ich kann also momentan über einen Aufruf der Form
1
RefreshScreen(Hauptmenu);
Das jeweilige Menü darstellen.
Nun ist dies jedoch sehr unpraktisch.
Ich würde gerne diese beiden Menüs (Structs) in ein Array oder in eine 
Struct packen, um dann per Index das jeweilige Menü benennen zu können. 
Also etwa in der Form:
1
RefreshScreen(Menus(1));
oder
1
 
2
RefreshScreen(Menus(aktuellesMenu));
Das hätte den Vorteil, dass ich damit eine Variable definieren kann, die 
das aktuell anzuzeigende Menü repräsentiert.

Könnt Ihr mir da helfen?
Ich habe mir bereits zahlreiche Beiträge zu Structs durchgelesen, bin 
aber noch nicht auf einen grünen Zweig gekommen, Sehe momentan den Wald 
vor Bäumen nicht.
Mit Dank, Tüftler

von Remote O. (remote1)


Lesenswert?

Tueftler schrieb:
> Nun ist dies jedoch sehr unpraktisch.
> Ich würde gerne diese beiden Menüs (Structs) in ein Array oder in eine
> Struct packen, um dann per Index das jeweilige Menü benennen zu können.


warum packst du das nicht wieder in eine neue struct. Dann kannst du 
bequem mit dem "." referenzieren.

von Karl H. (kbuchegg)


Lesenswert?

Tueftler schrieb:
> Ich beschäftige mich momentan mit dem Thema MenuStruktur und damit
> einhergehend auch mit Strukturen in C.
> Bin noch relativ neu im gesamten Themenkreis, besonders Structs.
>
> Nun stehe ich vor dem Problem, dass ich gerne per Variable auf eine
> Struktur zugreifen will:
> Ich habe zwei Strukturen von folgendem Aufbau (in Anlehnung an die
> MC-FAQ)

Du bist noch ein wenig zu sehr dem Array verpflichtet

Ein Menü ist noch etwas mehr als eine Abfolge von Menüpunkten in einem 
Array :-). Zb hat ein Menü einen Titel (der aber selbst kein 
auswählbarer Menüpunkt ist) und es hat eine Anzahl von Menüpunkten (wie 
stellen denn deine verarbeitenden Funktionen fest, wieviele Menüpuntke 
es gibt?)

(Ausserdem solltest du dir angewöhnen, für Texte, die sich nicht ändern, 
keine char-Arrays zu allokieren, sondern ganz einfach nur einen Pointer 
auf const char einzubauen. Zum einen spart das Speicher, zum anderen 
wirst du es dir danken wenn du anfängst die Texte ins Flash auszulagern)

struct Menueintrag
{
  const char* Text;
  VoidFnct    Function;
};

struct Menu
{
  const char*         Title;
  uint8_t             NrEntries;
  struct Menueeintrag Entries[];
};

struct Menu HauptMenu =
{  "++Haupt-Menue++",
   3,
   { { "Einstellungen", settings },
     { "Start",         begin },
     { "Abbruch",       abort},
   }
};

struct Menu SettingsMenu =
{  "+Einstellungen+",
   3,
   { { "Set Frequency", setFreq },
     { "Set Amount",    setAmount },
     { "Set Interval",  setInterval },
   }
};

> Nun möchte ich gerne in einer Funktion namens
>
1
> void RefreshScreen(struct Menueintrag Menu[])
2
>
> das Aktuelle Menü auf eineme Display ausgeben. die Ausgabe funktioniert
> auch schon.
> Ich kann also momentan über einen Aufruf der Form
>
1
> RefreshScreen(Hauptmenu);
2
>
> Das jeweilige Menü darstellen.
> Nun ist dies jedoch sehr unpraktisch.

Ist es.

> Ich würde gerne diese beiden Menüs (Structs) in ein Array oder in eine
> Struct packen, um dann per Index das jeweilige Menü benennen zu können.

Brauchst du im Grunde nicht.
Es reicht, wenn du eine Pointer Variable hast, die auf das aktuelle Menü 
zeigt

struct Menu* actMenu;


> Also etwa in der Form:
>
1
> RefreshScreen(Menus(1));
2
>
> oder
>
1
> RefreshScreen(Menus(aktuellesMenu));
2
>

oder einfach

  actMenu = &HauptMenu;

  RefreshScreen( actMenu );

von Tueftler (Gast)


Lesenswert?

Hallo Karl,
Vielen Dank für deine ausführliche Antwort, diese hat mich bereits sehr 
viel weiter gebracht. Ich habe das jetzt mal so wie von dir 
vorgeschlagen implementiert. Nun habe ich allerdings wiederum ein 
Problem.
Ich versuche mal zu erklären was ich überhaupt gemacht hab, um zu 
prüfen, dass ich das auch verstanden habe:
Als erstes definiere ich eine Struktur mit Namen Menueeintrag, deren 
Einträge jeweils aus einem Text und einer Funktion bestehen.
1
struct Menueeintrag
2
{
3
  const char*   Text;
4
  VoidFnct Function;
5
};

Nun definiere ich eine neue Structure, die sich zusammensetzt aus:
-einem Pointer auf ein Char-?Eintrag? mit Namen Title
-einer Variable vom typ uint8_t mit Namen NrEntries
-sowie einer Structur vom obigen "Typ" Menueeintrag mit Namen Entries
1
struct Menue{      
2
  const char*    Title;
3
  uint8_t    NrEntries;
4
  struct Menueeintrag   Entries[];      //
5
};

Nun erzeuge ich eine Struktur vom Typ Menue und trage die Werte ein.
1
struct Menue Einstellungen =
2
{"++Einstellungen++",
3
  3,
4
{
5
  {"   Messintervall", setInterval},
6
  {"     Frequenz", setFreq},
7
  {"     Spielzahl", setAmount}
8
}
9
};

Nun definiere ich noch einen Pointer der auf den Typ Menue zeigt und 
actMenue heisst:
1
struct Menue* actMenue;

Die nun folgende Funktion bekommt den oben definierten Pointer als 
Argument übergeben und soll den Menutitel sowie die Texte der einzelnen 
Einträge ausgeben: Hier fängt das Problem an:
Ich muss zuerst den Inhalt des übergebenen Pointers in eine lokale 
Struktur laden, wiederum vom Typ Menue.
Anschliessend kann ich auf den Title der lokalen Struktur zugreifen und 
diesen ausgeben. Auch kann ich auf NrEntries zugreifen und diese korrekt 
auslesen. Sobald ich allerdings auf die Texte der intregrierten Struct 
Menueeintrag zugreifen will, kommen nur noch wirre Zeichen. (in der 
Schleife)
1
void RefreshScreen(struct Menue *Menuelokal)
2
{
3
  struct Menue Menuelocal =*Menuelokal;
4
   
5
  WriteString(2,0,Menuelocal.Title);  
6
7
  for (uint8_t z=1; z<=Menuelocal.NrEntries; z++)
8
  {
9
  WriteString(2,z+2,Menuelocal.Entries[z-1].Text);
10
  }
11
}

Ander hingegen wenn ich nicht den Pointerinhalt ausgebe sondern direkt 
den Inhalt eines Menus:
1
WriteString(2,5,HauptMenue.Entries[1].Text);
Diese Ausgabe funktioniert fehlerfrei.

Meine Frage also:
1. Wie greife ich auf die Elemente der Struct in der Struct zu? :)
2. Welches Buch erklärt diese Sachen ausführlich und verständlich, evtl. 
mit Beispielen?

Vielen Dank für deine Mühen und natürlich auch allen Anderen die mir 
weiterhelfen können oder es versuchen.

von abcxyz (Gast)


Lesenswert?

In RefreshScreen brauchst du keine lokale Struktur anzulegen.
Nutze einfach den Pointer um auf die Elemente zuzugreifen:

void RefreshScreen(struct Menue *actMenue)
{


  WriteString(2,0,actMenue->Title);

...

      WriteString(2,z+2,actMenue->Entries[z-1].Text);
....

von Tueftler (Gast)


Lesenswert?

Hallo nochmal,

Das geht nun sehr gut, Vielen Dank für diese Hilfe.

Habe nun das Problem, dass ich einer neuen Funktion zwei Argumente 
(Pointer) übergebe.

void ExecCommand(struct Menue *actMenue, uint8_t *Selection)
{
actMenue->Entries[*Selection].Function();
}

Allerdings klappt dies nicht, Ich lade extern in den Pointerbereich eine 
Zahl:
1
(*Selection)=1;
Dann rufe ich die Funktion auf und übergebe ihr die Adresse an der die 
Zahl steht, die ich vorher hineingeschrieben habe.
1
ExecCommand(actMenue,&Selection);

Nun wird jedoch immer das gleiche Unterprogramm aufgerufen, egal welche 
Zahl ich vorher in die Speicheradresse auf die der Pointer zeigt 
geschrieben habe.

Also
1
(*Selection)=1;
2
ExecCommand(actMenue,&Selection);
3
(*Selection)=2;
4
ExecCommand(actMenue,&Selection);
5
(*Selection)=3;
6
ExecCommand(actMenue,&Selection);
Ruft immer das gleiche Untermenü auf.

Was mache ich da noch falsch?
Danke für eure Bemühungen.

von ozo (Gast)


Lesenswert?

Meiner Ansicht nach hast du da einen "Pointer" zuviel drin.
Statt
1
(*Selection)=2;
2
ExecCommand(actMenue,&Selection);
sollte es gefühlt eher so aussehen:
1
u8_t Selection;
2
Selection=2;
3
ExecCommand(actMenue,&Selection);
Aber eigentlich kannst du doch auf die Übergabe von Selection als 
Pointer verzichten und direkt Selection übergeben? Oder soll deine 
Funktion schreibend auf Selection zugreifen können?

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.