Forum: PC-Programmierung C++ 17: Eure Meinung zu 'If statement with initializer'


von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hallo Leute,

mich wuerde einfach mal eure Meinung zu diesem Feature interessieren.

C++17 If statement with initializer
https://skebanga.github.io/if-with-initializer/
1
if (init; condition)
Ich verstehe den Zweck dahinter (Sichtbarkeit von Variablen 
beschraenken), aber ich finde, dass es die Lesbarkeit doch sehr 
verschlechtert. Gerade bei groesseren Projekten kann ich mir vorstellen, 
dass das bei einem Codereview eher stoert, da man jetzt jedes if noch 
genauer unter die Lupe nehmen muss.

Natuerlich, man muss das Feature ja nicht benutzen, schon klar.

Wie ist eure Meinung dazu?

Gruesse

von Felix U. (ubfx)


Lesenswert?

Ich finde es super. Vor Allem weil man die temporären Variablen, die man 
im init Statement erzeugt, meistens auch nur im if-context benötigt und 
sie sonst außerhalb deklarieren müsste. Insofern räumt es also sogar das 
Scope außerhalb des ifs auf, weil dort eine Variable weniger rumhängt.

Beim Code Review sollte es auch nicht weiter stören, weil die Variable 
ja dann auch nur im if-context existiert.

: Bearbeitet durch User
von Dussel (Gast)


Lesenswert?

Das ist das gleiche 'Problem', wie bei Schleifen. Immer wenn ich C 
programmieren muss, vermisse ich die Deklaration von Variablen in 
Schleifenköpfen.
Bei If bin ich es noch nicht gewohnt, deshalb vermisse ich es auch 
nicht, aber wenn es sich einmal verbreitet hat, wird das auch nicht 
anders sein, als bei Schleifen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dussel schrieb:
> Immer wenn ich C programmieren muss, vermisse ich die Deklaration von
> Variablen in Schleifenköpfen.

Dann nimm doch einen halbwegs aktuellen Compiler …

von Johannes S. (Gast)


Lesenswert?

man kann auch Variablen in einen {} Block setzen, damit hat man auch den 
Gültigkeitsbereich eingegrenzt. So mache ich das gerne, das entschärft 
etwas Kopierfehler bei denen eine Variable dann evtl. nicht mehr den 
richtigen init Wert hat wenn sie wiederverwendet wird. Und das geht noch 
mit altmodischem C++98.
1
void myFunc()
2
{
3
  {
4
    int i = 0;
5
  }
6
7
  {
8
    int i = 0;
9
  }
10
}

von Cyberpunk (Gast)


Lesenswert?

Ich hätte ja lieber if-statements als Ausdruck wie z.B. bei Rust, aber 
dass neue If ist auch nicht schlecht.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

also ich finde die If statement with initializer Variante viel lesbarer, 
weil ja sofort und eindeutig geklärt ist, dass die Variable nur für den 
if-scope gedacht ist. Wenn ich die Variable im umgebenden scope 
definieren muss, muss der Leser ja erstmal untersuchen, ob sie noch 
irgendwo gebraucht wird.

Zusammen mit structured bindings ergibt das wirklich lokale und präzise 
Ausdrücke und er der Code wird viel aussagekräftiger.

Meine Meinung

vlg
 Timm

von Scott Meyers (Gast)


Lesenswert?

Kaj G. schrieb:
> Hallo Leute,
>
> mich wuerde einfach mal eure Meinung zu diesem Feature interessieren.
>
> C++17 If statement with initializer
> https://skebanga.github.io/if-with-initializer/
> if (init; condition)
> Ich verstehe den Zweck dahinter (Sichtbarkeit von Variablen
> beschraenken), aber ich finde, dass es die Lesbarkeit doch sehr
> verschlechtert. Gerade bei groesseren Projekten kann ich mir vorstellen,
> dass das bei einem Codereview eher stoert, da man jetzt jedes if noch
> genauer unter die Lupe nehmen muss.
>
> Natuerlich, man muss das Feature ja nicht benutzen, schon klar.
>
> Wie ist eure Meinung dazu?

Du hast doch schon eigentlich einen ganz guten Post dazu zitiert. 
Überzeugt Dich das nicht?

Gerade im Zusammenhang mit den Iteratoren / Algorithmen der stdlibc++ 
ist es sehr interessant. Weil es einfach den Code kürzer macht und 
oftmals ein explicit-bool-conv-op obsolet macht.

Nur eine gelöschte Codezeile ist eine gute Codezeile.

von A. S. (Gast)


Lesenswert?

Dussel schrieb:
> Immer wenn ich C programmieren muss, vermisse
> ich die Deklaration von Variablen in Schleifenköpfen.

Dussel schrieb explizit *köpfen*:
1
for(int i=0; i < MAX_STUFF; i++)
2
{
3
    doStuff(i);
4
}
5
/* und hier sollte i wieder unbekannt sein!! */

Das Problem in C ist doch, dass i unten weiterlebt.

von Daniel A. (daniel-a)


Lesenswert?

Ich finde das Feature einfach nur unnötig. Es gibt bereits einfache 
Mittel um den Scope von Variablen zu begrenzen und es verlagert den Code 
ja nur von 2 Zeilen auf eine Zeile. Ich finde alles auf eine Zeile zu 
packen reduziert nur die Lesbarkeit.

Achim S. schrieb:
> Dussel schrieb explizit *köpfen*:
> for(int i=0; i < MAX_STUFF; i++)
> {
>     doStuff(i);
> }
> /* und hier sollte i wieder unbekannt sein!! */
>
> Das Problem in C ist doch, dass i unten weiterlebt.

Nein, das tut i nicht.

: Bearbeitet durch User
von gcc (Gast)


Angehängte Dateien:

Lesenswert?

gcc 7.2

von mh (Gast)


Lesenswert?

Wenn man sich die Kommentare durchliest, könnte man auf den Gedanken 
kommen, dass c++17 die Definition von Variablen in der if-condition 
einführt. Neu ist allerdings nur die explizite Trennung von init und 
condition.
1
if(const char* hallo = "Welt") {}
ist schon in c++98 möglich. Nur
1
if(const char* hallo = "Welt"; false) {}
ist neu in c++17.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Achim S. schrieb:
>
1
> for(int i=0; i < MAX_STUFF; i++)
2
> {
3
>     doStuff(i);
4
> }
5
> /* und hier sollte i wieder unbekannt sein!! */
6
>
>
> Das Problem in C ist doch, dass i unten weiterlebt.

s:C:JavaScript:

In JavaScript werden Deklarationen zum Anfang des Gültigkeitsbereich 
gehoistet, d.h.
1
function fun (MAX_STUFF)
2
{
3
  doStuff (0);
4
  for (var i = 0; i < MAX_STUFF; i++)
5
  {
6
      doStuff (i);
7
  }
8
}

ist gleichbedeitend mit:
1
function fun (MAX_STUFF)
2
{
3
  var i;
4
  doStuff (0);
5
  for (i = 0; i < MAX_STUFF; i++)
6
  {
7
      doStuff (i);
8
  }
9
}

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Achim S. schrieb:
> Das Problem in C ist doch, dass i unten weiterlebt.
Nein. Dieses Verhalten kenne ich nur von Visual Studio 2003, und da ist 
das ein bekannter Bug.

von Name (Gast)


Lesenswert?

Johann L. schrieb:
> In JavaScript werden Deklarationen zum Anfang des Gültigkeitsbereich
> gehoistet, d.h.

Deswegen sollte man ES2015 und let/const statt var verwenden. ;)

von Dussel (Gast)


Lesenswert?

Jörg W. schrieb:
> Dussel schrieb:
>> Immer wenn ich C programmieren muss, vermisse ich die Deklaration von
>> Variablen in Schleifenköpfen.
>
> Dann nimm doch einen halbwegs aktuellen Compiler …
Ok, ich meinte was anderes. Ich vermisse es, wenn ich C auf einem System 
programmieren muss, auf dem das nicht geht.

mh schrieb:
> Wenn man sich die Kommentare durchliest, könnte man auf den Gedanken
> kommen, dass c++17 die Definition von Variablen in der if-condition
> einführt. Neu ist allerdings nur die explizite Trennung von init und
> condition.
> if(const char* hallo = "Welt") {}
> ist schon in c++98 möglich.
Das wusste ich tatsächlich nicht.

Daniel A. schrieb:
> Ich finde das Feature einfach nur unnötig. Es gibt bereits einfache
> Mittel um den Scope von Variablen zu begrenzen und es verlagert den Code
> ja nur von 2 Zeilen auf eine Zeile. Ich finde alles auf eine Zeile zu
> packen reduziert nur die Lesbarkeit.
Du definierst nie eine Variable im Schleifenkopf? Das ist mir auch noch 
nicht untergekommen.

von Scott Meyers (Gast)


Lesenswert?

Das ganze ist doch eine herrliche Vereinfachung einfach deswegen, weil 
nun for/if/switch nach demselben Prinzip gebildet werden können. Und 
m.E. ist jede Art von Gleichförmigkeit gut, weil man sich weniger merken 
muss ...

von Nop (Gast)


Lesenswert?

Wie ist das eigentlich mit dem Stackverbrauch bei Variablen-Scoping? Ist 
natürlich implementationsabhängig, aber was ist da üblich?

1) der Compiler ist schlau genug, disjunkte Scopes zu erkennen und 
verwendet dieselben Stack-Slots wieder
2) der Compiler kriegt das nicht hin und verbraucht die Summe aller 
Variablen

Im Falle von 2 würde konsequentes Scoping dann ja zu deutlich höherem 
Stackverbrauch führen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der Scope einer Variablen ist doch nur der theoretisch längstmögliche
Zeitraum, in dem diese Variable „lebt“.

Alle aktuellen Compiler limitieren die tatsächliche Lebensdauer auf
das notwendige Minimum.  Es ist ja nicht nur Stack, viel knapper ist
im Allgemeinen die Ressource „Register“, sodass man diese natürlich
maximal wiederverwenden können möchte.  Sowie eine Variable nicht mehr
gebraucht wird, fliegt sie da wieder raus.  Variablen, die gar nicht
gebraucht werden, bekommen folglich dann auch nie reale Ressourcen
zugewiesen.

Das setzt natürlich eingeschaltete Optimierungen voraus.

von Rolf M. (rmagnus)


Lesenswert?

Aber soweit ich weiß, wird die Größe des Stackframes nicht dynamisch 
während der Laufzeit einer Funktion verändert, sprich: Aller benötigter 
Stack für lokale Variablen wird zu Beginn der Funktion belegt, auch wenn 
die Variablen erst später definiert werden. Oder ist das falsch? Bei 
Registern mag das natürlich anders sein.

Jörg W. schrieb:
> Dussel schrieb:
>> Immer wenn ich C programmieren muss, vermisse ich die Deklaration von
>> Variablen in Schleifenköpfen.
>
> Dann nimm doch einen halbwegs aktuellen Compiler …

Wobei "halbwegs aktuell" bedeutet, dass er nicht mehr auf dem Stand aus 
den Neunzigern des letzten Jahrhunderts sein sollte.

Johannes S. schrieb:
> man kann auch Variablen in einen {} Block setzen, damit hat man auch den
> Gültigkeitsbereich eingegrenzt.

Das mache ich auch ganz gerne. Meist wird aber aus diesem Block später 
dann eine eigene Funktion.

Cyberpunk schrieb:
> Ich hätte ja lieber if-statements als Ausdruck wie z.B. bei Rust, aber
> dass neue If ist auch nicht schlecht.

Das gibt es doch schon mit dem ?:-Operator.

von Peter II (Gast)


Lesenswert?

Jörg W. schrieb:
> Alle aktuellen Compiler limitieren die tatsächliche Lebensdauer auf
> das notwendige Minimum.

sobald die Variablen Objekte sind, wird der Destruktor am ende vom Block 
aufgerufen, so lange existiert sie. Da kann nichts optimiert werden.

von Rolf M. (rmagnus)


Lesenswert?

Peter II schrieb:
> Jörg W. schrieb:
>> Alle aktuellen Compiler limitieren die tatsächliche Lebensdauer auf
>> das notwendige Minimum.
>
> sobald die Variablen Objekte sind, wird der Destruktor am ende vom Block
> aufgerufen, so lange existiert sie. Da kann nichts optimiert werden.

Selbstverständlich kann da optimiert werden. Solange alle Zugriffe auf 
volatile-Variablen und alle File-I/O noch stattfinden wie im Code 
angegeben, darf der Compiler da beliebig verändern (siehe "as-if rule").
Aber es ging gar nicht darum, ob der Destruktor verfrüht aufgerufen 
wird, sondern darum, ob der Speicher länger als nötig belegt wird (also 
z.B. schon bei Beginn der Funktion, obwohl das Objekt erst weiter unten 
im Code definiert ist).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Aber soweit ich weiß, wird die Größe des Stackframes nicht dynamisch
> während der Laufzeit einer Funktion verändert, sprich: Aller benötigter
> Stack für lokale Variablen wird zu Beginn der Funktion belegt, auch wenn
> die Variablen erst später definiert werden.

Das dürfte bei den meisten Compilern so sein.

Allerdings kann der Compiler natürlich auch auf dem Stack Variablen
überlagern, d. h. die gleiche Stackadresse kann zu verschiedenen
Zeiten für verschiedene Variablen genutzt werden.

von Michael B. (laberkopp)


Lesenswert?

Felix U. schrieb:
> Ich finde es super. Vor Allem weil man die temporären Variablen, die man
> im init Statement erzeugt, meistens auch nur im if-context benötigt

Witzbold, man KANN sie gar nicht ausserhalb verwenden.

Die Frage ist, wie oft man Variablen braucht, die bei der Auswertung des 
IFs errechnet werden und dann nur im THEN oder ELSE Fall irgendwie 
benötigt werden.

Eher selten.

Die Frage wäre auch, was an dem 'modernen' if
1
if(int i;(i=anzahl())>0)
2
{
3
  cout << "Anzahl: " << i;
4
}
oder
1
if(int i=anzahl();i>0)
2
{
3
  cout << "Anzahl: " << i;
4
}
besser und verständlicher ist als an
1
{
2
  int i;
3
  if((i=anzahl())>0)
4
  {
5
    cout << "Anzahl: " << i;
6
  }
7
}
oder wie ich finde übersichtlicher
1
{
2
  int i;
3
  i=anzahl();
4
  if(i>0)
5
  {
6
    cout << "Anzahl: " << i;
7
  }
8
}

von Di P. (drpepper) Benutzerseite


Lesenswert?

Michael B. schrieb:

>
> Die Frage wäre auch, was an dem 'modernen' if
>
1
> ...
2
>
> besser und verständlicher ist als an
>
1
> ...
2
>
> oder wie ich finde übersichtlicher
>
1
> ...
2
>

Weniger Zeilen, mMn besser lesbar, kein Vertun mit Scopes, ...

von Ralf G. (ralg)


Lesenswert?

Michael B. schrieb:
> Witzbold, man KANN sie gar nicht ausserhalb verwenden.

Im 'if'- context !
Dein 'i' wird auch (möglicherweise!) nur im Zusammenhang mit der 
Abfrage benötigt.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Michael B. schrieb:
> Die Frage wäre auch, was an dem 'modernen' if besser...
Beispiel (okay, Moeglicherweise ein nicht sonderlich gutes):
1
#include <new>
2
3
int main()
4
{
5
    if (int *ptr = new(std::nothrow) int; ptr != nullptr) {
6
        *ptr = 42;
7
        delete ptr;
8
    }
9
10
    *ptr = 666;   //  fehler!
11
12
    return 0;
13
}
Es gibt keine Moeglichkeit 'ausversehen' ausserhalb des if auf den schon 
freigegebenen Speicher zuzugreifen (use-after-free). Also eine 
Moeglichkeit, mit wenig Aufwand und wenig nachdenken, kritische 
Sicherheitsluecken zu vermeiden. Ganz ohne das man da extra {} setzen 
muss. Natuerlich kann man innerhalb des if nach dem delete immer noch 
mist machen, aber es ist auf diesen (im idealfall sehr kleinen) Scope 
begrenzt.

Bei deiner Methode besteht diese Gefahr aber.
1
#include <new>
2
3
int main()
4
{
5
    int *ptr = new(std::nothrow) int;
6
    if (ptr != nullptr) {
7
        *ptr = 42;
8
    }
9
10
    delete ptr;
11
    *ptr = 666;   //  use-after-free
12
13
    return 0;
14
}
Und use-after-free ist ein recht haeufiger Fehler.

Und ein
1
#include <new>
2
3
int main()
4
{
5
    {
6
        int *ptr = new(std::nothrow) int;
7
        if (ptr != nullptr) {
8
            *ptr = 42;
9
        }
10
11
        delete ptr;
12
    }
13
    *ptr = 666;
14
15
    return 0;
16
}
ist auch nicht wirklich lesbarer, da es wieder eine Einrueckungstiefe 
einfuegt, die man mit diesem Feature vermeiden kann. Aber das ist wieder 
Ansichtssache.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Jörg W. schrieb:

> Allerdings kann der Compiler natürlich auch auf dem Stack Variablen
> überlagern, d. h. die gleiche Stackadresse kann zu verschiedenen
> Zeiten für verschiedene Variablen genutzt werden.

Genau, aber tut z.B. ARM-GCC das auch tatsächlich?

von Matthias H. (hallamen)


Lesenswert?

Nop schrieb:
> Jörg W. schrieb:
>
>> Allerdings kann der Compiler natürlich auch auf dem Stack Variablen
>> überlagern, d. h. die gleiche Stackadresse kann zu verschiedenen
>> Zeiten für verschiedene Variablen genutzt werden.
>
> Genau, aber tut z.B. ARM-GCC das auch tatsächlich?

Ja.
1
$ arm-none-eabi-g++ -std=c++11 -fverbose-asm -Os -g0 -S code.cpp && grep "frame =" code.s
2
        @ args = 0, pretend = 0, frame = 8000
3
$ arm-none-eabi-g++ -std=c++11 -fverbose-asm -Os -g0 -S -DTWO_SCOPES code.cpp && grep "frame =" code.s
4
        @ args = 0, pretend = 0, frame = 4000

code.cpp:
1
extern int *a, *b, *c; 
2
3
constexpr int N = 1000;
4
5
void f(int x) {
6
#ifdef TWO_SCOPES
7
    {   
8
#endif
9
        int t1[N];
10
        for(int i=0;i<N;i++) {
11
            t1[i] = a[i];
12
        }
13
        for(int i=0;i<N;i++) {
14
            a[b[i]] = t1[i];
15
        }
16
#ifdef TWO_SCOPES
17
    }   
18
    {   
19
#endif
20
        int t2[N];
21
        for(int i=0;i<N;i++) {
22
            t2[i] = b[i];
23
        }
24
        for(int i=0;i<N;i++) {
25
            b[c[i]] = t2[i];
26
        }
27
#ifdef TWO_SCOPES
28
    }   
29
#endif
30
}

von mh (Gast)


Lesenswert?

Michael B. schrieb:
> Die Frage ist, wie oft man Variablen braucht, die bei der Auswertung des
> IFs errechnet werden und dann nur im THEN oder ELSE Fall irgendwie
> benötigt werden.
>
> Eher selten.
Bei mir kommt es ziemlich häufig vor, dass ich Variablen nur im 
statement-true und/oder statement-false benötige. Z.B. im zusammenhang 
mit map, set und co.
1
void map_bsp(std::map<int,int>& m) {
2
  if(auto i = m.find(42); i != m.end()) {
3
4
  }
5
  else {
6
7
  }
8
}
9
10
void set_bsp(std::set<int>& s, int v) {
11
  if(auto [i, flag] = s.insert(v); flag) {
12
    std::cout << *i << std::endl;
13
  }
14
  else {
15
16
  }
17
}

> Die Frage wäre auch, was an dem 'modernen' ifif(int i;(i=anzahl())>0)
> {
>   cout << "Anzahl: " << i;
> }
> oderif(int i=anzahl();i>0)
> {
>   cout << "Anzahl: " << i;
> }
Warum sollte man die erste Variante benutzen?

> besser und verständlicher ist als an{
>   int i;
>   if((i=anzahl())>0)
>   {
>     cout << "Anzahl: " << i;
>   }
> }
> oder wie ich finde übersichtlicher{
>   int i;
>   i=anzahl();
>   if(i>0)
>   {
>     cout << "Anzahl: " << i;
>   }
> }
Warum sollte man die erste Variante benutzen?

Und ja, ich finde
1
if(int i = anzahl(); i > 0)  {}
übersichtlicher, als deine bevorzugte Varianten. Vor allem mit deinem
1
int i;
2
i = anzahl();
3
...
habe ich Probleme.

von jz23 (Gast)


Lesenswert?

mh schrieb:
> Vor allem mit deinem
> int i;
> i = anzahl();
> ...
> habe ich Probleme.

Ja, so ein Code kann wirklich ziemlich schnell unübersichtlich werden. 
Vor allem wenn ich mehrere Variablen gleichzeitig definiere. Da kann man 
leicht mal was vergessen. Außerdem kann man auto nicht benutzen, was 
gerade bei Datentypen mit langen Namen (bzw. Templates) viel Aufwand 
spart.
1
int i, j, k;
2
i = anzahl();
3
j = anzahl2();
4
k = anzahl3();

vs.
1
auto i = anzahl(), j = anzahl2(), k = anzahl3();

von Rolf M. (rmagnus)


Lesenswert?

Michael B. schrieb:
> Die Frage ist, wie oft man Variablen braucht, die bei der Auswertung des
> IFs errechnet werden und dann nur im THEN oder ELSE Fall irgendwie
> benötigt werden.
>
> Eher selten.

Ich brauche das eigentlich ständig. Ich würde vielmehr sagen, dass ich 
eher selten die Variable danach noch brauche.

jz23 schrieb:
> Ja, so ein Code kann wirklich ziemlich schnell unübersichtlich werden.
> Vor allem wenn ich mehrere Variablen gleichzeitig definiere. Da kann man
> leicht mal was vergessen. Außerdem kann man auto nicht benutzen, was
> gerade bei Datentypen mit langen Namen (bzw. Templates) viel Aufwand
> spart.

Ich bevorzuge es auch, Variablen gleich bei der Definition mit ihrem 
Wert vorzubelegen, wenn es geht. Ich finde jedenfalls ein
1
int i;
2
i = 3;
weder lesbarer, noch eleganter als ein
1
int i = 3;

Und bei Klassen kommt dann natürlich noch dazu, dass die Erzeugung ggf 
umständlicher ist, wenn erst der Default-Konstruktor das Objekt erzeugt 
und dann eine Zuweisung kommt, als wenn es gleich mit dem richtigen 
Konstruktor erzeugt wird.

von Carl D. (jcw2)


Lesenswert?

Ganz nebenbei kann man mit dieser Syntax elegant ein if-Statement inkl. 
dem darin befindlichen Block atomar ausführen. Wenn im if-initializer 
ein Objekt erzeugt wird, dessen Konstruktor z.B. die Falgs sichert und 
INT's sperrt, und dessen Destruktor am Ende des "if-Blocks" die Flags 
wieder "restored". Damit ist alles inkl. der "if-Condition" selbst 
atomar (singlecore-AVR vorausgesetzt). Es reicht ein:
1
if( Guard guard; <Condition> ) {
2
    // nö INT's!
3
}

Das kann man natürlich auch als separaten Block um den "if-Block" 
schreiben, aber irgendwas in dieser Art mit automatischer Destruktion 
bei Scope-Ende scheint oft genug gebraucht zu werden, daß das 
ISO-Komitee dies aufnahm. Symmetrie zu "for" spricht ja aber auch nicht 
dagegen.

Automatische Destruktion von Objekten ist der Hauptpunkt, weniger die 
Bereitstellung von n Instanzen der Zähl-Variablen i innerhalb einer 
Funktion.
Obiges Guard-Objekt belegt übrigens genau ein Byte, eine Kopie des 
Flag-Registers, und landet, entsprechend kurzen "if-Block" vorausgesetzt 
einfach in einen freien CPU-Register.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.