Forum: Mikrocontroller und Digitale Elektronik Verständnis Struct mit Zeigern


von Philipp (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich bin zur Zeit dabei, ein c-Programm zur Erstellung einer vernünftigen 
Menüstruktur auszuarbeiten. Dieses möchte ich dann für zukünftige 
Projekte verwenden.

Dazu habe möchte ich die Micro-Menu lib von Dean Camera verwenden 
(https://github.com/abcminiuser/micromenu-v2). Wahrscheinlich gibt es um 
einiges Bessere, aber ich habe bewusst diese gewählt, da der Code noch 
einigermaßen übersichtlich ist.

Habe die Dateien zum Projekt in den Anhang gepackt.

Leider scheitert es schon beim Verständnis der Structs für die einzelnen 
Menüpunkte.
Es geht mir konkret um dieses Stück Code (zu finden in MicroMenu.h):

1
  /** Type define for a menu item. Menu items should be initialized via the helper
2
   *  macro \ref MENU_ITEM(), not created from this type directly in user-code.
3
   */
4
  typedef const struct Menu_Item {
5
    const struct Menu_Item *Next; /**< Pointer to the next menu item of this menu item */
6
    const struct Menu_Item *Previous; /**< Pointer to the previous menu item of this menu item */
7
    const struct Menu_Item *Parent; /**< Pointer to the parent menu item of this menu item */
8
    const struct Menu_Item *Child; /**< Pointer to the child menu item of this menu item */
9
    void (*SelectCallback)(void); /**< Pointer to the optional menu-specific select callback of this menu item */
10
    void (*EnterCallback)(void); /**< Pointer to the optional menu-specific enter callback of this menu item */
11
    const char Text[]; /**< Menu item text to pass to the menu display callback function */
12
  } Menu_Item_t;
13
14
  /** Creates a new menu item entry with the specified links and callbacks.
15
   *
16
   *  \param[in] Name      Name of the menu entry, must be unique.
17
   *  \param[in] Next      Name of the next linked menu item, or \ref NULL_MENU if no menu link.
18
   *  \param[in] Previous  Name of the previous linked menu item, or \ref NULL_MENU if no menu link.
19
   *  \param[in] Parent    Name of the parent linked menu item, or \ref NULL_MENU if no menu link.
20
   *  \param[in] Child     Name of the child linked menu item, or \ref NULL_MENU if no menu link.
21
   *  \param[in] SelectFunc  Function callback to execute when the menu item is selected, or \c NULL for no callback.
22
   *  \param[in] EnterFunc   Function callback to execute when the menu item is entered, or \c NULL for no callback.
23
   */
24
  #define MENU_ITEM(Name, Next, Previous, Parent, Child, SelectFunc, EnterFunc, Text) \
25
    extern Menu_Item_t MENU_ITEM_STORAGE Next;     \
26
    extern Menu_Item_t MENU_ITEM_STORAGE Previous; \
27
    extern Menu_Item_t MENU_ITEM_STORAGE Parent;   \
28
    extern Menu_Item_t MENU_ITEM_STORAGE Child;  \
29
    Menu_Item_t MENU_ITEM_STORAGE Name = {&Next, &Previous, &Parent, &Child, SelectFunc, EnterFunc, Text}

Die Definition des Structs "Menu_Item" ist mir bis auf den letzten 
Eintrag noch einigermaßen klar.
Hier deshalb nun meine Fragen:

- Warum wird der Eintrag "Text" ( const char Text[];) nicht wie die 
Einträge davor als Zeiger mit *Text definiert?

- Wozu wird in der letzten Zeile dem Array nochmal ein Eintrag "Name" 
hinzugefügt, der nochmals Zeiger auf die einzelnen Elemente (Next, 
Previous, ...) enthält?

- Im Beispiel (Example.c) wird bei der Definition der Menüitems dann 
nochmals ein Name zugewiesen (erstes Argument Menu_1)?
1
MENU_ITEM(Menu_1, Menu_2, Menu_3, NULL_MENU, Menu_1_1 , Level1Item1_Select, Level1Item1_Enter, "1");
 Aber der Eintrag Name wird doch schon automatisch erstellt (s. 
vorheriger Absatz), oder?


Habe auch schon Gegooglet, aber zu solch "komplexen" Array Strukturen 
(Verschachtelt mit Zeigern auf sich selbst) habe ich nichts 
verständliches gefunden.

Deswegen würde ich mich freuen, wenn jemand mir kurz diesen Code 
erklären könnte.

Vielen Dank schonmal für eure Mühe,

Philipp

von mh (Gast)


Lesenswert?

Der "Name" ist der Variablenname für die mit dem Makro MENU_ITEM 
angelegte "Menüeintragsstruktur".

von Georg G. (df2au)


Lesenswert?

Philipp schrieb:
> Warum wird der Eintrag "Text" ( const char Text[];) nicht wie die
> Einträge davor als Zeiger mit *Text definiert?

Der Name muss ja irgendwo gespeichert werden. Würde dort nur ein Pointer 
auf den Namen stehen, müsstest du dennoch irgendwo die "richtigen" 
Zeichen speichern.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das ist ein leeres Array. Manche C-Compiler erlauben so etwas in 
Strukturdefinitionen; das bedeutet, daß der Speicher, den dieses Array 
repräsentiert, hinter dem Speicher liegt, den die Struktur belegt.

Ich schreibe bewusst "manche C-Compiler", weil das eine nicht vom 
Standard abgedeckte Spracherweiterung ist.

So ein leeres Array ist genau einmal und nur am Ende einer Struktur 
möglich.

Ein einfaches Beispiel zur Verdeutlichung:
1
typedef struct
2
{
3
  uint16_t a;
4
  uint16_t b;
5
  char text[];
6
} mytype;
7
8
9
char daten[] = { 12, 34, 56, 78, 'h', 'a', 'l', 'o', 0 };
10
11
mytype* p = (mytype *) daten;
12
13
printf("a = %d, b = %d, Text '%s'\n", p->a, p->b, p->text);
14
15
printf("sizeof mytype = %d, sizeof daten = %d\n", sizeof mytype, sizeof daten);

Was geschieht hier?

Die Struktur belegt nur den Speicher, der für die Speicherung der 
Elemente a und b erforderlich ist. Bei einer direkten Instanziierung der 
Struktur z.b. mit
1
mytype x;
2
char bla[] = "wasanderes";
3
4
x.a = 1;
5
x.b = 2;

verweist das Strukturelement text auf nicht allokierten Speicher, 
nämlich auf den Speicher hinter der Struktur. Das kann der vom Array 
bla belegte Speicher sein, muss aber nicht. Ein Zugriff darauf ist 
also ... gewagt.

Im obigen Beispiel wird mit daten ein Speicherbereich mit Daten 
gefüllt, der größer ist als diese Struktur. Mit dem Typecast wird ein 
Pointer auf den Strukturtyp erzeugt -- und da ist der Zugriff auf das 
Strukturelement text möglich, denn in daten stehen auch dort 
sinnvolle Werte.


Wozu ist das ganze gut? So etwas kann verwendet werden, um 
Dateistrukturen oder Kommunikationsstrukturen zu definieren, in denen 
ein fester Header einer variablen Anzahl Nutzbytes vorangeht.

Der Gebrauch aber ist umstritten, da das stark von der jeweiligen 
Compilerimplementierung und Maschine abhängt (Gepackte Strukturen? 
Alignment?) und, wie einleitend schon erwähnt, nicht vom Standard 
abgedeckt ist.


Mit anderen Worten: Begegnet man in fremdem Sourcecode einem leeren 
Array, ist das mit äußerster Vorsicht zu betrachten, denn so etwas 
sollte nur verwenden, wer auch wirklich genau weiß, was er da treibt.

Aufgabe an den geneigten Leser :

Was geben die beiden printf -Aufrufe im Codebeispiel oben aus?

: Bearbeitet durch User
von Sebastian V. (sebi_s)


Lesenswert?

Rufus Τ. F. schrieb:
> Das ist ein leeres Array. Manche C-Compiler erlauben so etwas in
> Strukturdefinitionen; das bedeutet, daß der Speicher, den dieses Array
> repräsentiert, hinter dem Speicher liegt, den die Struktur belegt.
>
> Ich schreibe bewusst "manche C-Compiler", weil das eine nicht vom
> Standard abgedeckte Spracherweiterung ist.

Doch ist im Standard. Zumindest wenn man den C99 Standard dazu zählt:
https://en.wikipedia.org/wiki/Flexible_array_member

von aSma>> (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Aufgabe an den geneigten Leser :
>
> Was geben die beiden printf -Aufrufe im Codebeispiel oben aus?


Rufus Τ. F. schrieb:
> typedef struct
> {
>   uint16_t a;
>   uint16_t b;
>   char text[];
> } mytype;
>
> char daten[] = { 12, 34, 56, 78, 'h', 'a', 'l', 'o', 0 };
> printf("a = %d, b = %d, Text '%s'\n", p->a, p->b, p->text);
1
 a = 12, b=34, Text 8NHalo

> printf("sizeof mytype = %d, sizeof daten = %d\n", sizeof mytype, sizeof
> daten);
1
 sizeof mytype = 2,  sizeof daten = 2+2+1+1+1+1+1+1+1 (bytes)

von Noch einer (Gast)


Lesenswert?

> zu solch "komplexen" Array Strukturen
> (Verschachtelt mit Zeigern auf sich selbst) habe ich nichts
> verständliches gefunden.

Das war mal eine verkettet Liste, wie hier beschrieben:
https://de.wikibooks.org/wiki/C-Programmierung:_Verkettete_Listen

Damit man die Liste in beide Richtungen ablaufen kann, kam noch ein 
*Previous dazu.

Dann wurde aus dem einfachen value ein Text, die *Callback und das 
*Child.

Damit man vom Child aus den ganzen Rest findet, kam noch ein *Parent 
dazu.

Und damit man alle Zeiger konsistent füllen kann, kam dann auch noch das 
#define oben drauf.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sebastian V. schrieb:
> Doch ist im Standard. Zumindest wenn man den C99 Standard dazu zählt:
> https://en.wikipedia.org/wiki/Flexible_array_member

Danke. Das ist immerhin ein Fortschritt. Und ein Zeichen dafür, daß ich 
nach wie vor überwiegend mit einem C89-Compiler arbeite (Microsoft lernt 
halt nicht dazu).


aSma>> schrieb:
> a = 12, b=34, Text 8NHalo

Ganz sicher nicht.

aSma>> schrieb:
> sizeof mytype = 2,  sizeof daten = 2+2+1+1+1+1+1+1+1 (bytes)

Auch nicht.

Was ist uint16_t ? Wie groß ist das?

Und wie kommst Du auf die wirre Summe von "daten"?

Richtig wären 4 und 9.

von W.S. (Gast)


Lesenswert?

Philipp schrieb:
1
const struct TMenu_Item 
2
{    const struct Menu_Item *Next;
3
     const struct Menu_Item *Previous; 
4
     const struct Menu_Item *Parent; 
5
     const struct Menu_Item *Child; 
6
     void (*SelectCallback)(void); 
7
     void (*EnterCallback)(void); 
8
     const char Text[]; 
9
};

Also erstens ist das doch im Wesentlichen sehr verständlich und zweitens 
fehlt da einiges. Im Grunde ist das eine C-Formulierung für ein Objekt. 
Dieses hat natürlich Zeiger auf seinen Vorgänger und Nachfolger, da es 
ja in einer doppelt verketteten Liste organisiert ist, es hat obendrein 
einen Zeiger auf seinen Besitzer und es har einen Zeiger auf seine 
Members, also wiederum eine doppelt verkettete Liste von Menüobjekten, 
die es beherbergt. Dazu kommen natürlich Methoden, damit das Objekt 
agieren kann. Wenigstens zwei Methoden sind essentiell: eine 
OnEvent-Methode, mit der das Objekt auf Ereignisse reagieren kann und 
eine OnDraw-Methode, mit der es sich selbst darstellen kann. Dazu kommen 
natürlich innere Variablen, die den Zustand des Objektes darstellen. Da 
es aber vorgesehen ist, solche Objekte im Flash anzuordnen, braucht es 
anstelle des konstanten Textes eigentlich einen Zeiger, der in den RAM 
zeigen kann (wenn das Objekt das braucht), wo dann objektspezifische 
Variablen stehen, die nur das Objekt selber kennt. Dies ist der 
Unterschied zu Komponenten, wie sie in PC-Programmen üblich sind.

Zu ersterem:
Das Grundgerüst entspricht weitgehend dem Menü, was ich für die 
Lernbetty geschrieben habe. Es fehlt als gravierende Einschränkung die 
Methode "Draw", der universelle Zeiger in den RAM und ein Satz von Flags 
und Konstanten, aus denen der Owner (bei dir: Parent) wichtige Dinge 
erkennen kann: ob das Objekt den Fokus erhalten kann, Koordinaten wo es 
sich befindet und seine Größe.

Hier mal das Original:
1
#define RCST  const struct TMenuItem
2
struct TMenuItem
3
{ struct TRect R;                  /* die Koordinaten relativ zum Owner       */
4
  const struct TMenuItem *davor;   /* Listenverkettung: Item davor            */
5
  const struct TMenuItem *danach;  /* Listenverkettung: Item danach           */
6
  const struct TMenuItem *Owner;   /* Item, wo dieses enthalten ist           */
7
  const struct TMenuItem *Members; /* Liste der enthaltenen Items             */
8
  dword Flags;                     /* diverse Flag-Bits                       */
9
  void  *Data;                     /* Zeiger auf fallspezifische Daten        */
10
  void  (*OnKey)  (RCST* self, word *aKey);     /* wertet Tastendrücke aus    */
11
  void  (*OnEvent)(RCST* self, word *aEvent);   /* wertet Ereignisse aus      */
12
  void  (*Draw)   (RCST* self);    /* zeichnet sich und Members  */
13
};

Eine solche Menü-Element-Struktur kann Menüs ganz unterschiedlichen 
Aussehens erzeugen: vom rein textuellen Listenmenü über grafische Menüs 
mit und ohne Icons bis hin zu komplexen visuellen Komponenten wie 
Listboxen, Checkboxen, DropDown-Listen, Fortschrittsbalken, Paneelen mit 
und ohne Headline und und und.

Allerdings ist der Menü-Entwurf eher was für ein PC-Programm, wo man das 
komplette Display des µC's emuliert und die Menü-Elemente grafisch 
anordnet und editiert. Sozusagen eine Proleten-Version von Delphi.

Nochwas: die Trennung zwischen OnEvent und OnKey hatte ich eingeführt, 
weil es unterschiedliche Signalpfade geben sollte zwischen Events, die 
möglicherweise alle Objekte im System angehen können (wie z.B. 
Broadcast's von Uhrzeit usw.) und Events, die navigationsspezifisch 
sind, also zu allererst vom fokussierten Element zur Kenntnis genommen 
werden müssen. Will man das vereinheitlichen, dann müssen Konventionen 
her, die die Struktur von Events betreffen, also so ähnlich wie bei 
Windows (incl. wParam, lParam usw.)

W.S.

von aSma>> (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Was ist uint16_t ? Wie groß ist das?
>
> Und wie kommst Du auf die wirre Summe von "daten"?
>
> Richtig wären 4 und 9.

uint16_t sind 16 (in Worten sechszehn) bits! 8bits ergeben ein byte. 
Merkst du wer hier Wirr ist?! Ein char ist immer ein byte Kollega.

Amen.

von aSma>> (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Rufus Τ. Firefly

Ja, hast recht. Brauche ne pause hier.

von Sheeva P. (sheevaplug)


Lesenswert?

aSma>> schrieb:
> Rufus Τ. F. schrieb:
>> Was ist uint16_t ? Wie groß ist das?
>>
>> Und wie kommst Du auf die wirre Summe von "daten"?
>>
>> Richtig wären 4 und 9.
>
> uint16_t sind 16 (in Worten sechszehn) bits! 8bits ergeben ein byte.
> Merkst du wer hier Wirr ist?! Ein char ist immer ein byte Kollega.

Der Compiler macht aus
1
char daten[] = { 12, 34, 56, 78, 'h', 'a', 'l', 'o', 0 };

aber nur ein Array von 9 Bytes, der Typecast
1
mytype* p = (mytype *) daten;

ändert daran nichts. Das char-Array wird einfach nur neu als Zeiger auf 
ein "mytype" neu interpretiert, an den Daten passiert dabei aber nichts; 
deswegen wird daten[0] als High Byte von mytype.a und daten[1] als Low 
Byte von mytype.a interpretiert. p->a ist demzufolge dann "(34 << 8) + 
12" oder 8716. Dasselbe gilt für daten[3,4] und mytype.b: "(78 << 8) + 
56" -> 20024, aber "sizeof(daten)" bleibt dann folgerichtig "9".

"sizeof(mytype)" ergibt übrigens tatsächlich "4": zwei uint16_t sind 2 * 
2 Bytes; der Compiler scheint das Array auf den ersten Blick zu 
ignorieren. Wenn aber Rufus' Aussage, "das Strukturelement text 
[verweise] auf nicht allokierten Speicher, nämlich auf den Speicher 
hinter der Struktur" stimmt, dann gehört der betreffende Speicher 
nicht zu der Struktur und "text" ist nur ein Compilersymbol, das 
diesen Speicher adressiert. Daher ist "4" die richtige Antwort.

von Falk B. (falk)


Lesenswert?

@ aSma>> (Gast)

>uint16_t sind 16 (in Worten sechszehn) bits! 8bits ergeben ein byte.
>Merkst du wer hier Wirr ist?! Ein char ist immer ein byte Kollega.

Nö, das ist zwar bei den MEISTEN Compilern so, aber nicht bei allen!

Beim C2000 von TI ist ein Char 16 Bits und die nennen das sogar Byte!!!

von Tim (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> C89-Compiler arbeite (Microsoft lernt halt nicht dazu)

Was gibts da zu lernen?

Microsoft hat die Entscheidung getroffen und inzwischen über die Jahre 
dutzendfach öffentlich gemacht, daß sie C nur soweit unterstützen, wie 
es für ihren Legacy-Code notwendig ist (also C89), neuere C-Features nur 
"nebenbei" mit reinkommen, wie sie für C++ ohnehin implementiert werden.

C unter Windows ist mausetot. Das ist zwar schade, aber wenn man ehrlich 
ist, kann man diese Entscheidung sehr gut nachvollziehen.

von Tim (Gast)


Lesenswert?

Falk B. schrieb:
> Beim C2000 von TI ist ein Char 16 Bits und die nennen das sogar Byte!!!

Das ist ja auch ein Byte. Das Ding, das du meinst, nennt sich Oktett.

von Falk B. (falk)


Lesenswert?

@ Tim (Gast)

>> Beim C2000 von TI ist ein Char 16 Bits und die nennen das sogar Byte!!!

>Das ist ja auch ein Byte. Das Ding, das du meinst, nennt sich Oktett.

Naja. Wenn gleich ich jetzt keine Quelle nennen kann, so war und bin ich 
der Meinung, daß ein BYTE schon seit Jahrzehnten als Ansammlung von 8 
Bits allgemeingültig definiert ist! Diesen akademischen Eiertanz um die 
Größendefinition von Variablentypen, wie sie in C betrieben werden, 
macht man bei der Definition von BYTE NICHT!!!

von Einer K. (Gast)


Lesenswert?

Falk B. schrieb:
> und bin ich
> der Meinung, daß ein BYTE schon seit Jahrzehnten als Ansammlung von 8
> Bits allgemeingültig definiert ist!

Ein kleiner Irrtum.

Wikipedia sagt dazu:
"Das Byte [baɪt] ist eine Maßeinheit der Digitaltechnik und der 
Informatik, das meist für eine Folge von 8 Bit steht. Historisch gesehen 
war ein Byte die Anzahl der Bits zur Kodierung eines einzelnen 
Text-Schriftzeichens im jeweiligen Computersystem und ist daher das 
kleinste adressierbare Element in vielen Rechnerarchitekturen."

von Falk B. (falk)


Lesenswert?

@U. F. (ufuf)

>> der Meinung, daß ein BYTE schon seit Jahrzehnten als Ansammlung von 8
>> Bits allgemeingültig definiert ist!

>Ein kleiner Irrtum.

Nö.

>Wikipedia sagt dazu:

Ist zwar nett, aber Wikipedia ist nicht das Maß der DInge.

>"Das Byte [baɪt] ist eine Maßeinheit der Digitaltechnik und der
>Informatik, das meist für eine Folge von 8 Bit steht.

Eben!

> Historisch gesehen
>war ein Byte die Anzahl der Bits zur Kodierung eines einzelnen

HISTORISCH!! EBEN! Das WAR mal so, daß es verschieden lange Bytes gab! 
Das ist aber JAHRZEHNTE her! Heute ist ein Byte so ziemlich auf jedem 
System 8 Bit breit. Egal ob PC, MAC, Spielkonsole, Handy, Speicherkarte! 
Bestenfalls die Definition des Begriffs WORD ist architekturabhängig!

Darum halte ich die die Nomenklatur von TI für mißlungen! Ich hab kein 
Problem mit einer CPU, die nur 16 Bit Worte adressieren kann oder daß 
ein char in C 16 Bit breit ist. Aber das ist KEIN Byte!!!

von Axel S. (a-za-z0-9)


Lesenswert?

Falk B. schrieb:
> @U. F. (ufuf)
>
>>> der Meinung, daß ein BYTE schon seit Jahrzehnten als Ansammlung von 8
>>> Bits allgemeingültig definiert ist!
>
>>Ein kleiner Irrtum.
>
> Nö.

Doch :)

> ... Wikipedia ist nicht das Maß der DInge.

Aber da es hier um C geht ... gilt der C Standard?

Denn in C-Nomenklatur ist "Byte" die Bezeichnung für die kleinste 
adressierbare Dateneinheit [1]. Und das können dann schon mal 16 oder 
mehr Bits sein.

Ja, das ist dämlich. Insbesondere weil man sich überall außerhalb der 
Welt der C-Standardisierer darauf geeinigt hat, ein Byte wäre das 
gleiche wie ein Oktett.

[1] naja, so fast. "addressable unit of data storage large enough to 
hold any member of the basic character set of the execution 
environment". An anderer Stelle wird dann noch gefordert, daß mindestens 
256 verschiedene Zeichen darstellbar sein müssen, was die Größe eines 
Byte auf mindestens 8 Bit festlegt.

von Falk B. (falk)


Lesenswert?

@ Axel Schwenke (a-za-z0-9)

>> Nö.

>Doch :)

Ohhhh!! ;-)

>> ... Wikipedia ist nicht das Maß der DInge.

>Aber da es hier um C geht ... gilt der C Standard?

Jain. Das C diverse akademische Spielchen mit der Definition von 
Datentypen macht ist bekannt und muss man so ertragen. Das tu ich auch. 
Siehe oben!

Beitrag "Re: Verständnis Struct mit Zeigern"

>Denn in C-Nomenklatur ist "Byte" die Bezeichnung für die kleinste
>adressierbare Dateneinheit [1]. Und das können dann schon mal 16 oder
>mehr Bits sein.

Ja.

>Ja, das ist dämlich.

EBEN!!

> Insbesondere weil man sich überall außerhalb der
>Welt der C-Standardisierer darauf geeinigt hat, ein Byte wäre das
>gleiche wie ein Oktett.

EBEN^2!!!

>[1] naja, so fast. "addressable unit of data storage large enough to
>hold any member of the basic character set of the execution
>environment". An anderer Stelle wird dann noch gefordert, daß mindestens
>256 verschiedene Zeichen darstellbar sein müssen, was die Größe eines
>Byte auf mindestens 8 Bit festlegt.

Akademischer Firlefanz!

von Philipp (Gast)


Lesenswert?

Erstmal vielen Dank für eure zahlreichen Antworten!

Der Hinweis, dass es sich bei meiner "Problematik" um sog. verkettete 
Listen handelt, hat mir sehr weitergeholfen, unter dem Stichwort findet 
sich einiges im Internet.

Habe ich also richtig verstanden, dass ich durch das Angeben des 
Nächsten (Vorherigen, Eltern, Kind) Menüpunktes z.B. mit dem Makro
1
MENU_ITEM(Menu_1, Menu_2, Menu_3, NULL_MENU, Menu_1_1 , Level1Item1_Select, Level1Item1_Enter, "1");
 automatisch den Speicher für das jeweilige Element reserviere. Später, 
wenn ich dieses Element, z.B. Menu_2, definiere, fülle ich dann die 
vorher reservierte "Speicherzelle" mit Daten?

Das nächste Problem ist, dass ich beim Compilieren (AVR Studio 6.2, GCC 
4.8.1) folgende Fehlermeldung bekomme:

" Error  3  initializer-string for array of chars is too long 
[-fpermissive]"

Damit meint er die Zeile
1
   Menu_Item_t MENU_ITEM_STORAGE Name = {&Next, &Previous, &Parent, &Child, SelectFunc, EnterFunc, Text};

Sieht jemand hier einen Fehler?

von Rolf M. (rmagnus)


Lesenswert?

Falk B. schrieb:
>> Historisch gesehen
>>war ein Byte die Anzahl der Bits zur Kodierung eines einzelnen
>
> HISTORISCH!! EBEN! Das WAR mal so, daß es verschieden lange Bytes gab!
> Das ist aber JAHRZEHNTE her! Heute ist ein Byte so ziemlich auf jedem
> System 8 Bit breit.

Ja, so ziemlich, aber eben nicht komplett. Und BRÜLL NICHT SO! Das lässt 
dein Posting ziemlich ALBERN WIRKEN.

>>Wikipedia sagt dazu:
>
> Ist zwar nett, aber Wikipedia ist nicht das Maß der DInge.

Aber du bist das Maß der Dinge, oder wie?

> Darum halte ich die die Nomenklatur von TI für mißlungen! Ich hab kein
> Problem mit einer CPU, die nur 16 Bit Worte adressieren kann oder daß
> ein char in C 16 Bit breit ist. Aber das ist KEIN Byte!!!

Nur weil du die ursprüngliche Bedeutung für ungültig erklärt hast? Ich 
verstehe auch nicht, was da nun schlimm dran sein soll, das als Byte zu 
bezeichnen.

Falk B. schrieb:
> Bestenfalls die Definition des Begriffs WORD ist architekturabhängig!

Das ist sie ganz bestimmt. Schon alleine bei den beiden meist 
verbreiteten Architekturen ist ein Word unterschiedlich groß. Das kommt 
allerdings auch eher aus der Historie. Mit "Word" wird für gewöhnlich 
die native Größe der CPU bezeichnet. Bei x86 waren das ursprünglich mal 
16 Bit, deshalb ist ein Word dort auch heute noch 16 Bit breit, obwohl 
das schon lange nicht mehr die native Breite ist.

von Markus F. (mfro)


Lesenswert?

Philipp schrieb:
> Das nächste Problem ist, dass ich beim Compilieren (AVR Studio 6.2, GCC
> 4.8.1) folgende Fehlermeldung bekomme:
>
> " Error  3  initializer-string for array of chars is too long
> [-fpermissive]"
>
> Damit meint er die Zeile   Menu_Item_t MENU_ITEM_STORAGE Name = {&Next,
> &Previous, &Parent, &Child, SelectFunc, EnterFunc, Text};
> Sieht jemand hier einen Fehler?

Da ist keiner. Der Fehler ist woanders.

Die Fehlermeldung bekommst Du, wenn Du eben nicht mit gcc compilierst, 
sondern mit g++ (dem C++-Compiler).

C99 kann variable length arrays, C++ kann's nicht (und braucht's auch 
nicht zu können, da gibt's elegantere und weniger fehlerträchtige 
Möglichkeiten, solche Probleme zu lösen).

von Philipp (Gast)


Lesenswert?

Das war es, jetzt kann ich das Programm wenigstens ohne Fehlermeldung 
kompilieren.


Zwischenzeitlich ist aber nochmal eine Frage zum Programmablauf 
aufgetaucht. Hier der relevante Codeausschnitt (vollständiger Code im 
Anhang des Anfangsthreads):

1
Menu_Item_t PROGMEM NULL_MENU = {0};
2
3
/** \internal
4
 *  Pointer to the generic menu text display function
5
 *  callback, to display the configured text of a menu item
6
 *  if no menu-specific display function has been set
7
 *  in the select menu item.
8
 */
9
static void (*MenuWriteFunc)(const char* Text) = NULL;
10
11
/** \internal
12
 *  Pointer to the currently selected menu item.
13
 */
14
static Menu_Item_t* CurrentMenuItem = &NULL_MENU;
15
16
17
Menu_Item_t* Menu_GetCurrentMenu(void)
18
{
19
  return CurrentMenuItem;
20
}
21
22
void Menu_Navigate(Menu_Item_t* const NewMenu)
23
{
24
  if ((NewMenu == &NULL_MENU) || (NewMenu == NULL))
25
    return;
26
27
  CurrentMenuItem = NewMenu;
28
29
  if (MenuWriteFunc)
30
    MenuWriteFunc(CurrentMenuItem->Text);
31
32
  void (*SelectCallback)(void) = MENU_ITEM_READ_POINTER(&CurrentMenuItem->SelectCallback);
33
34
  if (SelectCallback)
35
    SelectCallback();
36
}

Ausgangssituation:
Alle Strukturen für die einzelnen Menüpunkte wurden im Flash angelegt.
Der Zeiger CurrentMenuItem zeigt noch auf das leere Struct NULL_MENU.

Meine Frage dazu: Wie kann jetzt eine Navigation auf den nächsten bzw. 
ersten Menüpunkt erfolgen? Die Funktion zum Navigieren nimmt ja z.B. 
einen Zeiger auf den nächsten Menüpunkt entgegen. Dieser Zeiger wird mit 
dem Makro
1
 #define MENU_NEXT           MENU_ITEM_READ_POINTER(&Menu_GetCurrentMenu()->Next)
 aus dem Struct des aktuellen Menüpunktes geholt.

Im Struct CurrentMenuItem gibt es aber doch am Anfang keinen Zeiger, 
denn CurrentMenuItem zeigt ja am Anfang auf NULL_MENU?

von Falk B. (falk)


Lesenswert?

@  Rolf Magnus (rmagnus)

>> Darum halte ich die die Nomenklatur von TI für mißlungen! Ich hab kein
>> Problem mit einer CPU, die nur 16 Bit Worte adressieren kann oder daß
>> ein char in C 16 Bit breit ist. Aber das ist KEIN Byte!!!

>Nur weil du die ursprüngliche Bedeutung für ungültig erklärt hast?

Nö, aber die im allgmeinen verwendete Deutung von Byte ist nun mal 8 
Bit.

>Ich
>verstehe auch nicht, was da nun schlimm dran sein soll, das als Byte zu
>bezeichnen.

Weil es Unsinn ist. Der Eiertanz geht auch in der Doku weiter, wo immer 
krampfhaft auf 16 Bit Worte hingewiesen wird.
Denn man will ja beim Marketing nicht nur die Häfte Speicher im 
Controller präsentieren.

>> Bestenfalls die Definition des Begriffs WORD ist architekturabhängig!

>Das ist sie ganz bestimmt.

Eben. Also sollte man bei Ti von Words sprechen und nicht von praktisch 
eindeutig als 8 Bit definierten Bytes.
Ich wiederhole mich.

>allerdings auch eher aus der Historie. Mit "Word" wird für gewöhnlich
>die native Größe der CPU bezeichnet. Bei x86 waren das ursprünglich mal
>16 Bit, deshalb ist ein Word dort auch heute noch 16 Bit breit, obwohl
>das schon lange nicht mehr die native Breite ist.

Richtig. Und die natürlich Wortbreite dieser CPUs ist 16 Bit. Gar kein 
Problem.
Aber 16 Bit als Byte zu bezeichnen ist eins, denn damit hat man TI-Bytes 
und RestderWelt-Bytes.
Diesen Kuddelmuddel haben wir eigentlich verdammt lange hinter uns.

Ein Byte hat 8 Bit und das überall. Punkt!
Die Breite eines Worts ist architekturabhängig.

von Einer K. (Gast)


Lesenswert?

Falk B. schrieb:
> die im allgmeinen verwendete Deutung von Byte ist nun mal 8
> Bit.
Im allgemeinen stimmt das ja auch....

Aber im Speziellen kann das schon mal anders sein.
Von mir aus, darfst du das ausblenden.
Aber diesen Unsinn in einem Forum zu verbreiten ist, gelinde gesagt, 
daneben.


Also:
Lebe in deiner 8Bit = 1Byte Märchenwelt, ok.
Aber im Forum gibts Kontra.

von Philipp (Gast)


Lesenswert?

Meine Zwischenfrage hat sich gerade auch erledigt.

Der CurrentMenuItem Zeiger wird am Anfang des Hauptprogramms mit
1
 Menu_Navigate(&Menu_1);
auf den gewünschten ersten Menüpunkt gesetzt.

von W.S. (Gast)


Lesenswert?

Philipp schrieb:
> Meine Frage dazu: Wie kann jetzt eine Navigation auf den nächsten bzw.
> ersten Menüpunkt erfolgen?

Du hast ein Problem: nämlich daß du dich bei Github und dort bei bei dem 
Autor, der dieses Menüsystem verzapft hat, selbst belesen mußt. Hier 
kann dir keiner deine Fragen zu dieser speziellen Implementierung 
beantworten - alle Leser müßten sich ja zuvor bei Github belesen, und so 
weit geht das Interesse nicht wirklich.

Wenn du die Struktur benutzt hättest, die ich in der Lernbetty 
verwendete, dann könnte ich dir weiterhelfen. Dort geht es im Prinzip 
so, daß jeder Screen d.h. jedes Menü zunächst mal ein TMenuItem ist - 
und alles was man auf dem Display dann sieht, sind seine Members. Jedes 
derartige TMenuItem nimmt quasi von oben (das "oberste" aus 
DispatchEvent) einen Event entgehen. Ist dieser ein Broadcast-Event, 
dann wird dieser Event der Reihe nach an alle Members gegeben (deswegen 
die verkettete Liste). Ist er hingegen kein Broadcast-Event, dann wird 
er nur an den gerade fokussierten Member gegeben. Ist er danach 
abgearbeitet, dan ist es gut, wenn nicht, dann muß sich das TMenuItem 
selber drum kümmern, z.B. zum Navigieren herausfinden, wo je nach Event 
(evUp, evDown, evLeft, evRight oder so) der nächstbeste Member liegt, um 
selbigem den Fokus zu geben und ihn sich dann selbst zeichnen zu lassen.

W.S.

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.