Typsicherheit

Typsicherheit ist ein Begriff aus der Informatik, speziell aus dem Bereich Programmiersprachen. Er bezeichnet den Zustand (einer Programmausführung), bei dem die Datentypen gemäß ihren Definitionen in der benutzten Programmiersprache verwendet werden und keine Typverletzungen auftreten.

BeschreibungBearbeiten

Typsicherheit herzustellen ist Aufgabe des Compilers beziehungsweise des Interpreters. Als Typprüfung bezeichnet man dabei den Vorgang, die Verwendung von Datentypen innerhalb des Typsystems zu prüfen, um etwaige Typverletzungen festzustellen.

Hierbei müssen beispielsweise bei Zuweisungen die beteiligten Typen nicht notwendig identisch sein, da ganze Zahlen unter Umständen Gleitkommavariablen zugewiesen werden können. Die beteiligten Typen müssen aber zuweisungskompatibel sein.

Werden dementsprechende Typfehler spätestens zur Laufzeit erkannt, spricht man von „typsicheren Programmiersprachen“.

NutzenBearbeiten

Bei der Software-Entwicklung gilt statische Typsicherheit als ein wesentlicher Faktor, der die Code-Qualität und die Zuverlässigkeit des entwickelten Programms erhöht. So weisen typsichere Programme eine hohe Betriebssicherheit und Informationssicherheit auf.[1] Zudem erleichtert statische Typisierung den Arbeitsfluss beim Code-Refactoring. Gelegentlich wird argumentiert, dass die Aspekte der Typsicherheit ebenfalls durch eine ausreichende Testabdeckung nachgewiesen werden können.

Die weit verbreitete Programmiersprache C ist im Gegensatz zur ebenfalls sehr verbreiteten Programmiersprache Java nur begrenzt typsicher. Es wird daher häufig die Ansicht vertreten, dass weniger erfahrene oder unaufmerksame Programmierer in C fehlerhaften oder leicht angreifbaren Programmcode erzeugen können.[2]

Statische und dynamische TypsicherheitBearbeiten

Bei den typisierten Programmiersprachen gibt es solche mit Typprüfungen während der Kompilierung (statisch typisiert) und solche, bei denen strenge Typprüfungen erst zur Laufzeit stattfinden können (dynamisch typisiert), wie zum Beispiel bei Smalltalk. Ersteres Konzept erzeugt schneller ablaufenden Programmcode, der insbesondere für größere Software-Systeme erforderlich ist,[3] letzteres erlaubt effizientere und flexiblere Datenmodellierung, wie zum Beispiel bei der objektorientierten Programmierung. Viele moderne Programmiersprachen wie zum Beispiel Oberon[4] oder C# unterstützen daher beide Konzepte.[5]

BeispieleBearbeiten

C++Bearbeiten

Die folgenden Beispiele veranschaulichen, wie Umwandlungsoperatoren in der Programmiersprache C++ bei falscher Verwendung die Typsicherheit beeinträchtigen können. Das erste Beispiel zeigt, wie grundlegende Datentypen falsch umgewandelt werden können:

#include <iostream>
using namespace std;

int main()
{
    int ival = 5;                                 // integer
    float fval = reinterpret_cast<float&>(ival);  // reinterpret Bitmuster
    cout << fval << endl;                         // Gibt integer als float aus
    return 0;
}

In diesem Beispiel verhindert reinterpret_cast explizit, dass der Compiler eine sichere Konvertierung von einer ganzen Zahl in einen Gleitkommawert durchführt.[6] Wenn das Programm ausgeführt wird, gibt es einen Garbage-Floating-Point-Wert aus. Das Problem hätte vermieden werden können, indem stattdessen float fval = ival geschrieben würde. Das nächste Beispiel zeigt, wie Objektreferenzen falsch umgewandelt werden können:

#include <iostream>
using namespace std;

class Parent
{
public:
    virtual ~Parent() {}  // Virtueller Destruktor für RTTI
};

class Child1 : public Parent
{
public:
    int a;
};

class Child2 : public Parent
{
public:
    float b;
};

int main()
{
    Child1 c1;
    c1.a = 5;
    Parent & p = c1;                        // Der upcast ist immer sicher
    Child2 & c2 = static_cast<Child2&>(p);  // Ungültiger downcast
    cout << c2.b << endl;                   // Gibt Datenmüll aus
    return 0;
}

Die beiden Child-Klassen haben Member unterschiedlicher Typen. Wenn Sie einen Parent-Klassenzeiger auf einen untergeordneten Klassenzeiger übertragen, zeigt der resultierende Zeiger möglicherweise nicht auf ein gültiges Objekt des richtigen Typs. Im Beispiel führt dies dazu, dass der Datenmüll ausgegeben wird. Das Problem hätte vermieden werden können, indem static_cast durch dynamic_cast ersetzt wurde, das bei ungültigen Umwandlungen eine Ausnahme auslöst.[7]

EinzelnachweiseBearbeiten

  1. Hans J. Schneider: Geschichte der Programmiersprachen (Memento des Originals vom 9. Juli 2016 im Internet Archive)  Info: Der Archivlink wurde automatisch eingesetzt und noch nicht geprüft. Bitte prüfe Original- und Archivlink gemäß Anleitung und entferne dann diesen Hinweis.@1@2Vorlage:Webachiv/IABot/www2.cs.fau.de, Friedrich-Alexander-Universität Erlangen-Nurnberg (2012), abgerufen am 9. Juli 2016
  2. Aaron Greenhouse: A Programmer-Oriented Approach to Safe Concurrency, Carnegie Mellon University Pittsburgh (Mai 2003), abgerufen am 9. Juli 2016
  3. Hanspeter Mössenböck: Object-Oriented Programming in Oberon-2, siehe auch Stichwort "Smalltalk", abgerufen am 9. Juli 2016
  4. Hanspeter Mössenböck, Niklaus Wirth: The Programming Language Oberon-2, Institut für Computersysteme, ETH Zürich (Oktober 1993), abgerufen am 9. Juli 2016
  5. Hanspeter Mössenböck: Object-oriented Programming in Oberon (Memento des Originals vom 27. September 2016 im Internet Archive)  Info: Der Archivlink wurde automatisch eingesetzt und noch nicht geprüft. Bitte prüfe Original- und Archivlink gemäß Anleitung und entferne dann diesen Hinweis.@1@2Vorlage:Webachiv/IABot/www.statlab.uni-heidelberg.de, ETH Zürich, Institut für Computersysteme (19. November 1997), abgerufen am 9. Juli 2016
  6. cppreference.com: reinterpret_cast conversion
  7. cppreference.com: dynamic_cast conversion