Was ist der Unterschied zwischen einer stark typisierten Sprache und einer statisch typisierten Sprache?


Antworten:


539

Was ist der Unterschied zwischen einer stark typisierten Sprache und einer statisch typisierten Sprache?

Eine statisch typisierte Sprache verfügt über ein Typsystem, das zur Kompilierungszeit von der Implementierung (einem Compiler oder Interpreter) überprüft wird. Die Typprüfung lehnt einige Programme ab, und Programme, die die Prüfung bestehen, werden normalerweise mit einigen Garantien geliefert. Der Compiler garantiert beispielsweise, dass für Gleitkommazahlen keine ganzzahligen arithmetischen Anweisungen verwendet werden.

Es gibt keine wirkliche Übereinstimmung darüber, was "stark typisiert" bedeutet, obwohl die in der Fachliteratur am häufigsten verwendete Definition lautet, dass es dem Programmierer in einer "stark typisierten" Sprache nicht möglich ist, die durch das Typensystem auferlegten Einschränkungen zu umgehen . Dieser Begriff wird fast immer verwendet, um statisch typisierte Sprachen zu beschreiben.

Statisch gegen dynamisch

Das Gegenteil von statisch typisiert ist "dynamisch typisiert", was bedeutet, dass

  1. Zur Laufzeit verwendete Werte werden in Typen eingeteilt.
  2. Es gibt Einschränkungen, wie solche Werte verwendet werden können.
  3. Wenn diese Einschränkungen verletzt werden, wird die Verletzung als (dynamischer) Typfehler gemeldet.

Beispielsweise hat Lua , eine dynamisch typisierte Sprache, unter anderem einen Zeichenfolgentyp, einen Zahlentyp und einen Booleschen Typ. In Lua gehört jeder Wert zu genau einem Typ, dies ist jedoch nicht für alle dynamisch typisierten Sprachen erforderlich. In Lua ist es zulässig, zwei Zeichenfolgen zu verketten, es ist jedoch nicht zulässig, eine Zeichenfolge und einen Booleschen Wert zu verketten.

Stark gegen schwach

Das Gegenteil von "stark typisiert" ist "schwach typisiert", was bedeutet, dass Sie das Typensystem umgehen können. C ist notorisch schwach typisiert, da jeder Zeigertyp einfach durch Umwandeln in einen anderen Zeigertyp konvertierbar ist. Pascal sollte stark typisiert sein, aber ein Versehen im Design (nicht getaggte Variantenaufzeichnungen) führte eine Lücke in das Typensystem ein, so dass es technisch schwach typisiert ist. Beispiele für wirklich stark typisierte Sprachen sind CLU, Standard ML und Haskell. Standard ML wurde in der Tat mehrfach überarbeitet, um Lücken im Typensystem zu beseitigen, die nach der weit verbreiteten Verwendung der Sprache entdeckt wurden.

Was ist hier wirklich los?

Insgesamt stellt sich heraus, dass es nicht so nützlich ist, über "stark" und "schwach" zu sprechen. Ob ein Typsystem eine Lücke aufweist, ist weniger wichtig als die genaue Anzahl und Art der Lücken, wie wahrscheinlich es ist, dass sie in der Praxis auftreten, und welche Konsequenzen hat die Ausnutzung einer Lücke. In der Praxis ist es am besten, die Begriffe "stark" und "schwach" insgesamt zu vermeiden , weil

  • Amateure verbinden sie oft mit "statisch" und "dynamisch".

  • Anscheinend wird "schwache Typisierung" von einigen Personen verwendet, um über die relative Prävalenz oder das Fehlen impliziter Konvertierungen zu sprechen.

  • Fachleute können sich nicht genau darauf einigen, was die Begriffe bedeuten.

  • Insgesamt ist es unwahrscheinlich, dass Sie Ihr Publikum informieren oder aufklären.

Die traurige Wahrheit ist, dass bei Typsystemen "stark" und "schwach" keine allgemein anerkannte technische Bedeutung haben. Wenn Sie die relative Stärke von Typsystemen diskutieren möchten, ist es besser, genau zu diskutieren, welche Garantien gegeben sind und welche nicht. Eine gute Frage lautet beispielsweise: "Wird garantiert, dass jeder Wert eines bestimmten Typs (oder einer bestimmten Klasse) durch Aufrufen eines Konstruktors dieses Typs erstellt wurde?" In C lautet die Antwort nein. In CLU, F # und Haskell ist es ja. Für C ++ bin ich mir nicht sicher - ich würde es gerne wissen.

Im Gegensatz dazu bedeutet statische Typisierung , dass Programme vor der Ausführung überprüft werden und ein Programm möglicherweise vor dem Start abgelehnt wird. Dynamische Typisierung bedeutet , dass die Art der Werte werden überprüft während der Ausführung, und ein schlecht getippt Betrieb könnte das Programm zu stoppen verursachen oder sonst ein Fehlersignal zur Laufzeit. Ein Hauptgrund für die statische Typisierung besteht darin, Programme auszuschließen, die solche "dynamischen Typfehler" aufweisen könnten.

Bedeutet eins das andere?

Auf pedantischer Ebene, nein, denn das Wort "stark" bedeutet eigentlich nichts. In der Praxis machen Menschen fast immer eines von zwei Dingen:

  • Sie verwenden (fälschlicherweise) "stark" und "schwach", um "statisch" und "dynamisch" zu bedeuten. In diesem Fall verwenden sie (fälschlicherweise) austauschbar "stark typisiert" und "statisch typisiert".

  • Sie verwenden "stark" und "schwach", um die Eigenschaften statischer Systeme zu vergleichen. Es ist sehr selten, dass jemand über ein "starkes" oder "schwaches" dynamisches Typsystem spricht. Mit Ausnahme von FORTH, das eigentlich kein Typensystem hat, kann ich mir keine dynamisch typisierte Sprache vorstellen, in der das Typsystem untergraben werden kann. Per Definition werden diese Überprüfungen in die Ausführungs-Engine übernommen, und jede Operation wird vor der Ausführung auf ihre Richtigkeit überprüft.

Wenn eine Person eine Sprache als "stark typisiert" bezeichnet, spricht diese Person sehr wahrscheinlich über eine statisch typisierte Sprache.


3
@Adam: Offensichtlich nicht korrekt genug, um positiv bewertet zu werden :) Da Cletus 'Antwort so viele Missverständnisse enthält (obwohl ich das Schlimmste herausgearbeitet habe), fühlte ich mich gezwungen, alles in Worten einer Silbe zu formulieren ...
Norman Ramsey

1
Nun, ich habe dich positiv bewertet :) Selbst das Wort "Kompilieren" ist bei heutigen VMs, auf denen dynamische Sprachen ausgeführt werden, nicht eindeutig. Technisch gesehen werden Java und C # beide zweimal kompiliert (JIT) und beide führen eine Typanalyse durch. Eine Sprache wie Javascript, die in .NET VM ausgeführt wird, ist aufgrund der VM möglicherweise typsicherer.
Adam Gent

2
Ich bin jetzt so verwirrt! Okay, liebe große Gladiatoren der Arena, kann eine arme Seele wie ich mit dem folgenden einfachen Verständnis gehen? 1.Static: Werte werden dem Typ während der Kompilierungszeit und nicht der Laufzeit zugeordnet.2.Dynamic: Werte werden dem Typ zur Laufzeit zugeordnet, daher kann sich der Typ des Werts zur Laufzeit ändern, sodass er anfälliger für Probleme im Zusammenhang mit Typumwandlungen ist zur Laufzeit. 3. Stark / schwach: Vergiss es! Dies sind keine Fachbegriffe und nur eine schlechte Nomenklatur. Es kommt darauf an, über welchen Kontext man spricht. Kann ich mit diesem einfachen Verständnis mein Leben fortsetzen? :(
Saurabh Patil

"Wird garantiert, dass jeder Wert eines bestimmten Typs (oder einer bestimmten Klasse) durch Aufrufen eines Konstruktors dieses Typs erstellt wurde?" In C lautet die Antwort nein. Könnte jemand eine Beispielsituation angeben, in der dies in C auftritt? Ich nehme an, es geht darum, Zeiger auf Strukturen zu werfen.
Corazza

Starke und schwache Typisierung: keine solche Klassifizierung.
Raúl

247

Dies wird oft missverstanden, also lassen Sie es mich klären.

Statische / dynamische Typisierung

Bei der statischen Typisierung ist der Typ an die Variable gebunden . Typen werden zur Kompilierungszeit überprüft.

Bei der dynamischen Typisierung ist der Typ an den Wert gebunden . Typen werden zur Laufzeit überprüft.

Also in Java zum Beispiel:

String s = "abcd";

swird "für immer" ein String. Während seines Lebens kann es auf verschiedene Strings verweisen (da dies seine Referenz in Java ist). Es kann einen nullWert haben, aber es wird sich niemals auf ein Integeroder ein beziehen List. Das ist statische Eingabe.

In PHP:

$s = "abcd";          // $s is a string
$s = 123;             // $s is now an integer
$s = array(1, 2, 3);  // $s is now an array
$s = new DOMDocument; // $s is an instance of the DOMDocument class

Das ist dynamisches Tippen.

Starke / schwache Eingabe

(Alarm bearbeiten!)

Starkes Tippen ist eine Phrase ohne allgemein vereinbarte Bedeutung. Die meisten Programmierer, die diesen Begriff für etwas anderes als statische Typisierung verwenden, implizieren damit, dass es eine Typendisziplin gibt, die vom Compiler erzwungen wird. Beispielsweise verfügt die CLU über ein starkes Typsystem, das es dem Clientcode nicht ermöglicht, einen Wert vom abstrakten Typ zu erstellen, außer unter Verwendung der vom Typ bereitgestellten Konstruktoren. C hat ein etwas starkes Typsystem, kann jedoch bis zu einem gewissen Grad "unterwandert" werden, da ein Programm immer einen Wert eines Zeigertyps in einen Wert eines anderen Zeigertyps umwandeln kann. So können Sie beispielsweise in C einen von zurückgegebenen Wert nehmen malloc()und ihn fröhlich umwandeln FILE*, und der Compiler wird nicht versuchen, Sie aufzuhalten - oder Sie sogar warnen, dass Sie etwas zweifelhaftes tun.

(Die ursprüngliche Antwort sagte etwas über einen Wert "Typ zur Laufzeit nicht ändern" aus. Ich kannte viele Sprachdesigner und Compiler-Autoren und kannte keinen, der über Werte sprach, die den Typ zur Laufzeit ändern, außer möglicherweise einige sehr fortgeschrittene Untersuchungen zum Typ Systeme, bei denen dies als "starkes Update-Problem" bekannt ist.)

Eine schwache Typisierung impliziert, dass der Compiler keine Typisierungsdisziplin erzwingt oder dass die Durchsetzung leicht untergraben werden kann.

Das Original dieser Antwort verband schwache Typisierung mit impliziter Konvertierung (manchmal auch als "implizite Werbung" bezeichnet). Zum Beispiel in Java:

String s = "abc" + 123; // "abc123";

Dieser Code ist ein Beispiel für eine implizite Heraufstufung: 123 wird implizit in eine Zeichenfolge konvertiert, bevor sie mit verkettet wird "abc". Es kann argumentiert werden, dass der Java-Compiler diesen Code wie folgt umschreibt:

String s = "abc" + new Integer(123).toString();

Stellen Sie sich ein klassisches PHP-Problem vor:

if (strpos('abcdef', 'abc') == false) {
  // not found
}

Der Fehler hier ist, dass strpos()der Index der Übereinstimmung zurückgegeben wird, wobei 0 ist. 0 wird in einen Booleschen Wert gezwungen falseund somit ist die Bedingung tatsächlich wahr. Die Lösung besteht darin , implizite Konvertierungen zu vermeiden, ===anstatt sie ==zu vermeiden.

Dieses Beispiel zeigt, wie eine Kombination aus impliziter Konvertierung und dynamischer Typisierung Programmierer in die Irre führen kann.

Vergleichen Sie das mit Ruby:

val = "abc" + 123

Dies ist ein Laufzeitfehler, da in Ruby das Objekt 123 nicht implizit konvertiert wird, nur weil es zufällig an eine +Methode übergeben wird. In Ruby muss der Programmierer die Konvertierung explizit machen:

val = "abc" + 123.to_s

Der Vergleich von PHP und Ruby ist hier ein gutes Beispiel. Beide Sprachen sind dynamisch typisiert, aber PHP hat viele implizite Konvertierungen und Ruby (vielleicht überraschend, wenn Sie damit nicht vertraut sind) nicht.

Statisch / Dynamisch vs Stark / Schwach

Der Punkt hier ist, dass die statische / dynamische Achse unabhängig von der starken / schwachen Achse ist. Die Leute verwirren sie wahrscheinlich teilweise, weil starke und schwache Typisierung nicht nur weniger klar definiert sind, sondern es auch keinen wirklichen Konsens darüber gibt, was genau unter stark und schwach zu verstehen ist. Aus diesem Grund ist eine starke / schwache Typisierung eher ein Grauton als ein Schwarz- oder Weißton.

Um Ihre Frage zu beantworten: Eine andere Möglichkeit, dies zu betrachten, die größtenteils richtig ist, besteht darin, zu sagen, dass statische Typisierung die Sicherheit vom Typ zur Kompilierungszeit und starke Typisierung die Sicherheit vom Typ Laufzeit ist.

Der Grund dafür ist, dass Variablen in einer statisch typisierten Sprache einen Typ haben, der deklariert werden muss und beim Kompilieren überprüft werden kann. Eine stark typisierte Sprache hat Werte, die zur Laufzeit einen Typ haben, und es ist für den Programmierer schwierig, das Typsystem ohne eine dynamische Überprüfung zu untergraben.

Es ist jedoch wichtig zu verstehen, dass eine Sprache statisch / stark, statisch / schwach, dynamisch / stark oder dynamisch / schwach sein kann.


Anstatt zu sagen, dass $ s eine Ganzzahl oder eine Zeichenfolge ist, wäre es besser gewesen, wenn Sie sagen würden, dass der Typ dem "abcd" oder 1234 nicht der Variablen $ s zugeordnet ist.
Srinivas Reddy Thatiparthy

Hervorragende Antwort mit klaren Beispielen. Ich denke jedoch, dass es die Verwirrung darüber, warum Menschen nach stark / statisch als Doppelpaar von Konzepten fragen, nicht vollständig angeht. Zum Beispiel der OP-Wortlaut "impliziert statische Typisierung eine starke Typisierung?" Ihre Antwort betont ihre Unabhängigkeit. Um die Klarstellung fortzusetzen, warum der Starke oft mit statischer Aufladung gepaart ist, ist Norman Ramseys vorherige Antwort sehr gut: stackoverflow.com/questions/376611/…
JasDev

1
"abc" + 123ist ein Laufzeitfehler , kein Kompilierungsfehler in Ruby. Wenn es sich um einen Kompilierungsfehler handelt, wird Ruby statisch eingegeben.
sepp2k

Die schwachen Tippbeispiele müssen verbessert werden (siehe meine Antwort), aber ansonsten schöne Formatierung.
Adam Gent

In meiner Meinung ist stark gegen schwach typgin: Stark: "c" + True = Laufzeitfehler oder Kompilierungszeitfehler. Schwach: "c" + True = "b" oder "d", weil alles als Rohbytes behandelt wird. Stark: C #, Ruby, C ++ Schwach: Assembly, C (aufgrund impliziter ungültiger Zeiger)
Jonathan Allen

17

Beide sind Pole auf zwei verschiedenen Achsen:

  • stark getippt vs. schwach getippt
  • statisch typisiert vs. dynamisch typisiert

Stark typisiert bedeutet, dass a nicht automatisch von einem Typ in einen anderen konvertiert wird. Schwach getippt ist das Gegenteil: Perl kann eine Zeichenfolge wie "123"in einem numerischen Kontext verwenden, indem sie automatisch in int konvertiert wird 123. Eine stark typisierte Sprache wie Python wird dies nicht tun.

Statisch typisiert bedeutet, dass der Compiler den Typ jeder Variablen zur Kompilierungszeit ermittelt. Dynamisch typisierte Sprachen ermitteln nur zur Laufzeit die Variablentypen.


6
Ich muss nicht zustimmen. Eine stark typisierte Sprache kennt die Typen zur Laufzeit. Eine schwach typisierte Sprache mag Assembly nicht. Ihr Beispiel befindet sich auf einer dritten Achse, "implizite oder explizite Konvertierungen".
Jonathan Allen

1
Eigentlich normal stimme ich Jonathan zu, aber Sie müssen nicht über die zur Laufzeit verfügbaren Typen verfügen, um stark typisiert zu werden, wenn Sie eine vollständige statische Analyse durchführen und kein Casting zulassen. (siehe meine bearbeitete Antwort).
Adam Gent

Python ist ein Beispiel für eine dynamisch typisierte und eine stark typisierte Sprache
MaRoBet

12

Stark typisiert bedeutet, dass es Einschränkungen zwischen Konvertierungen zwischen Typen gibt. Statisch typisiert bedeutet, dass die Typen nicht dynamisch sind. Sie können den Typ einer Variablen nach ihrer Erstellung nicht mehr ändern.


Um dies zu demonstrieren: In einer stark typisierten Sprache können Sie "5" == 5 nicht vergleichen und es als wahr ansehen: Zeichenfolgen sind keine ganzen Zahlen. Wenn mein Gedächtnis dient, werden die meisten modernen Skriptsprachen stark dynamisch typisiert. Tcl / Tk ist jedoch schwach typisiert - Alles kann als Zeichenfolge behandelt werden.
Little Bobby Tables

Bobby, in einer schwach getippten Sprache wird "5" == 5 als 0x35 == 0x05 gelesen. Mit anderen Worten, alles wird als Rohbyte behandelt.
Jonathan Allen

Ich muss euch beiden nicht zustimmen. Nimm Lua; Sie können "5" == 5 vergleichen und es wird false zurückgegeben. Eine schnelle Konvertierung kann jedoch durchgeführt werden, indem Sie "5" + 0
wählen

12

Datenzwang bedeutet nicht unbedingt schwach typisiert, weil manchmal sein syntakischer Zucker:

Das obige Beispiel zeigt, dass Java aufgrund von schwach typisiert ist

String s = "abc" + 123;

Ist kein schwach typisiertes Beispiel, weil es wirklich funktioniert:

String s = "abc" + new Integer(123).toString()

Datenzwang ist auch nicht schwach typisiert, wenn Sie ein neues Objekt erstellen. Java ist ein sehr schlechtes Beispiel für schwach typisiert (und jede Sprache, die gut reflektiert wird, wird höchstwahrscheinlich nicht schwach typisiert sein). Weil die Laufzeit der Sprache immer weiß, um welchen Typ es sich handelt (die Ausnahme können native Typen sein).

Dies ist anders als bei C. C ist eines der besten Beispiele für schwach typisierte. Die Laufzeit hat keine Ahnung, ob 4 Bytes eine Ganzzahl, eine Struktur, ein Zeiger oder 4 Zeichen sind.

Die Laufzeit der Sprache definiert wirklich, ob sie schwach typisiert ist oder nicht, ansonsten ist sie wirklich nur eine Meinung.

BEARBEITEN: Nach weiteren Überlegungen ist dies nicht unbedingt der Fall, da zur Laufzeit nicht alle Typen im Laufzeitsystem geändert werden müssen, um ein stark typisiertes System zu sein. Haskell und ML verfügen über eine so vollständige statische Analyse, dass sie möglicherweise Informationen zum Typ aus der Laufzeit auslassen können.


B ist wahrscheinlich ein besseres, wenn auch etwas weniger bekanntes Beispiel.
Tom Hawtin - Tackline

Javascript ist auch ein eher schwacher Typ, aber weil es so wenige Typen gibt und man keine neuen Typen erstellen kann.
Adam Gent

9

Die Antwort ist bereits oben angegeben. Der Versuch, zwischen starkem und wöchentlichem und statischem und dynamischem Konzept zu unterscheiden.

Was ist stark getippt VS schwach getippt?

Stark typisiert: Wird nicht automatisch von einem Typ in einen anderen konvertiert

In Go oder Python wie stark typisierten Sprachen löst "2" + 8 einen Typfehler aus, da sie keinen "Typzwang" zulassen.

Schwach (lose) typisiert: Wird automatisch in einen Typ in einen anderen konvertiert: Schwach typisierte Sprachen wie JavaScript oder Perl geben keinen Fehler aus. In diesem Fall ergibt JavaScript '28' und Perl 10.

Perl Beispiel:

my $a = "2" + 8;
print $a,"\n";

Speichern Sie es in main.pl und führen perl main.plSie es aus. Sie erhalten die Ausgabe 10.

Was ist der statische VS Dynamic-Typ?

Bei der Programmierung definiert der Programmierer die statische und die dynamische Typisierung in Bezug auf den Punkt, an dem die Variablentypen überprüft werden. Statisch typisierte Sprachen sind solche, bei denen die Typprüfung zur Kompilierungszeit durchgeführt wird, während dynamische typisierte Sprachen solche sind, bei denen die Typprüfung zur Laufzeit durchgeführt wird.

  • Statisch: Typen, die vor der Laufzeit überprüft wurden
  • Dynamisch: Typen, die während der Ausführung im laufenden Betrieb überprüft werden

Was bedeutet das?

In Go werden vor der Laufzeit eingegebene Prüfungen überprüft (statische Prüfung). Dies bedeutet, dass nicht nur der von ihm ausgeführte Code übersetzt und typgeprüft wird, sondern auch der gesamte Code gescannt wird und ein Typfehler ausgegeben wird, bevor der Code überhaupt ausgeführt wird. Zum Beispiel,

package main

import "fmt"

func foo(a int) {
    if (a > 0) {
        fmt.Println("I am feeling lucky (maybe).")
    } else {
        fmt.Println("2" + 8)
    }
}

func main() {
    foo(2)
}

Speichern Sie diese Datei in main.go und führen Sie sie aus. Die Meldung "Kompilierung fehlgeschlagen" wird angezeigt.

go run main.go
# command-line-arguments
./main.go:9:25: cannot convert "2" (type untyped string) to type int
./main.go:9:25: invalid operation: "2" + 8 (mismatched types string and int)

Dieser Fall gilt jedoch nicht für Python. Der folgende Codeblock wird beispielsweise für den ersten Aufruf von foo (2) ausgeführt und schlägt für den zweiten Aufruf von foo (0) fehl. Dies liegt daran, dass Python dynamisch typisiert ist, nur Code übersetzt und typprüft, auf dem es ausgeführt wird. Der else-Block wird niemals für foo (2) ausgeführt, daher wird "2" + 8 niemals angesehen, und bei einem Aufruf von foo (0) wird versucht, diesen Block auszuführen, was fehlgeschlagen ist.

def foo(a):
    if a > 0:
        print 'I am feeling lucky.'
    else:
        print "2" + 8
foo(2)
foo(0)

Sie sehen folgende Ausgabe

python main.py
I am feeling lucky.
Traceback (most recent call last):
  File "pyth.py", line 7, in <module>
    foo(0)
  File "pyth.py", line 5, in foo
    print "2" + 8
TypeError: cannot concatenate 'str' and 'int' objects

8

Eins impliziert nicht das andere. Wenn eine Sprache statisch typisiert werden soll, bedeutet dies, dass die Typen aller Variablen zur Kompilierungszeit bekannt sind oder abgeleitet werden.

In einer stark typisierten Sprache können Sie einen Typ nicht als anderen verwenden. C ist eine schwach typisierte Sprache und ein gutes Beispiel dafür, was stark typisierte Sprachen nicht zulassen. In C können Sie ein Datenelement des falschen Typs übergeben, das sich nicht beschwert. In stark typisierten Sprachen können Sie nicht.


7

Starke Typisierung bedeutet wahrscheinlich, dass Variablen einen genau definierten Typ haben und dass es strenge Regeln für das Kombinieren von Variablen unterschiedlichen Typs in Ausdrücken gibt. Wenn beispielsweise A eine Ganzzahl und B ein Float ist, kann die strenge Regel für A + B lauten, dass A in einen Float umgewandelt und das Ergebnis als Float zurückgegeben wird. Wenn A eine Ganzzahl und B eine Zeichenfolge ist, kann die strenge Regel lauten, dass A + B nicht gültig ist.

Statische Typisierung bedeutet wahrscheinlich, dass Typen zur Kompilierungszeit zugewiesen werden (oder gleichwertig für nicht kompilierte Sprachen) und sich während der Programmausführung nicht ändern können.

Beachten Sie, dass sich diese Klassifikationen nicht gegenseitig ausschließen. Ich würde sogar erwarten, dass sie häufig zusammen auftreten. Viele stark typisierte Sprachen sind auch statisch typisiert.

Und beachten Sie, dass wenn ich das Wort "wahrscheinlich" verwende, es daran liegt, dass es keine allgemein akzeptierten Definitionen dieser Begriffe gibt. Wie Sie bereits aus den bisherigen Antworten gesehen haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.