Markus Pitha: Globale Variablen in C

N'Abend,
ich hätte da mal eine ganz allgemeine Frage zu globalen Variablen in C.
Oft ist es viel leichter und übersichtlicher wenn ich globale Variablen verwende, anstatt die Variablen in jeder Funktion neu zu definieren, da in meinem Fall eigentlich jede Funktion ständig den aktuellen Wert dieser Variablen kennen muss. (Kommunikation der Threads)
Irgendwie habe ich aber oft gehört, dass man globale Variablen nur dann einsetzen sollte, wenn es unbedingt nötig ist. Jetzt bin ich natürlich ein wenig verunsichert. Wie genau soll ich "unbedingt nötig" verstehen? Spricht etwas gegen eine bessere Übersichtlichkeit, die durch den gemäßigten Einsatz von globalen Variablen gewährleistet ist? Lesbarkeit ist doch auch eine Art von Effizienz.
Was soll ich nun davon halten?

Markus.

--
sh:( fo:| ch:? rl:( br:> n4:( ie:{ mo:) va:) de:] zu:) fl:( ss:| ls:] js:|
  1. Hallo Markus,

    Was soll ich nun davon halten?

    Du solltest Variablen so deklarieren, wie Du sie brauchst. Der Tipp, von globalen Variablen Abstand zu halten, ist gut gemeint, weil die Praxis, globale Variablen zu verwenden, leicht ausarten kann - allerdings sind globale Variablen nicht per Definition böse oder so etwas.

    Du solltest Dir bei einer Funktion immer folgende Frage stellen: Sollte die Funktion _unabhängig_ von jedwedem äußeren Faktor agieren (d.h. nur auf die Parameter achten)? Mal ein Beispiel: Du hast eine Funktion, die eine Zeichenkette umdreht (also aus "hallo" macht sie "ollah"). Diese Funktion sollte natürlich unabhängig sein, d.h. sie sollte _keine_ globalen Variablen verwenden, sondern sich nur auf die übergebenen Parameter verlassen. Wenn Du dagegen aber Threads synchronisieren willst (oder etwas ähnliches), dann sind globale Variablen durchaus sinnvoll - Du wirst eine Funktion, die die Thread-Kommunikation realisiert, niemals außerhalb eines bestimmten Kontexts aufrufen _wollen_. Natürlich kannst Du alle benötigten Variablen im Endeffekt auch als Parameter an alle nötigen Funktionen übergeben - allerdings bringt das keinerlei Vorteil und ich habe das auch noch nie gesehen.

    Achja, da Du von Threads sprichst: Threads sollten nie direkt in gemeinsam benutztze [1] Variablen schreiben, ohne vorher irgendwelche Sperrmechanismen zu benutzen (Mutexes, ...) - sonst endet das ziemlich böse.

    Viele Grüße,
    Christian

    [1] Das muss nicht zwangsläufig globale heißen, da man ja auch Zeiger als Funktionsparameter übergeben kann!

    1. Hallo,

      ok danke für den Hinweis. Dann bin ich offensichtlich doch am richtigen Weg :)

      Markus.

      --
      sh:( fo:| ch:? rl:( br:> n4:( ie:{ mo:) va:) de:] zu:) fl:( ss:| ls:] js:|
  2. Hallo Markus

    ich hätte da mal eine ganz allgemeine Frage zu globalen Variablen in C.

    In C?

    Oft ist es viel leichter und übersichtlicher wenn ich globale Variablen verwende, anstatt die Variablen in jeder Funktion neu zu definieren, da in meinem Fall eigentlich jede Funktion ständig den aktuellen Wert dieser Variablen kennen muss. (Kommunikation der Threads)

    Warum ist das übersichtlicher? Weil Du eine Variable bei der Parametereingabe einsparst?

    Erkläre mir bitte, warum Du globale Variablen für "übersichtlich" hältst. Mir geht es genau umgekehrt: globale Variablen sind unübersichtlich. Ich sehe nicht, wo sie deklariert wurde. Ich sehe nicht, wer sie zuletzt manipuliert hat. Ich wundere mich höchstens, dass der Compiler keinen Fehler ausspuckt (dafür der Linker eine Doppeldeklaration anmosert).

    Irgendwie habe ich aber oft gehört, dass man globale Variablen nur dann einsetzen sollte, wenn es unbedingt nötig ist.

    Selbstverständlich gilt der gesunde Grundsatz:
    So viele wie nötig, so wenige wie möglich (idealerweise sind es somit 0).

    Jetzt bin ich natürlich ein wenig verunsichert. Wie genau soll ich "unbedingt nötig" verstehen?

    Unbedingt nötig heißt: Du kannst auf diese globale Variable auf keinen Fall verzichten. Kannst Du es, so ist diese nicht nötig und du solltest auf diese Variable verzichten.l

    Spricht etwas gegen eine bessere Übersichtlichkeit, die durch den gemäßigten Einsatz von globalen Variablen gewährleistet ist? Lesbarkeit ist doch auch eine Art von Effizienz.

    Ok, wenn gemäßigter Einsatz "keine globale Variable" bedeutet, dann kann ich Dir zustimmen :-) Ohne Not verzichte auf globale Variablen.

    Was soll ich nun davon halten?

    Warum glaubst Du auf eine bestimmte globale Variable nicht verzichten zu können? Warum muss diese Variable in allen Modulen zur Verfügung stehen. Warum muss sie überall geändert werden können. Wie überwachst Du die Manipulation des Variableninhalts? Kannst Du Namenskonflikte ausschließen?

    Können Dir vielleicht modullokale Variablen weiterhelfen?

    Freundliche Grüße

    Vinzenz

    1. Hallo,

      Warum ist das übersichtlicher? Weil Du eine Variable bei der Parametereingabe einsparst?

      Nein, weil Threads nur eine Übergabe nach dem Schema void *func(void *arg); zulassen, und ich all die Variablen, die ich mal in func haben will, durch eine Struktur quetschen udn wieder typumwandeln muss, was ganz schon unübersichtlich wird.

      Markus.

      --
      sh:( fo:| ch:? rl:( br:> n4:( ie:{ mo:) va:) de:] zu:) fl:( ss:| ls:] js:|
  3. Hi,

    ich hätte da mal eine ganz allgemeine Frage zu globalen Variablen in C.

    Oh? Keine Lust mehr auf Threads?

    Oft ist es viel leichter und übersichtlicher wenn ich globale Variablen verwende, anstatt die Variablen in jeder Funktion neu zu definieren, da in meinem Fall eigentlich jede Funktion ständig den aktuellen Wert dieser Variablen kennen muss. (Kommunikation der Threads)

    Nein, doch noch nicht die Lust verloren, schön ;-)

    Globale Variablen aufzufädeln ist haarig wie Christian Seiler schon anzudeuten versuchte. Dann gibt es für die verschiedensten Probleme, die in single-threaded Programmen alle mit einer einfachen globalen Variable zufrieden wären in multi-threaded Programmen die verschiedensten Lösungen.
    Und bevor ich jetzt versuche eine Beschreibung von socket-based Multithreaded-Messaging in dieses Textarea zu quetschen und Du nur einen pieseligen Mutex benötigst frage ich lieber: was genau hast Du denn vor?

    Hier noch ein paar Links, die vielleicht nützlich sein könnten:
    Aus dem "Standardwerk" über Unix-C
    http://www.cs.cf.ac.uk/Dave/C/node31.html und folgende, nein eher _alle_ Seiten dort. Ist nicht mehr alles ganz aktuell aber sehr nützlich für den willigen Anfänger.

    Ein nicht ganz sauberes (die Flecken sind aber alle beschriftet) Beispiel zu Semaphoren:
    http://www.cse.psu.edu/~dheller/cse411/programs/semaphore_sample.c

    Einige Grundlagen und theoretische Bemerkungen:
    http://docs.sun.com/app/docs/doc/805-5080

    so short

    Christoph Zurnieden

    1. Hallo,

      Und bevor ich jetzt versuche eine Beschreibung von socket-based Multithreaded-Messaging in dieses Textarea zu quetschen und Du nur einen pieseligen Mutex benötigst frage ich lieber: was genau hast Du denn vor?

      Nun, da ich Grafiken erzeuge, und irgendwie eine "Spritkollision" erzeugen will, tue ich das, indem ich bei einer Grafik A ab einem gewissen y-Wert für eine gewisse Pixelanzahl überprüfe, ob Grafik B Grafik A berührt. Hierzu speichere ich einfach die Werte in einer Variable oder Struktur, lasse jeden Thread die Werte lesen und reagiere eben dann dementsprechend.
      Für bessere Lösungen bin ich gerne offen. Mit der Frage über die globalen Variablen wollte ich mir eigentlich nur ins Gewissen reden lassen, da ich es ja doch nicht unbedingt unprofessionell aussehen lassen will.

      Markus.

      --
      sh:( fo:| ch:? rl:( br:> n4:( ie:{ mo:) va:) de:] zu:) fl:( ss:| ls:] js:|
      1. Hi,

        Nun, da ich Grafiken erzeuge, und irgendwie eine "Spritkollision" erzeugen will, tue ich das, indem ich bei einer Grafik A ab einem gewissen y-Wert für eine gewisse Pixelanzahl überprüfe, ob Grafik B Grafik A berührt. Hierzu speichere ich einfach die Werte in einer Variable oder Struktur, lasse jeden Thread die Werte lesen und reagiere eben dann dementsprechend.

        Aha. So weit, so gut. Google schmeißt auf "SDL sprite collision example" zwar eine Menge aus, aber das hilft Dir für's Verständnis wahrscheinlich nicht weiter.

        Gut. Gehen wir mal davon aus, das zwei Sprites frei im Raume schweben. Ein Sprite ist hierbei eine dirkrete Fläche 0<F_1<R. R sei ein diskreter Raum, hier: eine diskrete Ebene. Wenn sich zwei Sprites berühren, dann gibt es mindestens ein Pärchen, die Nachbarn sind. Meist wird Pixelweise gerechnet, ein Pixel ist nicht viel, da wird eine Kollision besser dadurch definiert, das sich eine Schnittmenge beider Sprites von mindestens einem Element Größe bilden läßt.
        Ganz brutal läßt sich sowas lösen, indem man eine Matrix in der Größe R baut und alle Positionen der Außenmaße aller Sprites dort festhält. Soviel ist das aber meist gar nicht, hört sich nur doll an.
        Da sich die Sprites aber bewegen - und zwar alle! - muß ein ständiges Update der Positionen in der Matrix vorgenommen werden. Damit ein wenig Ordnung reinkommt empfiehlt es sich, das immer nur einer schreibt, während der ganze Rest lesen darf. Es ist also eine Sperre nötig. So eine Sperre kann man z.B. mit einem Mutex (MUTual EXclusion) realisieren.
        Ganz simples Beispiel:

          
        /* globale Variable */  
        int x;  
          
        /* Mutex bezeichnen und gleich initialisieren */  
        pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;  
          
        /*Mutex sperren*/  
        pthread_mutex_lock(&mut);  
          
        /* globale Variable um eins erhöhen */  
        x++;  
          
        /*Mutex wieder entsperren*/  
        pthread_mutex_unlock(&mut);  
        
        

        Das kann jedoch zu Problemen führen. Unter anderem zu solchen, die "Deadlock" genannt werden.

        So, das sollte für heute erstmal reichen ;-)
        Das Gebiet ist aber riesig, da gibt es viel zu entdecken. Du solltest Dir wirklich erstmal die Zeit nehmen und durch die Links wandern. Bei Multithreadeing kannst Du nunmal nicht "mal eben" 'was hacken, das erfordert einfach etwas mehr Sorgfalt.

        so short

        Christoph Zurnieden

        1. Morgen,

          Da sich die Sprites aber bewegen - und zwar alle! - muß ein ständiges Update der Positionen in der Matrix vorgenommen werden. Damit ein wenig Ordnung reinkommt empfiehlt es sich, das immer nur einer schreibt, während der ganze Rest lesen darf. Es ist also eine Sperre nötig. So eine Sperre kann man z.B. mit einem Mutex (MUTual EXclusion) realisieren.

          int x;

          /* Mutex bezeichnen und gleich initialisieren */
          pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

          /*Mutex sperren*/
          pthread_mutex_lock(&mut);

          /* globale Variable um eins erhöhen */
          x++;

          /*Mutex wieder entsperren*/
          pthread_mutex_unlock(&mut);
          [/code]

          Ja, ich mache es bereits so, da ohnehin nicht von 2 Threads gleichzeitig auf den Grafikspeicher geschrieben werden kann. (Die Grafiken verhalten sich sonst ganz seltsam und sind nur sehr langsam)

          Das kann jedoch zu Problemen führen. Unter anderem zu solchen, die "Deadlock" genannt werden.

          Ja, ich habe schon bereits darüber gelesen.

          Markus.

          --
          sh:( fo:| ch:? rl:( br:> n4:( ie:{ mo:) va:) de:] zu:) fl:( ss:| ls:] js:|