Solars C++ Kurs
Programmieren leicht erlernt
Funktionen

Globale Variablen

Globale Variablen sind innerhalb einer gesamten CPP-Datei gültig, und nicht nur in einer einzigen Funktion.
Da Globale Variablen in der gesamten Datei gelten und der Gültigkeitsbereich mit Hilfe von Headerdateien sogar auf mehrere CPP-Dateien erweitert werden kann, sollte man diese wenn möglich garnicht oder nur mit größter Vorsicht benutzen.

Folgende Gründe kann es für die Benutzung von globalen Variablen geben:
Bisher haben wir die Fehlermeldungen in scppkMath.cpp ausgegeben. In einer CPP-Datei die auch in anderen Programmen verwendbar sein soll, ist es jedoch nicht gut, Texte auszugeben. Die Funktionen könnten z.B. später in einer Windows-Umgebung benutzt werden, wo cout nicht funktioniert.

Deshalb nehmen wir jetzt alle Textausgaben aus scppkMath heraus und schreiben die Fehlermeldung sowohl als String als auch als Fehlernummer in globale Variablen:

scppkMath.cpp
#include <iostream.h>
#include <limits.h>
#include <float.h>
#include <string.h>
#include "scppkMath.h"



char szScppkMathError[128] = "";
int nScppkMathError = 0;



void ScppkMinus( int nZahl1, int nZahl2 )
{
  double dErgebnis;

  dErgebnis = (double) nZahl1 - (double) nZahl2;
  if ( nScppkIntegertest( dErgebnis ) )
  {
    cout << nZahl1 - nZahl2;
  }
}



int nScppkIntegertest( double dZahl )
{
  int nReturn = 0;
  if ( _finite( dZahl ) )
  {
    if ( dZahl > INT_MAX )
    {
      strcpy( szScppkMathError, SCPPK_STR_ERROR_ZUGROSS );
      nScppkMathError = 1;
    }
    else if ( dZahl < INT_MIN )
    {
      strcpy( szScppkMathError, SCPPK_STR_ERROR_ZUKLEIN );
      nScppkMathError = 2;
    }
    else
    {
      nReturn = 1;
      nScppkMathError = 0;
    }
  }
  else
  {
    strcpy( szScppkMathError, SCPPK_STR_ERROR_UNDEFINIERT );
    nScppkMathError = 3;
  }
  return nReturn;
}



void ScppkPlus( int nZahl1, int nZahl2 )
{
  double dErgebnis;

  dErgebnis = (double) nZahl1 + (double) nZahl2;
  if ( nScppkIntegertest( dErgebnis ) )
  {
    cout << nZahl1 + nZahl2;
  }
}


Die globalen Variablen sind jetzt innerhalb von scppkMath.cpp gültig und es ist möglich das Programm so zu kompilieren und nutzen. (Allerdings gibt es jetzt keine Fehlermeldungen mehr heraus, da die Fehlermeldungen zwar im Speicher stehen, aber nicht mit cout auf den Bildschirm gebracht werden.)

Die Fehlermeldungen sollen jetzt in main.cpp ausgegeben werden. Die beiden globalen Variablen szScppkMathError und nScppkMathError sind jedoch nur in scppkMath.cpp gültig. Da main.cpp sie noch nicht kennt, müssen wir sie jetzt bekannt machen. Das geschieht in scppkMath.h welche ja bereits in main.cpp eingebunden ist.

Da die beiden Variablen bereits in der CPP-Datei deklariert wurden, darf man sie in der Header-Datei nicht nocheinmal deklarieren. Sie (die Variablen) sollen bekannt gemacht werden. Dies geschieht, in dem wir dem Compiler sagen, die Variablen existieren 'irgendwo'. Dieses 'Irgendwo' kann sowohl weiter unten in der selben Datei als auch in einer ganz anderen zum Projekt gehörenden Datei sein.

Die Variablen werden bekannt gemacht, in dem sie mit dem Schlüsselwort extern deklariert werden:

scppkMath.h
#ifndef __scppkMath_h__
#define __scppkMath_h__

///
/// Texte
///

#define SCPPK_STR_ERROR_ZUGROSS \
"Error 1 (Das Ergebnis ist zu gross)\n"

#define SCPPK_STR_ERROR_ZUKLEIN \
"Error 2 (Das Ergebnis ist zu klein)\n"

#define SCPPK_STR_ERROR_UNDEFINIERT \
"Error 3 (Das Ergebnis ist \
nicht definiert)\n"

///
/// Variablen
///

extern char szScppkMathError[128];
extern int nScppkMathError;

///
/// Funktionen
///

void ScppkMinus( int nZahl1, int nZahl2 );
void ScppkPlus( int nZahl1, int nZahl2 );

///
/// Intern genutzte Funktionen
///

int nScppkIntegertest( double dZahl );

#endif


Nachdem die beiden globalen Variablen auch der Hauptfunktion bekannt sind, kann man sich dort um die Ausgabe der Fehlermeldungen kümmern:

main.cpp
#include <iostream.h>
#include <stdlib.h>
#include "main.h"
#include "scppkMath.h"



int main( int argc, char * argv[], char * envp[] )
{
  int nZahl1;
  int nZahl2;

  if ( argc != 3 )
  {
    cout << SCPPK_STR_HILFE;
  }
  else
  {
    nZahl1 = atoi( argv[ 1 ] );
    nZahl2 = atoi( argv[ 2 ] );
    cout << "\n" << nZahl1 << " + " << nZahl2 << " = ";
    ScppkPlus( nZahl1, nZahl2 );
    if ( nScppkMathError )
    {
      cout << szScppkMathError;
    }
    cout << "\n" << nZahl1 << " - " << nZahl2 << " = ";
    ScppkMinus( nZahl1, nZahl2 );
    if ( nScppkMathError )
    {
      cout << szScppkMathError;
    }
  }
  return 0;
}


Jetzt kann das Programm wieder kompiliert werden. (Die Datei main.h ist genau die selbe, wie bei 'Mehrere CPP-Dateien'. Falls sie hier in den Kurs eingestiegen sind, schreiben sie die Headerdatei einfach von dort ab.) Die Fehlermeldungen wurden leicht angepaßt, damit die Fehlernummer mit ausgegeben wird. Ansonsten ist die Funktionsweise des Programms gleich geblieben.

Die Rechenergebnisse werden jedoch immer noch von scppkMath.cpp ausgegeben. Dies muß natürlich auch noch geändert werden.
Am besten ist es, wenn die Rechenfunktionen das Ergebnis als Rückgabewert ausgeben. Ob dieser Rückgabewert gültig ist, kann ja in der Variablen nScppkMathError nachgesehen werden. Und da gegebenenfalls aufgetretene Fehler von der Funktion nSccpkIntegertest bereits dort gespeichert werden, braucht diese keinen Rückgabewert mehr:

scppkMath.cpp
#include <iostream.h>
#include <limits.h>
#include <float.h>
#include <string.h>
#include "scppkMath.h"



char szScppkMathError[128] = "";
int nScppkMathError = 0;



int nScppkMinus( int nZahl1, int nZahl2 )
{
  double dErgebnis;
  int nErgebnis;

  dErgebnis = (double) nZahl1 - (double) nZahl2;
  ScppkIntegertest( dErgebnis );
  nErgebnis = nZahl1 - nZahl2;
  return nErgebnis;
}



void ScppkIntegertest( double dZahl )
{
  if ( _finite( dZahl ) )
  {
    if ( dZahl > INT_MAX )
    {
      strcpy( szScppkMathError, SCPPK_STR_ERROR_ZUGROSS );
      nScppkMathError = 1;
    }
    else if ( dZahl < INT_MIN )
    {
      strcpy( szScppkMathError, SCPPK_STR_ERROR_ZUKLEIN );
      nScppkMathError = 2;
    }
    else
    {
      // ... //
      nScppkMathError = 0;
    }
  }
  else
  {
    strcpy( szScppkMathError, SCPPK_STR_ERROR_UNDEFINIERT );
    nScppkMathError = 3;
  }
  // ... //
}



int nScppkPlus( int nZahl1, int nZahl2 )
{
  double dErgebnis;
  int nErgebnis;

  dErgebnis = (double) nZahl1 + (double) nZahl2;
  ScppkIntegertest( dErgebnis );
  nErgebnis = nZahl1 + nZahl2;
  return nErgebnis;
}


Da sich die Funktions-Namen geändert haben, muß die Headerdatei angepasst werden. (Man sollte am Namen der Funktion schließlich den Variablentyp des Rückgabewertes erkennen können.)

scppkMath.h
.
.
.

///
/// Funktionen
///

int nScppkMinus( int nZahl1, int nZahl2 );
int nScppkPlus( int nZahl1, int nZahl2 );

///
/// Intern genutzte Funktionen
///

void ScppkIntegertest( double dZahl );

#endif


Leider müssen die Funktions-Namen dann auch in main.cpp angepasst werden. (Wäre ja schön wenn das automatisch ginge....)
Da dies bei großen Programmen nicht mehr so einfach möglich wäre, sollte man bereits beim Anlegen einer Funktion genau überlegen, was für einen Datentyp sie zurückgeben soll. Wenn der Datentyp des Rückgabewertes später doch einmal geändert werden sollte, muß man sich genau überlegen, ob man den Funktionsnamen noch ändert, oder ob man auf eine Änderung des Funktions-Namen verzichtet.

main.cpp
#include <iostream.h>
#include <stdlib.h>
#include "main.h"
#include "scppkMath.h"



int main( int argc, char * argv[], char * envp[] )
{
  int nZahl1;
  int nZahl2;
  int nErgebnis;

  if ( argc != 3 )
  {
    cout << SCPPK_STR_HILFE;
  }
  else
  {
    nZahl1 = atoi( argv[ 1 ] );
    nZahl2 = atoi( argv[ 2 ] );
    cout << "\n" << nZahl1 << " + " << nZahl2 << " = ";
    nErgebnis = nScppkPlus( nZahl1, nZahl2 );
    if ( nScppkMathError )
    {
      cout << szScppkMathError;
    }
    else
    {
      cout << nErgebnis;
    }
    cout << "\n" << nZahl1 << " - " << nZahl2 << " = ";
    nErgebnis = nScppkMinus( nZahl1, nZahl2 );
    if ( nScppkMathError )
    {
      cout << szScppkMathError;
    }
    else 
    {
      cout << nErgebnis;
    }
  }
  return 0;
}


Jetzt sind alle Benutzerschnittstellen aus scppkMath.cpp heraus genommen worden. Dadurch ist diese Datei jetzt noch universeller einsetzbar.

Hier war der Gebrauch von globalen Variablen durchaus sinnvoll. Man sollte aber immer darauf achten, globale Variablen nur dort einzusetzen, wo es wirklich notwendig ist. Je mehr Variablen nur lokal verwendet werden, desto leichter ist das Programm später zu warten.