Hi! Ich habe folgende Situation: Ich lese auf meinem Mikrocontroller zyklisch mehrere Sensoren ein. Diese müssen dann für mehrere Funktionen zur Verfügung stehen: - Schreiben auf SD Karte - Steuern von LEDs - Regeln eines Motors Bis jetzt habe ich das immer so gemacht, dass die Messwerte in globale Variablen geschrieben wurden, diese können dann von den einzelnen Funktionen abgerufen werden. Ich muss dazu sagen, dass ich absolut kein Profi der Programmierung bin. Ich "habe gehört", dass globale Variablen aufgrund Speicherverbrauch und Sicherheit zu vermeiden sind. Wie löst man das intelligenter? Ist die Übergabe mit Pointern hier richtig? Ich bin mir leider auch nicht sicher, wie man sowas am besten angeht... Grüße und Danke!
Mike Litoris schrieb: > Wie löst man das intelligenter? Ist die Übergabe mit Pointern hier > richtig? Ich bin mir leider auch nicht sicher, wie man sowas am besten > angeht... Ich verwende für sowas Pointer auf Strukturen. Es gibt z.B. ein struct "Messwerte". Das ist im Header der Funktion deklariert (Ausschließlich als type, nicht als struct!). Jede Funktion, die die Messwerte benötigt, benötigt halt dann den header und bekommt einen pointer darauf. Beispiel (LED-Streifen):
1 | //im header: |
2 | typedef struct |
3 | { |
4 | uint8_t ch_red; //channel setting : which is connected to what on the LPC6803: |
5 | //0 = OUT0, 1= OUT1, 2 = OUT2, other = invalid |
6 | uint8_t ch_green; // "" |
7 | uint8_t ch_blue; // "" |
8 | uint16_t NrOfLED; //Nr. Of Pixels in series |
9 | uint8_t stripe_nr; //number of stripe |
10 | bool active; //stripe active / inactive |
11 | rgb col; //color |
12 | rgb data[stripe_max_len]; //pixel data for stripe1 |
13 | }LED_CFG; |
14 | |
15 | //Funktion: |
16 | void set_Stripe(LED_CFG *cfg, uint8_t brightness); |
Das Struct wird einmal im main als Variable deklariert, und nur der Pointer herumgereicht. Im Beispiel hat jeder LED-Streifen ein eigenes Struct vom Type LED_CFG. Structs lassen sich beliebig verschachteln. Man kann sich auf die Art im main() eine Struktur deklarieren, die alle Daten enthält, und in der alle Funktionsblöcke logisch gegliedert sind. z.B. kann man der Struktur "Messung" eine Substruktur "ADC" eingliedern, die eine Substruktur "ADC-settings" enthält. Die Übersicht ist leichter zu behalten, als bei einzelnen Variablen. Den Speicherverbraucht sieht man auch sofort. Wichtig ist: Eine Funktion bekommt ausschließlich die Substruktur, die sie WIRKLICH benötigt! Mehr führt zum Verlust der Übersicht, und zu schlechter Wiederverwendbarkeit des Codes! Ob das so gut ist, weiß ich aber auch nicht, denn ich bin auch nur Autodidakt. Ich bin damit bisher gut gefahren, vor allem bei richtig komplexen Projekten mit hunderten oder tausenden Variablen. Man benötigt keine statics mehr, das ist auch hilfreich.
Ich bin bisher davon ausgegangen, das eine Variable immer ihren Platz benötigt. Egal ob lokal oder global. Soweit mir bekannt ist auch das alignment gleich. Bei der unnötigen Verwendung von Zeigern gilt: Mehr Programmspeicher (Zeiger holen + Daten holen) etwas langsamer, da mindestens zwei Schritte.
Sebastian S. schrieb: > Ich bin bisher davon ausgegangen, das eine Variable immer ihren > Platz > benötigt. Egal ob lokal oder global. > > Soweit mir bekannt ist auch das alignment gleich. > > Bei der unnötigen Verwendung von Zeigern gilt: > Mehr Programmspeicher (Zeiger holen + Daten holen) > etwas langsamer, da mindestens zwei Schritte. Der Speicherbereich einer lokalen Variable außerhalb der Main-Fkt wird immer nach beenden der Fkt immer freigegeben, bei Globalen dagegen nicht.
Sebastian S. schrieb: > Ich bin bisher davon ausgegangen, das eine Variable immer ihren Platz > benötigt. Egal ob lokal oder global. > > Soweit mir bekannt ist auch das alignment gleich. > > Bei der unnötigen Verwendung von Zeigern gilt: > Mehr Programmspeicher (Zeiger holen + Daten holen) > etwas langsamer, da mindestens zwei Schritte. Je nach (unbekanntem) μC ist da kein Unterschied. Und wenn wie beim AVR8 absolute Addressierung vorhanden ist, dann muß die Adresse im Befehl stehen, was (AVR8) 16Bit extra macht. Bei ARM gibt es keine absoluten Adressen, immer nur relativ zu einem Register, also egal ob globale Variablen oder Zeiger auf Strukturen.
@Teddy Das es "Probleme" mit der Lebensdauer und der Sichtbarkeit gibt ist mir bekannt. Ich bezog mich auch nur auf die verwendete Größe.
Mike Litoris schrieb: > Bis jetzt habe ich das immer so gemacht, dass die Messwerte in globale > Variablen geschrieben wurden, diese können dann von den einzelnen > Funktionen abgerufen werden Gute Lösung ! Und ist auch schneller. Früher als der Ram-Speicherplatz knapp war, ok, aber heute...
> Ich verwende für sowas Pointer auf Strukturen. > Das Struct wird einmal im main als Variable deklariert, und nur der > Pointer herumgereicht. Schön und übersichtlich, leider keinen Deut anders als /globale Variablen/ weil immernoch... global und ungeschützt weil jeder beliebige Code komplett in der Struktur rumwühlen kann bis zum umfallen. Du suchst nach eingeschränkte Sichtbarkeit (C) und Kapselung (allgemein). Ein erster, einfacher Ansatz f. C ist die strukturierte Variable ausschliesslich in einer Implementationsdatei zu haben und nur per Funktionen welche via Headerdatei zugänglich gemacht werden bedient werden. Bevor jemand wg. Overhead nölt: richtig geschrieben[TM] ist der nur zur Compiletime, bringt zusätzlichen Schutz und kostet exakt Null zur Laufzeit.
Programmiersprachentheaterintendant schrieb: > Bevor jemand wg. Overhead nölt: richtig geschrieben[TM] ist der nur zur > Compiletime, bringt zusätzlichen Schutz und kostet exakt Null zur > Laufzeit. Aha, danke, ich dachte bisher jedesmal neu anlegen und freigeben würde Zeit benötigen.
:
Bearbeitet durch User
globale Variablen sind böse, wenn - von verschiedenen Stellen wild darauf geschrieben wird - von parallelen Threads davon gelesen wird, ohne Konsistenz zu beachten. - in C viele davon verwendet werden, da es nur einen Namespace gibt. Also dutzende von Variablen wie MyValue1 oder Temp. Wenn eine Variable (z.B. ein Init-Flag oder ein i) lokal sein kann, oder zumindest static in einer Datei, dann sollte man das auch gerne tun. Und als Anfänger hilft es ungemein, globale Variablen zu meiden. Für den Profi sind Konventionen oder const/volatile-Qualifier für globale Variablen wirksamer als eine Pseudokapslung mit Pointer oder Getter/Setter.
Programmiersprachentheaterintendant schrieb: > Schön und übersichtlich, leider keinen Deut anders als globale Variablen > weil immernoch... global und ungeschützt weil jeder beliebige Code > komplett in der Struktur rumwühlen kann bis zum umfallen. Naja, hilft da nicht einfachr 'static' vor der Struktur? Ich finde die Idee mit der Pointeruebergabe eingentlich ganz gut.
Dumdi D. schrieb: > Programmiersprachentheaterintendant schrieb: >> Schön und übersichtlich, leider keinen Deut anders als globale Variablen >> weil immernoch... global und ungeschützt weil jeder beliebige Code >> komplett in der Struktur rumwühlen kann bis zum umfallen. > > Naja, hilft da nicht einfachr 'static' vor der Struktur? Ich finde die > Idee mit der Pointeruebergabe eingentlich ganz gut. Jeder Codeteil bekommt nur den Zeiger auf den Zweig der Struktur, den er benötigt. Der Codeteil KANN andere Teile der Struktur gar nicht bekommen, er bekommt den nötigen Type gar nicht. Sonst wäre mein Konzept ziemlich unsinnig. Ich mache das hirachisch. Nehmen wir an: - Der ADC hat ein ADC_Config struct - Die Messfunktion ein "MESS" struct wenn man Messung() aufruft, bekommt die einen Pointer auf "MESS". Messung ruft init_ADC() auf, das bekommt einen Pointer auf den Zweig "ADC_Config", und nicht mehr. Das C-File, das init_ADC() enthält, kennt "MESS" gar nicht, weil der nötige Header gar nicht inkludiert ist. Warum das sinvoll ist: Will man init_ADC() in einem neuen POrojekt verwenden, will man den Header von "MESS" nicht mitziehen.
Alex D. schrieb: > Programmiersprachentheaterintendant schrieb: >> Bevor jemand wg. Overhead nölt: richtig geschrieben[TM] ist der nur zur >> Compiletime, bringt zusätzlichen Schutz und kostet exakt Null zur >> Laufzeit. > > Aha, danke, ich dachte bisher jedesmal neu anlegen und freigeben würde > Zeit benötigen. [X] Du sollst nicht die Kerzen vom Kristbaum fressen. [ ] Ich hab ausschliesslich von lokalen Variablen geschrieben. [ ] Du hast Kapselung verstanden.
PfuschenderAutodidakt schrieb: > Der Codeteil KANN andere Teile der Struktur gar nicht bekommen, er > bekommt den nötigen Type gar nicht. Ich finde Deine Idee prima. Theoretisch kann jedoch auf eine globale Variable ohne static natuerlich zugegriffen werden, aber das ist nur relevant falls die Programme in einem Team entwickelt werden.
Dumdi D. schrieb: > PfuschenderAutodidakt schrieb: >> Der Codeteil KANN andere Teile der Struktur gar nicht bekommen, er >> bekommt den nötigen Type gar nicht. > > Ich finde Deine Idee prima. Theoretisch kann jedoch auf eine globale > Variable ohne static natuerlich zugegriffen werden, aber das ist nur > relevant falls die Programme in einem Team entwickelt werden. Man ist nicht gezwungen, die Struktur global anzulegen. Man kann sie einfach im main "lokal" deklarieren. Oder woanders. Nur muss man sicherstellen, dass der Speicher reserviert bleibt - also entweder als static, oder im main loop (das man ja nie verlässt). Oder sogar nur mit malloc(sizeof(type)); Eines sollte klar sein: Im Prinzip ist durch die Deklarierung als "typedef" nur eine Maske definiert, die über einen Speicherbereich gelegt werden kann. Konkret Speicher belgt man erst dann, wenn man eine Variable des Typs anlegt.
PfuschenderAutodidakt schrieb: > Man ist nicht gezwungen, die Struktur global anzulegen. Spätestens wenn ein Interrupt auf die Daten zugreifen muß, wirst Du wohl nicht darum herum kommen, die Strutur oder Teile davon global anzulegen.
Mike Litoris schrieb: > Bis jetzt habe ich das immer so gemacht, dass die Messwerte in globale > Variablen geschrieben wurden, diese können dann von den einzelnen > Funktionen abgerufen werden. Das ist auch sauber so. Die Variablen werden von einer einzigen Stelle aus geschrieben und können von überall her gelesen werden. Single producer, multiple consumer. Die Hauptbaustelle ist hier natürlich atomic access, das mußt Du schon sicherstellen.
Sheeva P. schrieb: > PfuschenderAutodidakt schrieb: >> Man ist nicht gezwungen, die Struktur global anzulegen. > > Spätestens wenn ein Interrupt auf die Daten zugreifen muß, wirst Du wohl > nicht darum herum kommen, die Strutur oder Teile davon global anzulegen. Naja, man kann dem Interrupt einen Pointer setzen. Selbstverständlich nur einen auf die Substruktur, die benötigt wird. Der Pointer muss global sein, das stimmt, aber man braucht ihn nicht projektglobal überall als "extern ..." durchzuschleifen. In solchen Fällen habe ich der Initfunktion dann den Pointer übergeben, diese setzt ihn dann auf den übergebenen Wert. Man kommt aber erstaunlich oft ohne Daten in einer ISR aus. Hier ist es wichtig, nur die "lokale" Substruktur zu verwenden. Z.B. das Strukturelement "ADC" in ADC.c welches in ADC.h deklariert ist, und nicht mehr. Würde man die Struktur verwenden, die die substruktur ADC enthältlt, müsste man der Funtkion alle verwendeten Typen bekanntgeben. Das macht Code unübersichtlich.
Mike Litoris schrieb: > Ich "habe gehört", dass globale Variablen aufgrund Speicherverbrauch und > Sicherheit zu vermeiden sind. Viele Leute haben viele Meinungen und geben oft genug Dünnschiss von sich. Es gibt keine globale Regel an die man sich halten sollte oder muss. Für alles gibt es Gründe dafür oder dagegen. Und wenn du hier im Forum mitliest, wirst du oft genug merken wie die Weltanschauungen aufeinanderprasseln. C gegen C++ oder asm, Tasteneinlesen per GPIO Interrupt oder Polling, Debugger oder printf, goto u.s.w. Am besten du schaust dir Codeschnipsel im Netz an und bildest dir deine eigene Meinung ob eine bestimmte Aufgabenstellung für dich elegant oder nicht gelöst wurde. Am wenigsten solltest du Leuten und ihrer Meinung vertrauen die für alles festgeklopfte Meinungen haben und darauf wie die Moralapostel herumreiten. Meistens schreien die leider am lautesten...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.