Null Als Input Parameter
 
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern

Wenn in der SpracheCee Objekte, Arrays oder Strings an Funktionen als Inputparameter übergeben werden, dann geschieht dies im Normalfall in Form eines Pointers (Zeigers). Normalerweise werden diese Pointer in der Funktion dereferenziert und es führt zu (allen möglichen) Fehlern wenn diese Pointer nicht auf entsprechende Objekte zeigen oder den Wert NULL haben. Meist führt dies - das ist noch der angenehme Fall - zu einer Zugriffsverletzung mit Programmabbruch.

Um das Ganze an einem Beispiel zu sehen, hier eine simple Implementation der Standard-C-Funktion strcpy:

char *strcpy(char *dest,char *src) {
  char *d=dest;
  while(*src) {
    *d++ = *src++;
  }
  *d='\0';
  return dest;
}

Wenn ein fehlerhafter Pointer oder NULL übergeben wird, dann greift das Programm beim Dereferenzieren mit hoher Wahrscheinlichkeit auf Speicherbereiche zu, auf die ihm der Zugriff verboten ist und es kommt zu einer Zugriffsverletzung.

Die Funktion macht im einzelnen folgende Annahmen über die Inputparameter:

Man könnte nun diese Funktion anders implementieren, so dass die Übergabe eines NULL-Zeigers keinen Schaden anrichten bzw. keinen Programmabbruch verursachen kann:

void kopiere_tolerant(char *dest,char *src) {
  if(dest!=NULL) {
    if(src!=NULL) {
      while(*src) {
        *dest++ = *src++;
      }
    }
    *dest='\0';
  }
}

oder die nicht-NULL-Vorbedingungen durch assert geprüft werden:

void kopiere_assert(char *dest,char *src) {
  assert(dest!=NULL);
  assert(src!=NULL);
  while(*src) {
    *dest++ = *src++;
  }
  *dest='\0';
}

Dabei ist absichtlich die Rückgabe des dest-Parameters weggelassen (er spielt keine Rolle und steht nur deshalb im strcpy-Beispiel, damit das Interface nicht vom Standard abweicht).

Man könnte also auch noch ein "nacktes" strcpy ergänzen:

void StrCpy(char *dest,char *src) {
  while(*src) {
    *dest++ = *src++;
  }
  *dest='\0';
}

Die selbstgestellte Aufgabe ist es nun, alle Vor- und Nachteile der einzelnen Implementationsvarianten (vor allem im Bezug auf mögliche Fehlersituationen) im Detail zu betrachten und zu bewerten. Was soll oder kann das bringen? Allgemeine Erkenntnisse über die Ausgestaltung von Interfaces, denn diese simple Funktion steht natürlich exemplarisch für die Mehrzahl der Funktionen in C, da ja fast jede Funktion Zeiger bzw. Objekt-Parameter in Form von Zeigern übernimmt. -- HelmutLeitner


"Null Als Input Parameter" wird auch in IstAssertSinnvoll/UndWieWeitSollEsGetriebenWerden diskutiert.


Siehe auch AssertionVermeidung --kg


Kurzer Diskussionsbeitrag: Entweder, die Beschreibung (Anforderung) an die jeweilige Methode sieht vor, das ein Input-Paramter auch null sein darf, oder nicht. Im zweiten Fall sollte auch kein Aufwand mit dem null-Test getrieben werden, im ersten Fall ist null eine mögliche Parametrierung.

Ich habe früher mal gedacht, besser wäre ein Programmierstil, der Abstürze möglichst verhindert, also wenn null übergeben wird, besser nichts machen, obwohl ein null-Zeiger an dieser Stelle nicht vorgesehen ist. Aber so verschleppt man Fehler! Sie führen nicht zum Abbruch, werden so nicht bemerkt. Seit ich Java kenne, habe ich dieses Paradigma gelassen. Weil ein falscher null-Zeiger als Exception mit Stack-Trace sehr gut erkennbar ist, und diese Exception im Gesamtkontext gut aufgefangen werden kann. So ist's richtiger, ist meine Erfahrung.

In diesem Zusammenhang halte ich auch nichts von assertion, sondern von guter try-catch-throw-Verwendung. Wir hatten auf Arbeit früher ein System, das ist bei einem Fehler sofort auf Gesamt-Halt gegangen, der Prozess stand. Als Inbetriebsetzer hatte man die Möglichkeit, in diesem eingefrorenem Zustand alle Werte anzuschauen. So war das gedacht. Aber das "H" passierte auch beispielsweise bei einem Underflow-Fehler (wenn ein double-Wert ganz nahe an der 0 lag, kleiner 2 hoch -38, und dann auf float gecastet wurde) oder solche Situationen. Das ganze war in C zu programmieren.

Mit try-catch-throw ist es aber nun möglich, einen Abbruch-fehler in einem Modul zu verkraften. Das Modul wird aborted, aber nicht gleich die ganze Steuerung. Das Modul lässt sich (mit evtl anderen Eingangsdaten) neu starten. Daher meine ich - zurück zum Thema - ist ein Angst-null-Test nicht mehr nötig. HartmutSchorrig.

Leider ist im Java-Stack nicht zu sehen, welcher Parameter denn nun Null war :-(

Weiterer Diskussionsbetrag: Eine Funktion, die nichts tut, wenn ein Null-Zeiger übergeben wird, ist aus meiner Sicht wertlos:

void optionale_funktion( char*p ) {
  if( p ) {
    /* hier erst passiert das, was den Lebenszweck dieser Funktion ausmacht */
  }
}

(Dieser Gedanke ist vielleicht eine Hilfe bei der Entscheidung optional oder nicht für einen als Zeiger übergebenen Parameter; wenn die Funktion zu einem "Nichtstuer" verkommt, ist die Optionalität kontraproduktiv.)

In C++ kann man für obligatorische Parameter Referenzen verwenden. In C könnte man Null-Zeiger bei obligatorischen Parameter übergehen, das Verhalten ist hierbei ja meist hochdefiniert. Die nächstbessere Wahl wäre assert aber das Programm ist hiermit natürlich nicht zu retten (aber man bekommt vieleicht mit, was passiert ist). Erst exception-artige Mittel könnten den Ausfall (Neustart) auf das Subsystem beschränken; andererseits liegt hier mit großer Gewissheit ein Programmfehler vor, ein erforderlicher Parameter darf nicht null sein, wie lange auch immer ein solches System weiterlaufen kann, die Software hat diesen Fehler und muss prinzipiell ausgewechselt werden. WolfPeuker


KategorieC KategorieCee KategorieProgrammierStil
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 29. November 2007 8:40 (diff))
Suchbegriff: gesucht wird
im Titel
im Text