Forum: Compiler & IDEs Umgang mit globalen Datenstrukturen


von Christian J. (Gast)


Lesenswert?

Moin,

auch mein Hobby Code soll sauber sein. Bei einem größeren Projekt mit 
einer Wetterstation für den F429, die auch noch 3 externe Sensoren 
bedient und ein extra Anzeige Display gibt es bei mir eine größere 
globale Struktur, in der etliche Wetter-Werte liegen, viele davon auch 
noch als Arrays of struct, da 5 Tage stundenweise gespeichert werden. 
Deswegen, damit das Ganze mit __packed in einem Rutsch per Funk an einen 
PC übertragen werden kann. Der Trümmer ist rund 4k gross, global 
external im main.h deklariert und in main.c implementiert, liegt per 
Linker Direktive im residenten Batt-gepufferten BKRAM drin, nahezu alle 
Routinen greifen darauf zu aus den ca 9 .c Files. Die einen füllen ihn, 
andere holen sich für die LCD Grafik alles da raus.

Nur.... ist das im Standard C so usus? Dass Routinen einfach sich das 
was rauspicken was sie brauchen, ganz ohne Funktionsparameter? Jede 
Routine kann ja alles verändern was sie will. main.h ist von allen 
Modulen aus sichtbar.

Wie ist da eine saubere Lösung? Einzelne Werte aus dem Struct als Zeiger 
übergeben? Ist natürlich deutlich mehr Aufwand, dem uC dürfte es egal 
sein, der Lesbarkeit und Wartbarkeit des Codes aber nicht.

Ok, C++... Klassen und gekapselte Daten aber das kann ich leider nicht, 
ging bisher ohne auch.

Wie macht ihr das?

Christian

von Daniel (Gast)


Lesenswert?

Du kannst in der main getter und setter implentieren und die Struktur in 
der main.c definieren. Dadurch haben externe module einen definierten 
Zugriff.

von Dirk B. (dirkb2)


Lesenswert?

Christian J. schrieb:
> Jede
> Routine kann ja alles verändern was sie will.

Ja, kann sie.

Daher liegt es an dir (als Programmierer) dieser Versuchung zu 
widerstehen.

> Einzelne Werte aus dem Struct als Zeiger
> übergeben?

Beim Zeiger ändert sich nichts an der Problematik des Änderns.

I.A. sollten die Funktionen nur (Zugriff auf) so viel Daten bekommen, 
wie sie auch brauchen.

structs kann man auch by-value übergeben. (4k auf Mikrocontroller ist 
blöd)
Daher gelten da ja auch etwas andere Regeln: globale Variablen sind ok.

Im Übrigen hast du bei GUI meist auch mehrere globale struct.

von Egon D. (Gast)


Lesenswert?

Christian J. schrieb:

> Nur.... ist das im Standard C so usus? Dass Routinen
> einfach sich das was rauspicken was sie brauchen,
> ganz ohne Funktionsparameter? Jede Routine kann ja
> alles verändern was sie will. main.h ist von allen
> Modulen aus sichtbar.

Kann man machen.
Hat aber bekanntermaßen den Nachteil, dass man i.d.R.
an vielen verstreuten Stellen etwas ändern muss, wenn
sich diese zentrale Datenstruktur ändert.


> Ok, C++... Klassen und gekapselte Daten aber das
> kann ich leider nicht, ging bisher ohne auch.

Heute eine weitere Folge aus der Serie: "Das kannst
auch Du" :)


> Wie macht ihr das?

Unabhängig von der Sprache - Kapseln: Zugriff auf die
Daten konsequent NUR über Getter und Setter, NIEMALS
direkt auf die Variablen. (Letztlich ist das -- die
Kapselung -- auch der entscheidende Trick an der OOP.)

Speziell in C: Ich weiss nicht, ob ich das Lehrbuch
richtig verstanden habe, aber "static" sollte hier
weiterhelfen. Die Variablen sowie alle Getter und
Setter kommen in einen eigenen Quelltext; die Variablen
werden außerhalb der Funktionen ("global") vereinbart
und bekommen das Attribut "static"; die Funktionen
bekommen es aber nicht.
Alle Funktionen sind jetzt von außerhalb sichtbar,
nicht aber die Variablen.

von Christian J. (Gast)


Lesenswert?

Ok,

soweit verstanden, diese Setter und Getter bzw Wrapper nutze ich schon 
lange "intuitiv" im Interface eines Moduls. Vor allem damit Änderungen 
nicht überall gemacht werden müssen. Sie werden vom GCC ja ohnehin 
spurlos aufgelöst, bewirken halt nur die Kapselung.

Denke, dass das Thema damit schon geklärt ist. Danke, Leute!

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Dirk B. schrieb:
> ...(4k auf Mikrocontroller ist blöd)...

Was ist denn daran "blöd" auf eine µC der 256k RAM hat?

von Christian J. (Gast)


Lesenswert?

Irgend W. schrieb:
> Was ist denn daran "blöd" auf eine µC der 256k RAM hat?

Ähm.. hust... genauer gesagt sind es 8MB. 8MB SRAM am ExtMemory
Bus, da es ein kastriertes Disco Board ist, wo das Display runter
ist und durch ein serielles ILI9341 SPI Display ersetzt wurde.
Linker File entsprechend angepasst.

Er meint vielleicht, dass man 4k nicht unbedingt call-by value
übergeben sollte, weil das den Stack massiv aufbläst und
sinnlose Kopie-Rechenzeit verbrät.

von Nop (Gast)


Lesenswert?

Dirk B. schrieb:

> structs kann man auch by-value übergeben. (4k auf Mikrocontroller ist
> blöd)

Auch am PC wird man hier normalerweise lieber Zeiger auf const 
übergeben.

von A. S. (Gast)


Lesenswert?

Getter und Setter sind oft sinnvoll. Wenn Du am Ende aber für jedes 
Datenfeld 2 oder mehr Funktionen hast, ist das Quatsch. Getter und 
Setter sind umso sinnvoller, je komplizierter der Zugriff ist (z.b. 
baudrate in Teiler umrechnen) oder je eher die Implementierung sich 
ändern kann (uint8 -> uint16 -> Zeiger ...).

Ein reines Feld fester Register ist oft als globale Struktur(en) genauso 
gut und bei vielen Bausteinen üblich.

von S. R. (svenska)


Lesenswert?

Christian J. schrieb:
> Ok, C++... Klassen und gekapselte Daten aber
> das kann ich leider nicht, ging bisher ohne auch.

Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen, 
sondern dich erstmal auf "C-with-classes" beschränken.

Aus der C-Sicht ist eine Klasse erstmal nichts weiter als eine normale 
Struktur mit zusätzlichen Funktionen. Jedes Element (Variable oder 
Funktion) kann für externen Zugriff blockiert (private) oder freigegeben 
(public) werden.

Fertig. Das allein sollte dir weiterhelfen.
Und wenn Wilhelm loslegt, hörst du erstmal weg. :-)

von Nick M. (Gast)


Lesenswert?

> Ok, C++... Klassen und gekapselte Daten aber
> das kann ich leider nicht, ging bisher ohne auch.

Jetzt zitier ich das auch noch. :-)
Es geht um Kapselung. Deine Datenstruktur geht den Rest des Programms so 
wenig wie möglich an.
Daher wandert deine globale Struktur in eine eigene .c und wird dort als 
static deklariert.
Im .h stehen nur die typedefs, enums, function, ... die nach aussen hin 
nötig sind.
Dann hast u (wie mehrfach genannt) getter und setter Funktionen die auf 
die Daten zugreifen können.
Alle Sicherheitsüberprüfungen (index out of range etc.) hast du somit 
nur in dem einen neuen .c. Der Rest des Programms kann sich darauf 
verlassen, dass möglichst jeder Unsinn abgefangen wird. Und auch in der 
Debug-Konsole ausgegeben wird.
Du kannst/sollst da auch die streaming-Funktion (senden an PC) 
reinbauen.

Alle Funktionen so weit möglich mit const Parametern, dann kann der 
Compiler die meisten selbst auferlegten Beschränkungen rausoptimieren.
Meistens kommt bei dem "data hiding" dabei sogar ein kompakterer Code 
raus. Einfach weil der Code in nur einem Modul auftaucht und nicht 
überall verteilt Kopien.


Nick

von Christian J. (Gast)


Lesenswert?

S. R. schrieb:
> Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,
> sondern dich erstmal auf "C-with-classes" beschränken.

Reicht das das Studium einer Arduino Lib erstmal? Oder gleich das 
geballte 500 Seiten Werk "C++ für Dummies"? Ich habe schon vor zig 
Jahren um die Jahrtausendwende beim Bjarne Stroustrup abgeschaltet und 
nichts verstanden. Und lieber meine Original Ausgabe von K&R 
geschmökert.

von Nick M. (Gast)


Lesenswert?

> Ich habe schon vor zig
> Jahren um die Jahrtausendwende beim Bjarne Stroustrup abgeschaltet

Der Stroustrup ist ganz gut. Zumindest kann man sich da das Konzept der 
OOP erarbeiten. Auch als C-Programmierer!
Beim OOP gehts nur um das Konzept und das dann sinngemäß umzusetzen 
(wenn sinnvoll).
Und nicht vergessen, C++ hat als Präprozessor für C angefangen.

> und nichts verstanden. Und lieber meine Original Ausgabe von K&R
> geschmökert.

K&R find ich absolut fürchterlich. C in a nutshell von Koenig bei 
O'Reilly ist zum Nachschlagen und Auffrischen sehr gut. Nehme ich immer 
wieder gerne her.

Nick

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:
> Christian J. schrieb:
>> Ok, C++... Klassen und gekapselte Daten aber
>> das kann ich leider nicht, ging bisher ohne auch.
>
> Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,
> sondern dich erstmal auf "C-with-classes" beschränken.
>
> Aus der C-Sicht ist eine Klasse erstmal nichts weiter als eine normale
> Struktur mit zusätzlichen Funktionen. Jedes Element (Variable oder
> Funktion) kann für externen Zugriff blockiert (private) oder freigegeben
> (public) werden.
>
> Fertig. Das allein sollte dir weiterhelfen.
> Und wenn Wilhelm loslegt, hörst du erstmal weg. :-)

Ok, ich halte mich zurück ;-)

Aber: C++24 fällt durchs Raster ... C++23 wird spannend genug!

von S. R. (svenska)


Lesenswert?

Christian J. schrieb:
>> Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,
>> sondern dich erstmal auf "C-with-classes" beschränken.
>
> Reicht das das Studium einer Arduino Lib erstmal? Oder gleich
> das geballte 500 Seiten Werk "C++ für Dummies"? Ich habe schon
> vor zig Jahren um die Jahrtausendwende beim Bjarne Stroustrup
> abgeschaltet und nichts verstanden.

Um in deinen kleinen C-Code eine oder zwei Klassen einzubauen, um von 
einer globalen Monsterstruktur wegzukommen, braucht man weder das eine 
noch das andere.

Aber wenn du dich partout weigerst, auch nur den kleinsten Schritt in 
diese Richtung zu gehen, dann ist das Thema ohnehin tot.

> Und lieber meine Original Ausgabe von K&R geschmökert.

Wer nicht will, der hat schon.

von Wilhelm M. (wimalopaan)


Lesenswert?

Christian J. schrieb:
> Moin,
>
> auch mein Hobby Code soll sauber sein. Bei einem größeren Projekt mit
> einer Wetterstation für den F429, die auch noch 3 externe Sensoren
> bedient und ein extra Anzeige Display gibt es bei mir eine größere
> globale Struktur, in der etliche Wetter-Werte liegen, viele davon auch
> noch als Arrays of struct, da 5 Tage stundenweise gespeichert werden.
> Deswegen, damit das Ganze mit __packed in einem Rutsch per Funk an einen
> PC übertragen werden kann. Der Trümmer ist rund 4k gross, global
> external im main.h deklariert und in main.c implementiert, liegt per
> Linker Direktive im residenten Batt-gepufferten BKRAM drin, nahezu alle
> Routinen greifen darauf zu aus den ca 9 .c Files. Die einen füllen ihn,
> andere holen sich für die LCD Grafik alles da raus.

Hört sich nach FORTRAn an: alles in einem common-block.

> Ok, C++... Klassen und gekapselte Daten aber das kann ich leider nicht,

Warum KANNST du das nicht? Du kannst doch auch 16-Bit in ein uint16_t 
packen ...

von Egon D. (Gast)


Lesenswert?

S. R. schrieb:

> Um in deinen kleinen C-Code eine oder zwei Klassen
> einzubauen, um von einer globalen Monsterstruktur
> wegzukommen, braucht man weder das eine noch das
> andere.

Mal eine Frage: Woher kommt eigentlich der
ununterdrückbare Drang auf µC.net, einem Fragesteller
eine deutlich kompliziertere Lösung zu empfehlen, als
er braucht?

Soweit ich das beurteilen kann, hat sein Problem mit
Kapselung zu tun. Kapseln kann man aber auch in
ANSI-C; dazu braucht's kein C++.

von S. R. (svenska)


Lesenswert?

Egon D. schrieb:
> Soweit ich das beurteilen kann, hat sein Problem mit
> Kapselung zu tun. Kapseln kann man aber auch in
> ANSI-C; dazu braucht's kein C++.

Ich finde die Grundlagen von Klassen jetzt nicht besonders kompliziert. 
Insbesondere dann nicht, wenn es sich auf dem Niveau von "class als 
struct mit Funktionen" drin bewegt. Nix Mehrfachvererbung, Templates, 
Lambdas, STL, zukünftige Coolness oder so.

Aber ja, man kann auch in C ordentlich kapseln und auch komplette OOP 
mit (Einfach-)Vererbung hochziehen. Ich habe nie das Gegenteil 
behauptet.

von Christian J. (Gast)


Lesenswert?

Naja, es ging nur um das hier... schon 2 Jahre her. Kann man sicher 
eleganter machen.
1
struct maxima_t {
2
          /* --- Board Sensoren --- */
3
    float P_Max,
4
          P_Min,
5
          T_Innen_Max,
6
          T_Innen_Min,
7
          /* -- Funk Sensor -- */
8
          T_Ext_Max,
9
          T_Ext_Min,
10
          F_Ext_Max,
11
          F_Ext_Min;
12
};
13
14
typedef struct
15
{
16
    int32_t  id;                                         // = 0x1234 nach Initialisierung
17
    uint16_t ptr;
18
    float    P_Now,                                      // Aktueller Druckwert
19
             T_Now;                                      // Aktueller Innen-Temperaturwert
20
21
    uint32_t P_MaxGraph,                                 // Aktuell oberer Druckwert im Bargraphen
22
             P_MinGraph;
23
24
    float    T_MaxGraph,                                 // Schwankungen um den Messpunkt
25
             T_MinGraph;
26
27
    // Maxima der Historie: Innen-Temperatur, Druck
28
    float    T_MaxInHistory,                             // Maximaler gemessener INNEN Temperaturwert
29
             T_MinInHistory,                             // Minimaler gemessener INNEN Temperaturwert
30
             T_AvgInHistory;
31
32
    uint32_t P_MaxInHistory,                             // Maximaler gemessener Druckwert
33
             P_MinInHistory;                             // Minimaler gemessener Druckwert
34
    float    P_AvgInHistory;
35
36
    float    RegrSteigung;                               // Steigung der Regressionsgeraden
37
    float    Ext_Temperature;
38
    float    Ext_Feuchte;
39
    float    Ext_UBatt;
40
    gebiet_t gebiet;                                     // Hoch, Tief, Neutral
41
    uint8_t  ZFN;                                        // Zambretti Forecast Letter
42
    struct   data_t History[HISTORY_MAX_IDX+1];          // Historie, stuendliche Eintragungen
43
44
}  Work_t __attribute__ ((packed));
45
46
47
// __attribute__ ((packed))
48
49
// Flags für die Zeit
50
typedef struct {
51
   _Bool   rtc_1s,
52
           rtc_sec10,
53
           rtc_1min,
54
           rtc_1hr,
55
           rtc_120min,
56
           rtc_1day,
57
           tag_nacht_wechsel,
58
           night,
59
           rf_timeout,          // 1 = RF Modul meldete sich nicht mehr
60
           f_SendHistoryToDisplay,
61
           NRF24L01_IsPresent,
62
           backlight;
63
64
} flags_t;

von Peter D. (peda)


Lesenswert?

Daniel schrieb:
> Du kannst in der main getter und setter implentieren und die Struktur in
> der main.c definieren.

Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.
Brauche ich dann 1000 Getter+Setter oder wie habe ich mir das 
vorzustellen?

von Nop (Gast)


Lesenswert?

Peter D. schrieb:

> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.

Sowas solltest Du gar nicht erst haben.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Daniel schrieb:
>> Du kannst in der main getter und setter implentieren und die Struktur in
>> der main.c definieren.
>
> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.

Was meinst Du mit Parametern bei einem struct?

> Brauche ich dann 1000 Getter+Setter oder wie habe ich mir das
> vorzustellen?

Wenn Du vorher eclipse benutzt hast, bestimmt noch mehr ;-)

von Christian (Gast)


Lesenswert?

Nop schrieb:
> Peter D. schrieb:
>
> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.
>
> Sowas solltest Du gar nicht erst haben.

Naja, MySQL auf uc ist vermutlich etwas heftig.

von Peter D. (peda)


Lesenswert?

Nop schrieb:
> Sowas solltest Du gar nicht erst haben.

D.h. ich soll keine Steuerungen mehr bauen?
Die Physiker wollen bei Teilchenquellen und Analysatoren nen Haufen 
Spannungen/Ströme setzen, lesen, überwachen und regeln können. 100 
Parameter ist da eher eine kleine Anzahl.

von Nick M. (Gast)


Lesenswert?

> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.

Dann ist das vermutlich ein enum als Parameter und es gibt eine getter 
und eine setter-Funktion.

Nick

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Anbei mal die Struct.

Nick M. schrieb:
> Dann ist das vermutlich ein enum als Parameter und es gibt eine getter
> und eine setter-Funktion.

Enum ja, aber alle können direkt zugreifen.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Egon D. schrieb:
> Zugriff auf die Daten konsequent NUR über Getter und Setter, NIEMALS
> direkt auf die Variablen.

Peter D. schrieb:
> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.
> Brauche ich dann 1000 Getter+Setter oder wie habe ich mir das
> vorzustellen?

Auch wenn es in einigen Anfänger-C++-Lehrbüchern so diktiert wird: Für
jede Membervariable grundsätzlich und ohne nachzudenken eine Set- und
Get-Funktion zu schreiben, ist Unfug. Wäre das so gewollt, gäbe es in
C++ weder Structs noch Public-Variablen.

Dieser Meinung ist übrigens auch die C++-Prominenz:

  https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rh-get

von Nop (Gast)


Lesenswert?

Peter D. schrieb:
> Nop schrieb:
>> Sowas solltest Du gar nicht erst haben.
>
> D.h. ich soll keine Steuerungen mehr bauen?

Du sollst Steuerungen nicht mit einem Megastruct bauen.

von Nick M. (Gast)


Lesenswert?

Peter D. schrieb:
> Enum ja, aber alle können direkt zugreifen.

Worauf? Auf die Daten? Das wäre nicht gewünscht. Der enum sagt, auf 
welches Datum mit dem getter/setter zugeriffen wird. Abhängig vom Typen.


Nick

von Egon D. (Gast)


Lesenswert?

Yalu X. schrieb:

> Egon D. schrieb:
>> Zugriff auf die Daten konsequent NUR über Getter und
>> Setter, NIEMALS direkt auf die Variablen.
>
> Peter D. schrieb:
>> Angenommen ich habe eine Struct von 100 Parametern mit
>> 5 Elementen. Brauche ich dann 1000 Getter+Setter oder
>> wie habe ich mir das vorzustellen?
>
> Auch wenn [...]

Quotemarder. Pfui!

von Yalu X. (yalu) (Moderator)


Lesenswert?

Egon D. schrieb:
> Quotemarder. Pfui!

Wieso Quotemarder?

Habe ich deine Aussage in irgendeiner Form sinnentstellend zitiert?

Wenn ja, musst du mir erklären, was du damit gemeint hast. Für mich ist
ihr Inhalt – nicht zuletzt durch die Hervorhebung der Wörter "NUR" und
"NIEMALS" – völlig unmissverständlich, unabhängig vom Kontext.

von Egon D. (Gast)


Lesenswert?

Yalu X. schrieb:

> Egon D. schrieb:
>> Quotemarder. Pfui!
>
> Wieso Quotemarder?
>
> Habe ich deine Aussage in irgendeiner Form
> sinnentstellend zitiert?

Durchaus, ja. :)


> Wenn ja, musst du mir erklären, was du damit gemeint hast.

Meine Aussage war:
"Wenn man eine Variable kapselt, muss man sie konsequent
kapseln. Das bedeutet, man greift NUR über die Methoden
und NIEMALS direkt auf die Variable zu -- und das geht
sogar auch in einer nicht-OOP-Sprache."

Du argumentierst aber gegen:
"Wann man kapselt, muss man IMMER ALLE Variablen kapseln."

Letzteres ist jedoch überhaupt nicht meine Aussage.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Egon D. schrieb:
> Meine Aussage war:
> "Wenn man eine Variable kapselt, muss man sie konsequent
> kapseln. Das bedeutet, man greift NUR über die Methoden
> und NIEMALS direkt auf die Variable zu -- und das geht
> sogar auch in einer nicht-OOP-Sprache."

Das hört sich vernünftig an, und das Ganze war wohl ein Missverständnis.
Ich habe deine Teilaussagen ursprünglich so verstanden:

Egon D. schrieb:
> Unabhängig von der Sprache - Kapseln:

"Man sollte immer Kapseln, auch in nicht OO-Sprachen."

> Zugriff auf die Daten konsequent NUR über Getter und Setter, NIEMALS
> direkt auf die Variablen.

"Da immer gekapselt wird, darf der Zugriff auf (Member-)Variablen nur
über Getter und Setter erfolgen."

Nachdem das Missverständnis nun aufgelöst ist, gehe ich davon aus, wir
beider der Meinung sind, dass Peda für seine 500 Struct-Elemente keine
Get- und Set-Funktionen braucht, oder?

von Egon D. (Gast)


Lesenswert?

Yalu X. schrieb:

> Ich habe deine Teilaussagen ursprünglich so verstanden:
>
> Egon D. schrieb:
>> Unabhängig von der Sprache - Kapseln:
>
> "Man sollte immer Kapseln, auch in nicht OO-Sprachen."

Verstehe. -- Nein, die Langform ist eher ungefähr so:

"Ich bin nicht qualifiziert, Dir einen Rat zu C oder C++
zu geben, aber Dein konkretes Problem lässt sich durch
sinnvolle Kapselung lösen, und Kapselung ist weitgehend
unabhängig von der konkreten Sprache anwendbar. Die
Regeln dafür sind folgende:..."


> Nachdem das Missverständnis nun aufgelöst ist, gehe ich
> davon aus, wir beider der Meinung sind, dass Peda für
> seine 500 Struct-Elemente keine Get- und Set-Funktionen
> braucht, oder?

Ja natürlich -- zumindest nicht für jedes Element einzeln :)

von S. R. (svenska)


Lesenswert?

Wann immer Getter und Setter trivial sind, sind sie auf Überflüssigkeit 
zu prüfen. Leider bestehen viele Tools darauf, erstmal sämtliche dieser 
Funktionen zu erzeugen und weil Dogmatik zählt, macht die auch keiner 
weg.

Aber hey, in Kotlin wird jedes getX/setX-Pärchen als "virtuelle 
Membervariable" X betrachtet. Dann sieht es im Code wieder so aus, als 
ob man Membervariablen zuweist. Im Bytecode ist es natürlich trotzdem 
ein Funktionsaufruf.

Eine Struktur mit 100 Elementen als Klasse zu machen fällt übrigens 
meistens unter "Gottobjekt" und ist nach Dogma ebenfalls zu vermeiden. 
Aber manchmal braucht man sowas trotzdem.

von Christian J. (Gast)


Lesenswert?

Ok.... ich lass euch dann mal weiter diskutieren. Meine Frage ist 
eigentlich beantwortet. Danke! Bin dann mal raus hier...

von Wilhelm M. (wimalopaan)


Lesenswert?

Christian J. schrieb:
> Ok.... ich lass euch dann mal weiter diskutieren. Meine Frage ist
> eigentlich beantwortet. Danke! Bin dann mal raus hier...

Was ist denn Dein Fazit?

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Meine Frage ist
> eigentlich beantwortet. Danke! Bin dann mal raus hier...

Solche Antworten fallen unter die Rubrik, hätte man besser sein lassen 
sollen.
Wem willst Du damit helfen?

von Christian J. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Was ist denn Dein Fazit?

Dass man das so kapseln kann wie direkt oben beschrieben. Oder eben C++ 
verwenden. Zumindest werde ich es bei größeren Sachen machen, nicht aber 
wenn ich wie gerade nur 2 Module habe und es läuft und nie erweitert 
werden wird.
Mein "Game of Life" hat auch zwei Felder, wäre aber sinnlos die zu 
kapseln.  Ein Zugriff feld.alt[x][y] und feld.neu[x][y] bleibt immer so, 
zudem er bidirektional erfolgt. Da ändert auch ein Getter/Setter nichts 
dran.

von Nop (Gast)


Lesenswert?

Christian J. schrieb:

> Mein "Game of Life" hat auch zwei Felder, wäre aber sinnlos die zu
> kapseln.

Du könntest mehr Performance hinkriegen, wenn Du die kapselst und dann 
nur indirekt über Pointer an die Berechnungsfunktion gibst. Dann kannst 
Du Dir nämlich das Umkopieren von feld.neu nach feld.alt am Anfang eines 
Berechnungszyklus sparen.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Nop schrieb:

> Du könntest mehr Performance hinkriegen, wenn Du die kapselst und dann
> nur indirekt über Pointer an die Berechnungsfunktion gibst.

In dem Fall allerdings eher nicht. 1 Feld Lesen, "umkreisen" und 2.tes 
Feld erzeugen für die Grafik-Funktion, die das Feld abbildet.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Peter D. schrieb:
> Nop schrieb:
>> Sowas solltest Du gar nicht erst haben.
>
> D.h. ich soll keine Steuerungen mehr bauen?
> Die Physiker wollen bei Teilchenquellen und Analysatoren nen Haufen
> Spannungen/Ströme setzen, lesen, überwachen und regeln können. 100
> Parameter ist da eher eine kleine Anzahl.

Er meinte glaube, dass du kein einzelnes struct mit 100+ Members haben 
sollst.
zB kannste den Stromteil in ein struct packen, dass dan in dem großen 
struct wohnt.
Dann kannste dieses "substruct" der Funktion für die Stromeinstellung 
geben anstatt ALLES vom struct zu übergeben.

mbedTLS hat das so, da gibts structs in structs in structs in structs in 
structs usw.
Oben haste das große struct in der Hand für die eine TLS Verbindung, 
dann gibts in diesem ein struct für zB das Zertifikat, dann noch eins 
für das Schküsselpaar und eiens für die Sockets. Das Zertifikatstruct 
hat dann wieder memberstructs für die certchain etc.

Das sieht aber trotzdem gruselig aus:
https://tls.mbed.org/api/structmbedtls__ssl__context.html

von Michael B. (laberkopp)


Lesenswert?

Egon D. schrieb:
> Mal eine Frage: Woher kommt eigentlich der
> ununterdrückbare Drang auf µC.net, einem Fragesteller
> eine deutlich kompliziertere Lösung zu empfehlen, als
> er braucht?
>
> Soweit ich das beurteilen kann, hat sein Problem mit
> Kapselung zu tun. Kapseln kann man aber auch in
> ANSI-C; dazu braucht's kein C++.

Weil irgendwann mal Irgendjemand was von angeblichen Vorzügen bestimmter 
Programmierweisen gelesen (oder in der Vorlesung gehört) hat, sei es 
Wiederverwendbarkeit oder eben Kapselung, und seit dem das ständig für 
Besser unter allen Umständen hält, er betet es wie ein Mantra runter 
ohne jemals selbst nachgedacht zu haben.

Das wirkt halt wie Werbung, Gehirn ausschalten.

Leider leiden unter solchen Leuten diejenigen, die wirklich die 
Hntergründe verstehen wollen und es richtig machen wollen.

Eine globale Variable erfordert nämlich zunächst mal einen Zugriff
LD reg,(address)
ist die Variable auf dem Stack muss der Stackframe addiert werden
LD pointerreg,address
ADD pointerreg,SP
LD reg,(pointerreg)
und hat man es malloct geht der Zugriff per Offset über eine pointer
LD pointerreg,address
ADD pointerreg,fieldoffset
LD reg,(pointerreg)
kommt ein getter/setter auf ein self hinzu muss ein call sein
PUSH self
CALL getter
  POP pointerreg (meist noch komplizierter compiliert)
  ADD pointerreg,fieldoffset
  LD returnreg,(pointerreg)
  RET
LD reg,returnreg ; vielleicht dasselbe
es sei denn der Compiler inlint den Aufruf.

Weil die meisten gar nicht wissen, welchen Overhead ihre 
Fehlentscheidungen so bewirken, sind heute alle Programme, auch bei 1000 
mal leistungsfähigerer Hardware, meistens lahmarschiger als noch vor 30 
Jahren, und natürlich 100 mal grösser.

JEDE Programmierweise, die (zur vollständigen Bearbeitung der Aufgabe 
inkl Fehlerbehandlung) auch nur 1 Befehl mehr auszuführenden code 
bewirkt, ist letztlich ein Programmierfehler.

Aber wenn es um die Worte geht, mit denen diese Programmierer ihre 
Fehler verteidigen, dann haben sie plötzlich tausend mal mehr Worte und 
Elan dafür, als für ein richtiges Programm.

von Vincent H. (vinci)


Lesenswert?

Kommt da auch noch was konstruktives? Weil wenn das Mantra "da beste 
Assembler is kein Assembler" ist, dann kann der Threadersteller seinen 
F429 kübeln und sich an Wetterfrosch zulegen...

von Nop (Gast)


Lesenswert?

Michael B. schrieb:

> JEDE Programmierweise, die (zur vollständigen Bearbeitung der Aufgabe
> inkl Fehlerbehandlung) auch nur 1 Befehl mehr auszuführenden code
> bewirkt, ist letztlich ein Programmierfehler.

Performance ist halt nicht die einzig relevante Metrik. In dem Maße, wie 
die Software komplexer wird, braucht man auch Methoden, diese 
Komplexität zu strukturieren, weil das sonst unwartbar wird.

Insbesondere, wenn sich wegen der normalen Job-Fluktuation auch noch 
jemand außer dem ursprünglichen Entwickler in überschaubarem Zeitrahmen 
in die Codebasis einarbeiten soll. Ebenso, wenn mehrere Entwickler am 
selben Projekt arbeiten, das tun sie dann gegen definierte 
Schnittstellen.

Unabhängig von der Programmiersprache sind Kapselung und Schichten ein 
probates Mittel, weil sie eine bestimmte Aufgabe in einem recht 
abgeschlossenen Bereich erledigen, der nach außen durch seine 
Schnittstellen interagiert. Will man da was ändern, muß man sich erstmal 
eben nur in diesen Bereich reinarbeiten.

von Christian J. (Gast)


Lesenswert?

Michael B. schrieb:
> JEDE Programmierweise, die (zur vollständigen Bearbeitung der Aufgabe
> inkl Fehlerbehandlung) auch nur 1 Befehl mehr auszuführenden code
> bewirkt, ist letztlich ein Programmierfehler.

Das ist nun echt Quark. Heute geht es mehr darum dass der Mensch den 
Code noch versteht und strukturieren kann als dass die Maschine den 
Idealcode zu sehen kriegt.  Kannst ja versuchen eine optische KI in 
Assembler zu programmieren. Die Dinger sind so schnell, dass da ein paar 
Zeilen mehr nicht auffallen. Und Getter und Setter werden vom Compiler 
vollständig aufgelöst, die existieren gar nicht mehr!

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Und Getter und Setter werden vom Compiler
> vollständig aufgelöst, die existieren gar nicht mehr!

Zumindest beim AVR-GCC werden sie das in der Regel nicht. Und dann wird 
rumgerechnet, bis der Arzt kommt, wärend auf die globale Struct bei 
bekanntem Index einfach direkt mit LDS zugegriffen wird.

Ist der Index variabel, kann man dem Compiler deutlich auf die Sprünge 
helfen, indem man schon die Basisadresse berechnet und 
zwischenspeichert.
1
  eeadcval_t* adc_eept = cmd->adc_eept;
2
  adc_eept->offset = meas[1].input - meas[1].val * adc_eept->gain;

Manchmal braucht man sogar mehrstufige Structpointer:
1
  cmd->adc_eept->comp = (float)get_adc_raw(cmd->idx) / get_adc_raw(comp_idx);
Der Getter ist hier notwendig, weil der Parameter-Index noch in den 
physischen ADC-Pin umgerechnet werden muß.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Zumindest beim AVR-GCC werden sie das in der Regel nicht. Und dann wird
> rumgerechnet, bis der Arzt kommt, wärend auf die globale Struct bei
> bekanntem Index einfach direkt mit LDS zugegriffen wird.

Dann mach mal ein konkretes Beispiel, wo das nicht passiert.

von Christian J. (Gast)


Lesenswert?

Peter D. schrieb:
> Ist der Index variabel, kann man dem Compiler deutlich auf die Sprünge
> helfen, indem man schon die Basisadresse berechnet und
> zwischenspeichert.

Was muss man denn da berechnen? Da steht doch bloss das & vor und das 
ist eine Adresse. Der Linker kennt die. Auch dass der GCC das nicht 
auflöst halte ich für ein Gerücht. Was ich gesehen habe bei -Os ist, 
dass der alles inline auflöst, was nur aus einer Zeile besteht oder 
irgendwie zur Kompilerzeit berechenbar ist.

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Dann mach mal ein konkretes Beispiel, wo das nicht passiert.

Ohne LTO? Wenn die in einer anderen Datei liegen. Wenn sie als Teil 
eines Interfaces extern (in einer Bibliothek) sind, sowieso.

Wenn natürlich Getter/Setter mit "static" dekoriert in der gleichen 
Datei wie Datenstruktur und aufrufender Code rumhängen, wird das alles 
aufgelöst.

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:
> Ohne LTO?

Vermute ich auch ...

S. R. schrieb:
> Wenn sie als Teil
> eines Interfaces extern (in einer Bibliothek) sind, sowieso.

Deswegen bevorzuge ich header-only Bibliotheken. Da hat man mit sowas 
gar kein Thema.

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Was muss man denn da berechnen?

Basisadresse + Index * Structgröße.
Danach kann er mit LDD + Displacement auf die einzelnen Elemente 
zugreifen.
Legt man diesen Pointer nicht an, dann macht er die Berechnung für jeden 
Zugriff neu.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Hier mal ein Beispiel aus meinem Scheduler.
Eimal mit und einmal ohne Struct-Pointer.

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.