Forum: Mikrocontroller und Digitale Elektronik Problem bei Erweiterung des ARRAY_SIZE(x) Hilfsmakros


von Patrick Z. (zorpat)


Lesenswert?

Hallo Community,
leider bin ich kein "C" Profi und stelle euch nun diese Frage, ich komm 
einfach nicht drauf wie ich das Problem lösen könnte..

Kurz eine Erklärung:

Ich bräuchte ein "SUB_ARRAY_SIZE" makro welches mir die Größe eines 
struct innerhalb eines anderen structs zurückgeben würde.

Beispiel:
1
#define ARRAY_SIZE(x)  (sizeof(x)/sizeof(*x))
2
3
/* ___ Menu defines ___ */
4
typedef void (*MenuFnct)(void);
5
6
struct DisplayElements // Elements to display on TFT screen
7
{
8
  uint8_t posx;         // "x" element position (the left upper corner)
9
  uint8_t posy;         // "y" element position (the left upper corner)
10
  uint8_t sizex;        // "x" element size
11
  uint8_t sizey;        // "y" element size
12
  uint8_t type;         // 1=button, 2=text
13
  char    Text[10];     // display text for element (just possible on some grafic_type elements!)
14
  
15
};
16
struct MenuEntry
17
{
18
  char             Text[20];   // Menu caption
19
  DisplayElements  *elements;  // Elements to draw on TFT
20
  MenuFnct         Function;   // function to call on activate/click/open
21
};
22
23
/* ___ MENU STRUCTURE ___ */
24
const struct DisplayElements base_setting_elements[] =
25
{
26
// posx,posy,sizex,sizey,type,Text
27
 {  100, 100,   20,   20,   1, "+"  }, // "+"-Button
28
 {  200, 200,   20,   20,   1, "-"  }  // "-"-Button
29
};
30
const struct DisplayElements time_setting_elements[] =
31
{
32
// posx,posy,sizex,sizey,type,Text
33
 {  300, 300,   20,   20,   2, "Option"  } // Text field
34
};
35
const struct MenuEntry MainMenu[] =
36
{
37
  { "Base",     (DisplayElements*)base_setting_elements ,     Base       },
38
  { "Uhrzeit",  (DisplayElements*)time_setting_elements ,     Time       },
39
};
40
const struct MenuEntry BaseMenu[] =
41
{
42
  { "Option1", NULL , NULL },
43
  { "Option2", NULL , NULL },
44
  { "Exit",    NULL , NULL }
45
};
46
const struct MenuEntry TimeMenu[] =
47
{
48
  { "Stunden", NULL , NULL },
49
  { "Minuten", NULL , NULL },
50
  { "Exit",    NULL , NULL }
51
};

Der Aufruf von "ARRAY_SIZE( BaseMenu )" funktioniert wunderbar (sollte 
ja auch so sein)

Aber ich bräuchte eben vom struct "MenuEntry" die Größe der "elements" 
da ich in einer loop Schleife alle "DisplayElements" abarbeiten möchte.

Im Prinzip sowas:
1
size = sizeof(Menu[1].elements)/sizeof(DisplayElements);

Wie Programmiert man das? Werde noch wahnsinnig.. :D

von Programmierer (Gast)


Lesenswert?

De Zordo P. schrieb:
> Wie Programmiert man das? Werde noch wahnsinnig.. :D
Das geht nicht. "elements" ist nur ein Pointer auf eine Speicherstelle, 
da ist keinerlei Information enthalten, wie viele Elemente dort 
tatsächlich zu finden sind. Du musst in deinem "struct MenuEntry" eine 
extra Variable vorsehen, in die du die Größe des Arrays packst.
Das ARRAY_SIZE Makro funktioniert nur mit Arrays, deren Größe dem 
Compiler bekannt ist, und deren Typ nicht zB "DisplayElements*" sondern 
"DisplayElements [3]" ist...

von Georg G. (df2au)


Lesenswert?

Eine gängige Lösung ist, die Liste mit einem NULL Pointer zu schließen.

von Patrick Z. (zorpat)


Lesenswert?

Georg G. schrieb:
> Eine gängige Lösung ist, die Liste mit einem NULL Pointer zu schließen.

..und dann liest man immer "DisplayElements" große Häppchen ein bis man 
zum NULL Wert kommt oder?

Also konkret ein struct vom Typen "DisplayElements" zur Laufzeit füllen, 
dann um "DisplayElements" weiterspringen bis NULL?

von Programmierer (Gast)


Lesenswert?

Georg G. schrieb:
> Eine gängige Lösung ist, die Liste mit einem NULL Pointer zu
> schließen.
Das geht hier aber nicht, weil das "elements" Array Objekte vom Typ 
DisplayElements enthält, welches kein Pointer-Typ ist.

De Zordo P. schrieb:
> ..und dann liest man immer "DisplayElements" große Häppchen ein bis man
> zum NULL Wert kommt oder?
Tja, leider gibt es keinen NULL-Wert für DisplayElements. Du könntest 
ein extra "DisplayElements" Objekt an das Ende des Arrays packen, dessen 
Felder so gesetzt sind dass es als "Ende-Marker" erkannt wird; zB "type" 
auf 0.

von Patrick Z. (zorpat)


Lesenswert?

Programmierer schrieb:
> Georg G. schrieb:
>> Eine gängige Lösung ist, die Liste mit einem NULL Pointer zu
>> schließen.
> Das geht hier aber nicht, weil das "elements" Array Objekte vom Typ
> DisplayElements enthält, welches kein Pointer-Typ ist.
>
> De Zordo P. schrieb:
>> ..und dann liest man immer "DisplayElements" große Häppchen ein bis man
>> zum NULL Wert kommt oder?
> Tja, leider gibt es keinen NULL-Wert für DisplayElements. Du könntest
> ein extra "DisplayElements" Objekt an das Ende des Arrays packen, dessen
> Felder so gesetzt sind dass es als "Ende-Marker" erkannt wird; zB "type"
> auf 0.

Könntest du mir ein kurzes, konkretes Beispiel zeigen?
Ich bin wieder mal total vorm Berg.. :O

Ich vermute mal so in etwa?
1
const struct DisplayElements base_setting_elements[] =
2
{
3
// posx, posy,sizex,sizey,type,Text
4
 {  100,  100,   20,   20,   1, "+"  }, // "+"-Button
5
 {  200,  200,   20,   20,   1, "-"  }, // "-"-Button
6
 { NULL, NULL, NULL, NULL,   0, NULL }  // End of list marker
7
};

Doch wie komme ich genau auf die Länge ohne diese im struct als eigene 
Variable mitzugeben?

Meine "Auswertroutine" die den struct parsed sollte folgendermaßen 
aussehen (nur als Beispielcode, dieser funktioniert klar so nicht!):
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry Menu[] )
2
{
3
  uint8_t i;
4
  struct DisplayElements elements_temp;
5
6
  for( i=0; i<MenuSize; ++i )
7
  {
8
    Serial.println( Menu[i].Text );
9
    elements_temp = Menu[i].elements;
10
    while ( elements_temp.type > 0 )
11
    {
12
      Serial.println( elements_temp.posx );
13
      /* "elements" müsste hier auf das nächste array im struct springen */
14
      ..
15
    }
16
  }
17
}

Vom Prinzip her ist es mir, glaube ich, klar! Aber wie coded man das 
dann auch? Deferenzieren und Referenzieren liegen mir noch nicht so 
ganz.. :(

Danke!

von Karl H. (kbuchegg)


Lesenswert?

Georg G. schrieb:
> Eine gängige Lösung ist, die Liste mit einem NULL Pointer zu schließen.

Dazu müsste er aber erst mal ein Pointer Array haben. Was er nicht hat.

Was aber ginge, wäre zb die Vereinbarung, dass ein DisplayElement mit 
einer Position von 255 / 255 (oder anderen Zahlen, die sonst nicht 
vorkommen werden) das letzte (nicht mehr darzustellende) Element im 
Array ist.
1
void DisplayMenu( struct MenuEntry* menu )
2
{
3
  DisplayElement* element;
4
5
...
6
7
  element = menu->elements;
8
  while( element->posx != 255 && element->posy != 255 )
9
  {
10
    output( element->posx, element->posy, element->Text );
11
    element++;
12
  }
13
}

Natürlich muss sicher gestellt sein, dass der Programmierer keinen 
Fehler macht, und in allen derartigen Beschreibungen, das jeweils 
abschliessende Element einfügt.
1
const struct DisplayElements base_setting_elements[] =
2
{
3
// posx,posy,sizex,sizey,type,Text
4
 {  100, 100,   20,   20,   1, "+"  },  // "+"-Button
5
 {  200, 200,   20,   20,   1, "-"  },  // "-"-Button
6
 {  255, 255,    0,    0,   0, "" }
7
};
8
const struct DisplayElements time_setting_elements[] =
9
{
10
// posx,posy,sizex,sizey,type,Text
11
 {  300, 300,   20,   20,   2, "Option"  }, // Text field
12
 {  255, 255,    0,    0,   0, "" }
13
};

Hmm. Ich denke, eine explizite Längenangabe im struct MenuEntry
1
struct MenuEntry
2
{
3
  char             Text[20];   // Menu caption
4
  uint8_t          NrElements; // Number of Elements
5
  DisplayElements  *elements;  // Elements to draw on TFT
6
  MenuFnct         Function;   // function to call on activate/click/open
7
};
ist einfacher und sicherer. Da ist man weniger der Sorgfalt des 
Programmierers ausgeliefert.
1
const struct MenuEntry MainMenu[] =
2
{
3
  { "Base",     ARRAY_SIZE( base_setting_elements ), base_setting_elements ,     Base       },
4
  { "Uhrzeit",  ARRAY_SIZE( time_setting_elements ), time_setting_elements,     Time       },
5
};
Zum einen kann man das mit dem ARRAY_SIZE Makro so hindrehen, dass einem 
der Compiler die lästige Arbeit des Nachziehens der Konstante abnimmt, 
wenn man die Darstellung ändert. Zum anderen ist es schwerer, die Angabe 
zu vergessen, weil dann die Anzahl der Initialisierungen nicht mit der 
Strukturdefinition übereinstimmt. Und weniger Platz benötigt es auch. 
Der Nachteil ist natürlich, dass man auf eine bestimmte maximale Anzahl 
an Display-Elementen limitiert ist.

von Karl H. (kbuchegg)


Lesenswert?

PS:

Die Casts hier
1
const struct MenuEntry MainMenu[] =
2
{
3
  { "Base",     (DisplayElements*)base_setting_elements ,     Base       },
4
  { "Uhrzeit",  (DisplayElements*)time_setting_elements ,     Time       },
5
};
müssen raus.
WEnn du hier einen const Fehler kriegst, dann kann die Lösung nicht 
darin liegen, dass du das const wegcastest, sondern darin, dass du hier
1
struct MenuEntry
2
{
3
  char                   Text[20];   // Menu caption
4
  const DisplayElements* elements;  // Elements to draw on TFT
5
  MenuFnct               Function;   // function to call on activate/click/open
6
};
die notwendige const-Eigenschaft zusicherst.

Ihr Castet mir alle ein bischen zu viel und vor allen Dingen zu 
leichtsinnig.
Gerade das Umcasten von Pointern ist eine gefährliche Sache!

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> WEnn du hier einen const Fehler kriegst, dann kann die Lösung nicht
> darin liegen, dass du das const wegcastest, sondern darin, dass du hier
>
1
> struct MenuEntry
2
> {
3
>   char                   Text[20];   // Menu caption
4
>   const DisplayElements* elements;  // Elements to draw on TFT
5
>   MenuFnct               Function;   // function to call on 
6
> activate/click/open
7
> };
8
>
> die notwendige const-Eigenschaft zusicherst.
>
> Ihr Castet mir alle ein bischen zu viel und vor allen Dingen zu
> leichtsinnig.
> Gerade das Umcasten von Pointern ist eine gefährliche Sache!

Sorry, bin recht neu. Aber habe ich mir gemerkt!
Vielen Dank!

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> Was aber ginge, wäre zb die Vereinbarung, dass ein DisplayElement mit
> einer Position von 255 / 255 (oder anderen Zahlen, die sonst nicht
> vorkommen werden) das letzte (nicht mehr darzustellende) Element im
> Array ist.

würde ich gerne mit
1
if ( type != 0 ) ..
lösen.
"Type 0" muss ich nicht haben, kann also gerne als "EOF" benutzt werden.

> Natürlich muss sicher gestellt sein, dass der Programmierer keinen
> Fehler macht, und in allen derartigen Beschreibungen, das jeweils
> abschliessende Element einfügt.

Das sollte kein Problem darstellen, einmal eingefügt bleibt die 
"EOF"-Zeile im Code und wird nicht mehr verändert.

>
> Hmm. Ich denke, eine explizite Längenangabe im struct MenuEntry
>

Ok, das mache ich jetzt so! Mit dem "ARRAY" Makro damit bei der 
Umstellung der Menüstruktur nicht an mehreren Stellen 
Variablen/Konstanten umgeändert werden müssen.
1
struct MenuEntry
2
{
3
  char      Text[20];                    // Menu caption
4
  uint8_t   display_struct_size;         // Holds the size of the display_struct
5
  const     DisplayElements  *elements;  // Elements to draw on TFT
6
  MenuFnct  Function;                    // function to call on activate/click/open
7
};
8
9
const struct MenuEntry MainMenu[] =
10
{
11
  { "Base",     ARRAY_SIZE( base_setting_elements ),  base_setting_elements ,  Base  },
12
  { "Uhrzeit",  ARRAY_SIZE( time_setting_elements ),  time_setting_elements ,  Time  }
13
};

> Der Nachteil ist natürlich, dass man auf eine bestimmte maximale Anzahl
> an Display-Elementen limitiert ist.

Wegen des uint8_t?

von Karl H. (kbuchegg)


Lesenswert?

De Zordo P. schrieb:

> Sorry, bin recht neu. Aber habe ich mir gemerkt!

Wenn du etwas const machst, dann musst du das auch durchziehen!
Es ist recht sinnfrei, Variablen als 'const' anzulegen, und dann im 
kompletten Code das const wieder wegzucasten. Da hättest du dir das 
const bei den Variablen auch gleich sparen können.

Das ist ungefähr so sinnvoll, wie sich ein blaues Auto zu kaufen, nur um 
dann nach und nach alle Teile auf rot umzulackieren.

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> De Zordo P. schrieb:
>
>> Sorry, bin recht neu. Aber habe ich mir gemerkt!
>
> Wenn du etwas const machst, dann musst du das auch durchziehen!
> Es ist recht sinnfrei, Variablen als 'const' anzulegen, und dann im
> kompletten Code das const wieder wegzucasten. Da hättest du dir das
> const bei den Variablen auch gleich sparen können.
>
> Das ist ungefähr so sinnvoll, wie sich ein blaues Auto zu kaufen, nur um
> dann nach und nach alle Teile auf rot umzulackieren.

Is es richtig so viel als möglich als "const" zu definieren um flash zu 
sparen? Denn dann sollte ich auch
1
const struct MenuEntry MainMenu[] =
2
{
3
  { "Base",     ARRAY_SIZE( base_setting_elements ),  base_setting_elements ,  Base  },
4
  { "Uhrzeit",  ARRAY_SIZE( time_setting_elements ),  time_setting_elements ,  Time  }
5
};
umbauen, denn meine Aufrufe der notwendigen Funktionen laufen momentan 
so ab:
1
DoMenu( ARRAY_SIZE( MainMenu ), (MenuEntry*)MainMenu );  // MainMenu starten..
Das ist, soweit ich das verstanden habe auch Müll den ich beseitigen 
sollte!?

von Karl H. (kbuchegg)


Lesenswert?

De Zordo P. schrieb:

> Is es richtig so viel als möglich als "const" zu definieren um flash zu
> sparen?

Wenn es auf deinem nicht genannten Compiler so ist, dass er 'const' 
Dinge automatisch ins Flash legt, dann kann es unter Umständen sogar 
lebenswichtig sein, dass du das const durchziehst!

Denn wenn auf deinem System die Zugriffe ins Flash technisch anders 
durchzuführen sind als Zugriffe ins normale RAM, dann muss der Compiler 
an den verwendenden Stellen auch wissen, dass da ein Pointer ins Flash 
zeigt und nicht ins RAM um dann die jeweils technisch richtige 
Zugriffsmethode zu benutzen!

> Das ist, soweit ich das verstanden habe auch Müll den ich beseitigen
> sollte!?

Unbedingt.
Du musst dir im klaren sein, dass ein derartiger Cast das komplette 
Typ-System des Compilers aushebelt! Dein Compiler würde dich gerne 
warnen, dass hier unter Umständen etwas grauslich schief gehen kann. Mit 
dem Cast sagst du dem Compiler "Halt die Klappe!". Nur: das beseitigt 
kein potentielles Problem, wenn du dem Compiler den Mund verbietest. Ein 
mögliches Problem ist ja trotzdem immer noch vorhanden, das löst sich ja 
deswegen nicht in Luft auf nur weil die Anweisung kommt, nicht darüber 
zu reden.

Wenn ich ein Buch habe, auf das ich aufpasse und in das ich keine 
Anmerkungen geschrieben haben will (das also effektiv const ist), dann 
will ich von dir die Zusicherung haben, dass du auch nichts ins Buch 
hineinschreibst! Nichts anders macht ein const in einer 
Argumentschnittstelle
1
void DoMenu( uint8_t sizeOfMenu, const struct MenuEntry * menu )
2
{
3
  ...
4
}

Dieses const ist hier zu lesen als: Egal auf welches struct MenuEntry 
der Pointer menu auch zeigt, ich werde die dort gespeicherten Daten 
nicht verändern! Versuche ich das im Code trotzdem ...
1
void DoMenu( uint8_t sizeOfMenu, const struct MenuEntry * menu )
2
{
3
  strcpy( menu->Text, "Trallala" );
4
}
dann wird mir der Compiler auf die Finger klopfen und das nicht 
zulassen.

Und genau diese Zusicherung will ich von einer Funktion sehen, der ich 
etwas übergebe, was const ist.

von Patrick Z. (zorpat)


Lesenswert?

Da fällt mir gerade noch was auf..

Welche Unterschiede ziehen eigentlich folgende Definitionen der Funktion 
"DrawMenu" mit sich?
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry Menu[] )
2
{
3
  ..
4
}
5
6
static void DrawMenu( uint8_t MenuSize, struct MenuEntry* Menu )
7
{
8
  ..
9
}

Ist der Unterschied der, dass bei der *1.Definition*:
  1. Ein lokales struct vom Typ "MenuEntry" angelegt wird und ich 
dadurch immer die "Offsets" mit "Menu[x]" mit angeben muss?
  2. Dass der Compiler mir dann auch das RAM innerhalb der Funktion 
"doppelt" belegt?

..und bei der *2.Definition*:
  1. Es wird nur mit Pointern (dereferenziert) gearbeitet --> kein 
zusätzlicher Speicherverbrauch da nur die Adresse übergeben wird?
  2. Kann ich mit der Inkrementierung der Pointeradresse um das "sizeof( 
DisplayElements )" weiter zum nächsten Array springen kann?

Viele Frage, doch ich muss es genau wissen! :)

Danke!

von Karl H. (kbuchegg)


Lesenswert?

De Zordo P. schrieb:

> Welche Unterschiede ziehen eigentlich folgende Definitionen der Funktion
> "DrawMenu" mit sich?

Gar keinen. Höchstens dokumentarischen, weil man im Protoypen sieht, 
dass die Funktion gerne einen Pointer bekommen würde auf etwas, das sie 
wie ein Array behandelt. Aber technisch gibt es keinen Unterschied.

Die [] Schreibweise ist nur syntaktischer Zucker für Leute, die mit der 
Pointer Schreibweise nicht klar kommen.

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:

> Wenn es auf deinem nicht genannten Compiler so ist, dass er 'const'
> Dinge automatisch ins Flash legt, dann kann es unter Umständen sogar
> lebenswichtig sein, dass du das const durchziehst!

Als Compiler bzw. Entwicklungsumgebung verwende ich gerade meinen 
Arduino. Ansonsten immer GCC der mit dem Atmel Studio mitgeliefert wird.
Ich weiß aber nicht genau ob diese das "const" als Optimierung ansehen?

Fakt ist, dass beim Kompilieren (Arduino) die größe des Programms mit 
den "const" Angaben abgenommen hat. Nur um einige 10Bytes.. Aber 
vielleicht landet dann mehr auf dem Stack/RAM?
Da der Speicher, also Flash und RAM bei den Mikrocontrollern sowiso 
nicht gerade rießig ist wollte ich kompakten C-Code generieren, 
angefangen bei den Deklarationen der Variablen.
Ist das ein guter Ansatz oder soll ich den einfach "fallen" lassen?

>> Das ist, soweit ich das verstanden habe auch Müll den ich beseitigen
>> sollte!?
>
> Unbedingt.

OK, das nehme ich mir zu Herzen und schreibe die paar Zeilen gleich um 
damit ich den Compiler nicht mehr "Überreden" muss meinen Müll 
auszuführen.. ;)

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> Die [] Schreibweise ist nur syntaktischer Zucker für Leute, die mit der
> Pointer Schreibweise nicht klar kommen.

Das klingt nach mir.. ;) Aber ich setzte mich daran die Pointer aus dem 
FF zu verstehen. Dauert nur ein wenig da es als Neuling doch etwas 
Verwirrung stiftet..

von Karl H. (kbuchegg)


Lesenswert?

De Zordo P. schrieb:

> Als Compiler bzw. Entwicklungsumgebung verwende ich gerade meinen
> Arduino. Ansonsten immer GCC der mit dem Atmel Studio mitgeliefert wird.
> Ich weiß aber nicht genau ob diese das "const" als Optimierung ansehen?

Dann sparst du durch das const erst mal kein RAM ein.

Hier wird const so benutzt, wie es von der Sprache her vorgesehen ist. 
const markiert, dass sich der Wert einer Variablen während ihres 
Lebenszykluses nicht verändern wird. Kennt daher der Compiler diesen 
Wert, dann kann er überall den Wert der Variablen direkt anstelle der 
Variablen einsetzen. Unter Umständen führt das dazu, dass die Variable 
überhaupt nicht mehr gebraucht wird und daher vom Optimierer komplett 
entfernt wird.

> Fakt ist, dass beim Kompilieren (Arduino) die größe des Programms mit
> den "const" Angaben abgenommen hat.

Aber nicht bei diesen Arrays. Bei
1
static const int i = 8;
2
3
void loop()
4
{
5
  serial.print( i );
6
}

kann es sein, dass das Programm ein bischen kleiner wird, weil wie 
gesagt der Compiler das zu dem hier
1
void loop()
2
{
3
  serial.print( 8 );
4
}
umformen kann, da er ja den Wert von i kennt, das i sonst nirgends mehr 
vorkommt und das const die Zusicherung gibt "An den 8 wird sich auch 
nichts ändern, großes Indianer-Ehrenwort".

> Da der Speicher, also Flash und RAM bei den Mikrocontrollern sowiso
> nicht gerade rießig ist wollte ich kompakten C-Code generieren,
> angefangen bei den Deklarationen der Variablen.

Du scheinst mir da eine gefährliche Mischung an Halbwissen zu haben.

von Patrick Z. (zorpat)


Lesenswert?

Damit ich die Pointer besser verstehe würde ich kurz folgendes Beispiel 
vorstellen wollen..

Funktionierende Ausgangsform:
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry Menu[] )
2
{
3
  uint8_t i;
4
  for( i=0; i<MenuSize; ++i )
5
  {
6
    Serial.println(Menu[i].Text);
7
  }
8
}

Nun möchte ich die Pointerschreibweise nutzen, also:
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry * Menu )
2
{
3
  uint8_t i;
4
  for( i=0; i<MenuSize; ++i )
5
  {
6
    Serial.println(??..??);
7
  }
8
}

Wie gehe ich das am besten an?

Vielen Dank!

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> "An den 8 wird sich auch
> nichts ändern, großes Indianer-Ehrenwort".

Glaub ich dir gerne! ;)
Es wird sicherlich eine "kleine" Optimierung gegeben haben wie du 
bereits erwähnt hast.
Diese kann man aber nicht global sehen, somit versuche ich "const" als 
"const" zu verwenden und nicht als "Speicheroptimierungsflag". ;)

Karl Heinz schrieb:
> Du scheinst mir da eine gefährliche Mischung an Halbwissen zu haben.

Leider reicht das "Basiswissen Schule" nicht aus um alle Details genau 
zu kennen.. :(

von Karl H. (kbuchegg)


Lesenswert?

De Zordo P. schrieb:

> Wie gehe ich das am besten an?

Genau gleich.

Tu dir selbst einen Gefallen und besorg dir ein C-Buch.

Die Pointer-Array Dualität ist in C einer der Grundpfeiler erfolgreichen 
programmierens. Und auch wenn das in C++ ein wenig zurückgedrängt wird, 
bedeutet das auch in C++ nicht, dass es sich um ein unwesentliches 
Detail handelt, über das man nicht Bescheid wissen muss.

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb im Beitrag #3882155:
> Tu dir selbst einen Gefallen und besorg dir ein C-Buch.

Kann und werde ich machen, doch zuerst muss es bei mir Zuhause warm 
werden, dazu muss mein Controllerboard mitsamt dem Code funktionieren.. 
;)

Karl Heinz schrieb im Beitrag #3882155:
> Genau gleich.

Hm...
Also wenn ich richtig verstehe, bedeutet das soviel, dass durch die 
"[]"-Schreibweise nichts anderes gemacht wird als die Adresse auf die 
der Pointer zeigt geändert wird? Und zwar immer in "struct MenuEntry" 
Schritten? oder?
Ich könnte also den Pointer "*Menu" um das "sizeof(MenuEntry)" 
inkrementieren und lande im nächsten Array des Menüs? :O

von Karl H. (kbuchegg)


Lesenswert?

Patrick Z. schrieb:
> Karl Heinz schrieb im Beitrag #3882155:
>> Tu dir selbst einen Gefallen und besorg dir ein C-Buch.
>
> Kann und werde ich machen, doch zuerst muss es bei mir Zuhause warm
> werden, dazu muss mein Controllerboard mitsamt dem Code funktionieren..
> ;)
>
> Karl Heinz schrieb im Beitrag #3882155:
>> Genau gleich.
>
> Hm...
> Also wenn ich richtig verstehe, bedeutet das soviel, dass durch die
> "[]"-Schreibweise nichts anderes gemacht wird als die Adresse auf die
> der Pointer zeigt geändert wird? Und zwar immer in "struct MenuEntry"
> Schritten? oder?
> Ich könnte also den Pointer "*Menu" um das "sizeof(MenuEntry)"
> inkrementieren und lande im nächsten Array des Menüs? :O

Welchen Teil von 'genau gleich' hast du nicht verstanden
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry * Menu )
2
{
3
  uint8_t i;
4
  for( i=0; i<MenuSize; ++i )
5
  {
6
    Serial.println(Menu[i].Text);
7
  }
8
}

In C ist die Array-Index Operation gemeinsam mit Pointer-Arithmetik 
genau so definiert, dass das geht. Daher ist es auch so essentiell 
wichtig, diese Dinge von der Pieke auf zu lernen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

>> Also wenn ich richtig verstehe, bedeutet das soviel, dass durch die
>> "[]"-Schreibweise nichts anderes gemacht wird als die Adresse auf die
>> der Pointer zeigt geändert wird?

Nein.

Es gibt keinen technischen Unterschied zwischen
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry * Menu )
und
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry Menu[] )

die beiden sind komplett und in allen technischen Belangen identisch! Es 
sind nur 2 verschiedene Schreibweisen für ein und dasselbe.

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> In C ist die Array-Index Operation gemeinsam mit Pointer-Arithmetik
> genau so definiert, dass das geht.

Ja, das habe ich verstanden.. ABER wäre es möglich auch den Pointer 
direkt zu manipulieren?
Ich weiß das ist KEINE gute Idee da man damit auch ins "Nichts" 
verweisen kann... :)

von Karl H. (kbuchegg)


Lesenswert?

Patrick Z. schrieb:

> Ja, das habe ich verstanden.. ABER wäre es möglich auch den Pointer
> direkt zu manipulieren?

Natürlich.

Aber auch hier wieder. DU inkementierst einfach.
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry * Menu )
2
{
3
  uint8_t i;
4
5
  for( i=0; i<MenuSize; ++i )
6
  {
7
    Serial.println(Menu->Text);
8
    Menu++;
9
  }
10
}


oder, wenn du das Funktionsargument unversehrt lassen willst.
1
static void DrawMenu( uint8_t MenuSize, struct MenuEntry * Menu )
2
{
3
  uint8_t i;
4
  struct MenuEntry* tmp = Menu;
5
6
  for( i=0; i<MenuSize; ++i )
7
  {
8
    Serial.println(tmp->Text);
9
    tmp++;
10
  }
11
}

Da braucht es keinen sizeof. Der COmpiler weiss ja, dass 'Menu' (oder 
'tmp') auf ein struct MenuEntry zeigt. Pointer Arithmetik ist so 
definiert, dass in
1
   T * pPointer;      // T sei irgendein Datentyp
2
3
   pPointer++;
4
   pPointer += 2;
5
   pPointer .....
die sizeof(T) automatisch immer mit eingerechnet wird. Darum brauchst du 
dich daher nicht kümmern und willst es auch gar nicht. Das ist ja auch 
einer der Gründe, warum du nicht einfach sagst "Das ist ein Pointer" 
sondern "Das ist ein Pointer auf ein struct Menu" oder "Das ist ein 
Pointer auf int" oder "Das ist ein Pointer auf ..."

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Das ist ja auch
> einer der Gründe, warum du nicht einfach sagst "Das ist ein Pointer"
> sondern "Das ist ein Pointer auf ein struct Menu" oder "Das ist ein
> Pointer auf int" oder "Das ist ein Pointer auf ..."


Und letzten Endes ist ja auch die Index Operation in C so definiert:
1
    a[i]  <==> *( a + i )

genau das macht der Compiler, wenn du
1
  int a[5];
2
3
  for( i = 0; i < 5; i++)
4
    serial.println( a[i] );
schreibst. Er formt als erstes den Ausdruck 'a[i]' in die äquivalente 
Pointer Schreibweise '*(a+i)' um. Der Name des Arrays fungiert 
(alleinstehend) als die Startadresse des Arrays. Dazu wird noch der 
Offset i addiert (unter Berücksichtung der sizeof dessen, worauf der 
Pointer zeigt) und mit der so erhaltenen Speicheradresse wird 
dereferenziert.

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> Da braucht es keinen sizeof. Der COmpiler weiss ja, dass 'Menu' (oder
> 'tmp') auf ein struct MenuEntry zeigt. Pointer Arithmetik ist so
> definiert, dass in   T * pPointer;      // T sei irgendein Datentyp
>
>    pPointer++;
>    pPointer += 2;
>    pPointer .....
> die sizeof(T) automatisch immer mit eingerechnet wird. Darum brauchst du
> dich daher nicht kümmern und willst es auch gar nicht. Das ist ja auch
> einer der Gründe, warum du nicht einfach sagst "Das ist ein Pointer"
> sondern "Das ist ein Pointer auf ein struct Menu" oder "Das ist ein
> Pointer auf int" oder "Das ist ein Pointer auf ..."

Das ist eine PERFEKTE Antwort! :)))

Vieeeelen vieeeelen Dank!

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> Karl Heinz schrieb:
>
>> Das ist ja auch
>> einer der Gründe, warum du nicht einfach sagst "Das ist ein Pointer"
>> sondern "Das ist ein Pointer auf ein struct Menu" oder "Das ist ein
>> Pointer auf int" oder "Das ist ein Pointer auf ..."
>
>
> Und letzten Endes ist ja auch die Index Operation in C so definiert:
>
>
1
>     a[i]  <==> *( a + i )
2
>
>
> genau das macht der Compiler, wenn du
>
1
>   int a[5];
2
> 
3
>   for( i = 0; i < 5; i++)
4
>     serial.println( a[i] );
5
>
> schreibst. Er formt als erstes den Ausdruck 'a[i]' in die äquivalente
> Pointer Schreibweise '*(a+i)' um.

Karl Heinz,
darf ich dich bitten mir einen konkreten Buchvorschlag zum Thema "C", 
vielleicht mit Schwerpunkt µC, ist aber auch nicht so wichtig, zu geben?
Ich will und muss mich einlesen. Ich möchte dann noch weitere Projekte 
angehen welche dann immer komplizierter werden und ich dann immer mehr 
"auf dem Schlauch" stehen werde..

von Karl H. (kbuchegg)


Lesenswert?

Patrick Z. schrieb:

> darf ich dich bitten mir einen konkreten Buchvorschlag zum Thema "C",
> vielleicht mit Schwerpunkt µC, ist aber auch nicht so wichtig, zu geben?

Das musst du sowieso trennen. Das sind 2 verschiedene Dinge.
Das eine ist die Programmierung mit der Programmiersprache C.
Das andere ist die µC Programmierung.

Die beiden haben natürlich irgendwo Berührungspunkte. Trotzdem ist es 
sinnvoll die Dinge zu trennen. Denn C funktioniert (bis auf Ausnahmen 
:-) überall gleich. Abgesehen von ein paar Details wie Bytegrößen von 
Datentypen und noch ein paar anderen Dingen, ist die Logik der Sprache, 
wie sie funktioniert und was die einzelnen Dinge bedeuten vom µC bis zum 
Supercomputer überall identisch.

Eine ziemlich vernünftige Empfehlung ist, sich die ersten C Hörner erst 
mal auf dem PC abzustossen und dort C in den Grundzügen zu lernen. Der 
Vorteil ist, dass es da massenhaft Literatur gibt (manche mit 
zweifelhaftem Lernwert), bei denen aber ausnahmslos auf einem PC erst 
mal alle Beispiele aus dem Buch funktionieren werden. Der Grund dafür 
ist einfach: C definiert ja auch ein gewisses Verhalten gegenüber der 
Umwelt. Ein printf funktioniert auf allen Systemen, die eine Konsole 
haben immer gleich und zaubert eine Ausgabe auf die Konsole. Nur: Auf 
einem µC hat man meistens keine Konsole, sondern muss sich die ebenfalls 
erst mal selber programmieren. Kurz und gut: Die in C-Büchern 
vorausgesetzte Umgebung existiert auf einem PC bereits und die C-Systeme 
kommen so daher, dass sie auch genau so funktioniert, wie im C-Standard 
(und damit auch in den Büchern) beschrieben. Auf einem µC ist das aber 
nicht so.
Dazu kommt noch, dass man auf einem PC viel bessere Debug-Möglichkeiten 
hat, als auf einem µC. D.h der Lernfortschritt ist auf dem PC in 
derselben Zeit viel höher als auf einem µC, weil man sich nicht mit 
µC-spezifischen Dingen rumschlagen muss wie zb die banale Ausgabe von 
Werten auf einem LCD.

Erst dann, wenn man die C Programmierung sei leidlich auf dem PC 
beherrscht, ist es sinnvoll auf einen µC zu wechseln und sich den 
Spezialitäten zu widmen, die das besondere Umfeld µC so mit sich bringt. 
Das Wissen, das man sich auf dem PC erwworben hat, ist ja nicht 
verloren. Die Sprache an sich funktioniert ja auf dem µC gleich zum PC. 
Weg fällt die Art und Weise, wie sich ein Programm nach aussen bemerkbar 
machen kann, dafür gibt es dann eben neue spezielle Dinge, die auf dem 
PC wieder nicht so wichtig sind, wie zb die Beherrschung von 
Bitoperationen.


C Bücher gibt es viele.
Der Klassiker "Kernighan & Ritchie, Programmieren in C" wird gerne 
genommen. Aber am besten ist es wohl, wenn du in eine Buchhandlung gehst 
(oder Bibliothek) und einfach mal in ein paar Büchern schmökerst, mit 
wessen Sprache du am besten zurecht kommst.

Es wird auch nicht so sein, dass du nach Durcharbeiten des Buches zu 
einem C-Spezialisten wirst. Da gibt es immer noch viele Feinheiten. Aber 
es ist schon mal viel einfacher, wenn man nicht an allen Ecken und Enden 
bei Adam und Eva anfangen muss.

von Patrick Z. (zorpat)


Lesenswert?

Karl Heinz schrieb:
> Es wird auch nicht so sein, dass du nach Durcharbeiten des Buches zu
> einem C-Spezialisten wirst. Da gibt es immer noch viele Feinheiten. Aber
> es ist schon mal viel einfacher, wenn man nicht an allen Ecken und Enden
> bei Adam und Eva anfangen muss.

Das meinte ich.. :-)

Vielen herzlichen Dank nochmal!

Jetzt versuche ich die "SPI-Grafikroutine" zu Programmieren, dann die 
automatische grafische Generierung der Oberfläche inkl. der Auswertung 
der Touch-Eingaben und dann hoffen wir mal, dass es langsam wieder warm 
wird.. :-P
Evtl. erstelle ich dann wieder ein neues Thema sobald ich wieder an die 
Grenzen meines "C-Universe" komme..

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.