mikrocontroller.net

Forum: Compiler & IDEs Handhabung globaler Variablen


Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie organisiert ihr eure globalen Variablen ab einer mittleren 
Projektgröße ?

Methode 1) Reservieren des Speicherplatzes in einer C Quelldatei mit 
häufiger Verwendeung derselben Variable. Alle anderen Quelldateien die 
darauf zugreifen möchten haben einen Header mit entsprechenden extern 
Deklarationen inkludiert.

Methode 2) Variablen werden in einer eigenen global.c Quelldatei 
reserviert, welche dann zum Projekt hinzugelinkt wird. Alle Quelldateien 
welche darauf zugreifen, inkludieren dann eine Header Datei mit 
entsprechenden extern Deklarationen.

Methode 3) um eine separate Pflege in verschiedenen Dateien bei Änderung 
einer Variable zu sparen gibt es noch diesen Ansatz:
http://www.imb-jena.de/~gmueller/kurse/c_c++/c_globv.html

Weitere Methoden - Vorteile - Nachteile ?

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich mache das häufig folgendermaßen:

1) so wenig globale Variablen wie möglich

2) steht im Header:
#ifdef main__
  #define EX
#else
  #define EX extern
#endif

3) globale Variablen werden dann in einem Header so deklariert:  EX int 
variable;
Der Header muss dann in main.c auch eingebunden werden.


4) in der main Datei steht dann einmal #define main__

Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die Methode von Klaus ist genau die vom Link. Für initialsierte 
Variablen haben die meisten Compiler Startup einen relativ ausgekocht 
kurzen Code. Das funktioniert dann mit initialisiergen Variablen nicht 
mehr bzw. gibt Warnungen beim Compilieren ?

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hm, gute Frage mit der Initiallisierung...

Wie wärs mit folgendem Hack:
#ifdef main__
  #define EX
  #define INIT =
#else
  #define EX extern
  #define INIT ;//
#endif

Deklaration:  EX int variable INIT 1;

Ob das was für nen Obfuscated C Contest ist? ;)

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
J. V. schrieb:

> Wie organisiert ihr eure globalen Variablen ab einer mittleren
> Projektgröße ?

Bei mir gibt es nur eine globale "Variable", das ist die einzige Instanz 
von Main, welche in Main.h als extern deklariert ist.

Alle anderen globalen Resourcen sind nur ueber Main zugreifbar.

Autor: Globuli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Variante 3 ist fast das schlimmste was ich je gesehen habe!!!
Da bekommt man ja einen Brechreiz beim Lesen.

Ich bevorzuge "Methode 1)". Da weiss man gleich zu welchem Modul die 
globale Variable hauptsächlich gehört und es gibt am wenigsten Probleme 
mit der Lesbarkeit.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bislang konnte ich selbst bei großen Projekten globale Variablen immer 
vermeiden. Ein gutes Konzept was dabei hilft sind eigentlich Handles. 
Man kann dann die Funktionen in seinen C Modulen so auslegen, dass sie 
mit den Informationen in den Handles arbeiten. Die Handles selbst sind 
dann beispielsweise lokale Variablen der Funktion main().

Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ob das was für nen Obfuscated C Contest ist?

Leider nein - es tut nämlich nicht. Der Preprozessor streikt beim //, 
d.h. er ersetzt INIT nur mit einem Strichpunkt und betrachtet die beiden 
Schrägstriche als Kommentar zur #define Zeile. Zeigt auch bereits mein 
Editor in der Einfärbung. Müsste es wohl sowas wie einen Trigraph oder 
eine unsichtbare Klammer oder so was dafür geben. Selbst wenns ginge, 
wäre wahrscheinlich das nächste Problem bei mehrzeiligen 
Initialisierungen von Tabellen. (Vielleicht kehre ich doch zur Methode 2 
zurück ?)

Im Übrigen gibt es bei Mikrokontrollern haufenweise Interrupts und 
neuerdings genausoviel DMA Kanäle wo globale Variablen nützlich sind.
Dementsprechend mögen sich PC Programmierer nicht über die Verwendung 
von global Variablen muckieren...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die Handles selbst sind dann beispielsweise lokale Variablen der
> Funktion main()

Und wo stecken die eigentlichen Daten?

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Methode 3 hat auch den Nachteil, dass eine Initialisierung der Variablen 
nicht möglich ist, weil der Compiler extern und Initialisierung 
gleichzeitig anmeckern sollte.

Der einzige "Vorteil" von Methode 3 ist, dass man die Zeile mit der 
Variable nur einmal schreiben muss. Im Zeitalter von Copy & Paste ist 
dieser Vorteil aber lächerlich.

Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>weil der Compiler extern und Initialisierung gleichzeitig anmeckern sollte.

mit -Wall gibts:
warning: 'TxBuffer' initialized and declared 'extern'

und später kotzt der Linker weil der Compiler das extern anstelle der 
Initialsierung weggeschmissen hat:
.\objects\main.o:(.data+0x0): multiple definition of `TxBuffer'
.\objects\cpuinit.o:(.data+0x0): first defined here

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man denn unbedingt diese "nur-einmal-schreiben"-Lösung haben 
möchte:
#ifdef main__
  #define EX
  #define INIT(x) = (x)
#else
  #define EX extern
  #define INIT(x)
#endif

EX int i INIT(1);

PS: Funktioniert dann aber auch nur für "simple" Initialisierungen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
klaus schrieb:
> Bislang konnte ich selbst bei großen Projekten globale Variablen immer
> vermeiden.

Für Controllerprojekte können sie aber durchaus effektiver sein als
das etagenweise Durchreichen von Zeigern.

Autor: Rene B. (themason) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Für Controllerprojekte können sie aber durchaus effektiver sein als
>das etagenweise Durchreichen von Zeigern.

ich denke auch das globale variablen nicht immer nur schlecht sind. es 
hängt halt hauptsächlich an der disziplin des/der programmierer (ok, 
abgesehen von daten die man sich zerschießen kann durch z.b. einen 
zeiger der über ein array hinausschießt).

methode 1) : so mache ich es auch immer. ist am zweckmäßigsten. ich 
benutze die extern-deklaration auch nur bei daten die wirklich nach 
außen hin freigegeben werden sollen, und alle anderen variablen die nur 
interne verwendung für ein modul finden sind nach außen hin nicht 
zugänglich. (ok da bin ich auch nicht immer so konsequent :-)), aber in 
den meisten fällen gibt es eh nur 2-3 module, inkl. des moduls in dem 
die daten definiert sind, die auf globale daten zugreifen müssen.

methode 2) : die globalen daten in einer einzigen global.c zu 
organsieren halte ich für unzweckmässig, da man gerade wenn man 
bestimmte code-teile heraustrennen möchte immer die global.c anpacken 
muß, bzw. es erschwert die portierbarkeit einzelner module, da man immer 
in der global.c nachschauen muß welche variablen das betreffende modul 
denn verwendet.

methode 3) : hab ich nicht so ganz kapiert ...

ich weiß nicht ob der ansatz schon besprochen wurde. wenn mans ganz 
sauber machen will deklariert man in den entsprechenden modulen 
funktionen die es erlauben die daten des betreffenden moduls auszulesen 
bzw zu beschreiben.

beispiel :

modul.c :

char data [100];

char data_read (char ptr)
{
  return data [ptr]; // evtl. noch plausicheck von ptr
}

void data_write (char ptr, char data)
{
  data [ptr] = data; // evtl. noch plausicheck von ptr und data
}

void data_do_irgendwatt (void)

modul.h :

char data_read (char ptr);
void data_write (char ptr, char data);
void data_do_irgendwatt (void);


ein vorteil hier wäre das man plausi-checks machen kann und im 
fehlerfalle eine meldung ausgeben kann. außerdem verhindert man so das 
man sich durch falsche programmierung andere speicherbereiche 
zerschießt. es kostet aber auch entsprechend code. und je nachdem 
wieviele daten hat (und vor allem wenn man unterschiedliche datentypen 
hat, die ja alle über unterschiedliche funktionen implementiert werden 
müssen) kommt da einiges an funktionen (und somit overhead) zusammen.
aber in mittleren bis großen projekten wo es nicht auf jedes byte 
ankommt ist das denke ich zu vernachlässigen.

ich bin für methode 1 :-)

Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> PS: Funktioniert dann aber auch nur für "simple" Initialisierungen.
immerhin tut:
#define countof(a)   (sizeof(a) / sizeof(*(a)))        
#define TxBufferSize   (countof(TxBuffer) - 1)
EXTERN volatile uint8_t TxBuffer[] INIT("\n\rbla bla bla\n\r");

aber offensichtlich gibts keine wirkliche Patentlösung

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
J. V. schrieb:
>> PS: Funktioniert dann aber auch nur für "simple" Initialisierungen.
> immerhin tut:

Ein einfaches Stringliteral ist eine simple Initialisierungen. Was ich 
mit "nicht simpel" meine, und was mit der Variante garantiert nicht 
funktioniert, sind Initialisierungen der Form {a,b,c} (die Kommas sind 
dann das Problem).

Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> die Kommas sind dann das Problem
Verstanden - Vielleicht gäbs dazu mit Variadic Macros noch einen Hack ?
(Besten Dank auch auf für --save-temp - damit sieht man was man 
anrichtet)

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Und wo stecken die eigentlichen Daten?

Im Block auf der höchsten Ebene. Vom Prinzip her so:

int main()
{
  MODULE_HANDLE handle; // <-- hier

  Module_Init(&handle, "parameter", 42);

  Module_DoSomething(&handle, "kuckuck");
}

//...

void Module_DoSomething(MODULE_HANDLE* arg_Handle, const char* x)
{
  int x = handle->x;
  AnotherModule_DoSomething(&(handle->anotherHandle), x);  
}


> Für Controllerprojekte können sie aber durchaus effektiver sein
> als das etagenweise Durchreichen von Zeigern.

Effizienz und Wartbarkeit steht oft im Widerspruch zueinander. Aber wenn 
die Frage bereits lautet:

> Wie organisiert ihr eure globalen Variablen ab einer
> mittleren Projektgröße ?

dann würde ich eher dazu tendieren die Zahl der globalen Variablen zu 
überdenken anstatt diese "besser zu organisieren".

Autor: J. V. (janvi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>dann würde ich eher dazu tendieren die Zahl der globalen Variablen zu
>überdenken anstatt diese "besser zu organisieren".

Es handelt sich um einen STM32 (Cortex-M3) und damit auch mein erstes 
Projekt auf einem 32 bit Prozessor wo ich bislang immer nur 8 bit 
Assembler geschrieben habe.

Der STM32 hat 64 verschiedene Interrupt Service Funktionen welcher über 
Nestet Interrupt Vector Controller aufgerufen werden plus ein Dutzend 
gleichzeitig möglicher DMA Kanäle welchen man weder einen Zeiger über 
den den Stack zuschieben, noch die angezeigten Inhalte auf Plausibilität 
überprüfen kann. Bei DMA ohne MMU knallts bei Fehlern eben einfach und 
daher sind die globalen Variablen zum Datenaustausch das kleinste Übel.

Autor: Globuli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> methode 3) : hab ich nicht so ganz kapiert ...

Sag ich doch. Die Lesbarkeit ist total bescheiden.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wie organisiert ihr eure globalen Variablen ab einer mittleren
>Projektgröße ?

Abschaffen. In C++ sowieso.

In C die, die es unbedingt braucht, (Kommunikation mit ISR's, etc.) 
möglichst mit Zugriffsfunktionen kapseln. Wenn das nicht geht, 
exportiert das Modul die per header-Datei, zu welchem diese Variable 
gehört.

> methode 3)

ist schlicht albern.

Oliver

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Und wo stecken die eigentlichen Daten?
>
> Im Block auf der höchsten Ebene. Vom Prinzip her so:
>
>
> int main()
> {
>    MODULE_HANDLE handle; // <-- hier

Ach so, dann haben wir an einander vorbeigeredet. Unter "Handle" 
verstand ich jetzt einen Integer, der innerhalb des Moduls dann z.B. als 
Index in ein Array mit den intern nötigen Verwaltungsinformationen 
verwendet wird. Ich wollte wissen, wo dieses Array dann stehen soll.
Du nimmst aber eine Struktur als Handle, wo diese Daten direkt drin 
stehen. Geht natürlich auch, hat aber den Nachteil, daß es auf dem Stack 
liegt, was den RAM-Verbrauch schwerer einschätzbar macht. Der Verbrauch 
durch globale Variablen wird mir nach dem Compilieren direkt angezeigt.

Übrigens sollte man meiner Meinung nach sowas:
  int x = handle->x;

vermeiden und stattdessen das Handle immer nur über Funktionen benutzen. 
Der Benutzer des Moduls sollte sich dann überhaupt nicht dafür 
interessieren müssen, welche Elemente ein Handle hat, so wie das z.B. 
beim FILE* aus C oder beim pthread_t aus den POSIX threads ist.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.