Menu

Sebastian Böthin

Softwareentwicklung

Off-Topic: Das NULL-Dilemma

05.09.2012

Manchmal streiten sich Mathematiker und Programmierer. Zum Beispiel über die Null. Der Programmierer wundert sich, wenn er folgendes SQL-Statement ausführt:

SELECT CASE WHEN null = null THEN 'YES' ELSE 'NO' END

Denn das Ergebnis ist 'NO'. Also ist null nicht gleich null. Und, schlimmer noch,

SELECT CASE WHEN NOT null = null THEN 'YES' ELSE 'NO' END

ergibt ebenfalls 'NO'. Das ist ein ziemlicher Skandal: ein boolescher Ausdruck, der mit einem NOT davor das gleiche Ergebnis liefert! Und null ist also weder gleich null, noch ungleich null.

Erst mit einem schmutzigen Trick kann der Programmierer seine Welt wieder in Ordnung bringen:

SET ANSI_NULLS OFF

Der Mathematiker hingegen kommentiert das gleiche Phänomen mit einem zustimmenden Kopfnicken und erinnert sich sofort an diese Vorlesung im 1. Semester, als der Begriff der Äquivalenzrelation definiert wurde.

Also, wir haben die Menge aller Variablen in einem Programm, die in einem bestimmten Zustand der Maschine mit Werten belegt sind (oder auch nicht, wenn sie NULL sind!). Nun wollen wir diese Variablen in Klassen aufteilen, und zwar so, dass eine Klasse jeweils genau alle Variablen enthält, die mit dem gleichen Wert belegt sind. Dies ergibt den Vergleichsoperator, der dann einfach testet, ob zwei Variablen der gleichen Klasse angehören.

Eine solche Relation hat folgende simple Eigenschaften:

  • Reflexivität: Für alle Variablen a gilt: a = a.
  • Symmetrie: Für alle Variablen a und b gilt: Wenn a = b, dann auch b = a.
  • Transitivität: Für alle Variablen a, b und c gilt: Wenn a = b und b = c, dann auch a = c.

In untypisierten Programmiersprachen ist die Sache ganz klar. Vollkommen trivial ist zum Beispiel in PHP der folgende Code:

if (null == null) {
  // sure!
}

Untypisiert bedeutet ja nicht, dass die Werte keinen Typ haben, sondern, dass der Typ an den Wert und nicht an die Variable gebunden ist. Den Wert null kann man sich daher einfach als mit einem speziellen Null-Typ verknüpft denken, der keinen anderen Wert als null annehmen kann.

In typisierten Sprachen, wie z.B. SQL, sind die Variablen jedoch von vorne herein in Typ-Klassen aufgeteilt, unterliegen also einer gröberen Äquivalenzrelation, bei der je zwei Variablen in Relation zueinander stehen, wenn sie vom gleichen Typ sind. Die Vergleichsrelation muss eine Verfeinerung der Typrelation bilden, denn sonst kommt man ja in die merkwürdige Situation, dass Werte verschiedenen Typs gleich sein können.

DECLARE @x [int];
DECLARE @y [varchar](100);
IF @x = @y
BEGIN  
   SELECT 'Argh! [int] = [varchar]??'
END

In objektorientierten Programmiersprachen wie C++ oder C# kann man ja sogar selbst definieren, wann zwei Objekte gleich sein sollen, etwa indem man den Operator == oder die Funktion Equals für eine Klasse implementiert. Das ändert jedoch nichts am Dilemma mit der Null. In Visual Basic fragt man daher wohlweislich:

If x Is Nothing Then
   ' x is nothing
EndIf

Hier wird anstelle des Vergleichsoperators der typübergreifende Operator Is benutzt. In C# schreibt man hingegen:

if (x == null)
{
   // x is null
}

Das ist eine fragwürdige Syntax, denn folgender Code ist nicht kompilierbar:

string x = null;
System.Uri y = null;
if (x == y)
{
   // Compiler Error:
   // Operator '==' cannot be applied to operands
   // of type 'string' and 'System.Uri'.
}

Somit ist der Operator == in C# nicht wirklich eine Äquivalenzrelation, sondern eher eine Funktion. Man wollte offenbar die Syntax aus der Ursprache C übernehmen und hat das logische Problem gelöst, indem man den Vergleich von Variablen verschiedenen Typs schlichtweg verboten hat. In C war das Konzept allerdings viel einfacher, denn wir lesen in stddef.h:

#define NULL (void*)0

NULL ist also ein untypisierter Zeiger, der auf die Speicheradresse 0 verweist.

int *x = NULL;
char *y = NULL;
if (x == y)
{  
   /* sure! */
}

Da jeder Zeiger implizit in den Typ void* konvertierbar ist und der Vergleich von Zeigern einfach ein numerischer Vergleich der Speicheradressen ist, gibt es hier kein Problem.

Gut, dass wir mal darüber gesprochen haben. Nun können wir weiter programmieren. ;)

Go Back

Kommentar