Forum: Compiler & IDEs Struct Tabelle mit Pointer auf Var/Fkt, wie ansprechen?


von Offlineuser (Gast)


Lesenswert?

Moin moin Allseits,

ich habe ein frage die zwar nicht ganz zu GCC passt, aber dem am 
nächsten kommt da es eine allgemeine C-Frage ist, denke ich.
Folgendes möchte ich machen und bekommen es nicht ganz gebacken. Ein 
CANopen Verzeichniss soll umgesetzt werden. Dazu habe ich eine Typedef 
Struct angelegt mit Index, SubIndex, Attribute (read, Write, etc.) Typ 
(Uint8, uint16, etc.) und einem pointer (der soll sowohl auf Variable, 
als auch auf Funktion zeigen können, falls das überhaupt geht). Hier 
erst mal der entsprechende Code:
1
typedef struct
2
{
3
  unsigned short    idx;  // index des eintrags
4
  unsigned char    sub;  // subindex des eintrags
5
  enum attributes  attr;  // attribute
6
  enum types      type;  // variablentyp
7
  const uchar      *pROM;
8
  
9
} _CANopen_DICT_Template;
10
11
const uchar __dummy[4] = {0,0,0,0};
12
13
const _CANopen_DICT_Template _db_object[] = {
14
  //idx    sub    attributen        typ      ptr auf variable/funktion
15
  {0x1000,  0x00,  CONST,            u32,    ((const unsigned char *)&rCO_DevType)},
16
  {0x1001,  0x00,  RO,              u8,    ((const unsigned char *)&uCO_DevErrReg)},
17
  {0x1002,  0x00,  RO,              u32,    ((const unsigned char *)&uCO_DevManufacturerStatReg)},
18
//  {0x1005,  0x00,  FUNC | RW,          u32,    ((const unsigned char *)&_CO_COMM_SYNC_COBIDAccessEvent)},
19
  {0x1008,  0x00,  CONST,            u32,    ((const unsigned char *)&rCO_DevName)},
20
  {0x1009,  0x00,  CONST,            u32,    ((const unsigned char *)&rCO_DevHardwareVer)},
21
  {0x100A,  0x00,  CONST,            u32,    ((const unsigned char *)&rCO_DevSoftwareVer)},
22
//  {0x100C,  0x00,  FUNC | RW,          u16,    ((const unsigned char *)&_CO_COMM_NMTE_GuardTimeAccessEvent)},
23
//  {0x100D,  0x00,  FUNC | RW,          u8,    ((const unsigned char *)&_CO_COMM_NMTE_LifeFactorAccessEvent)},
24
//  {0x1017,  0x00,  FUNC | RW,          u16,    ((const unsigned char *)&_CO_COMM_NMTE_HeartBeatAccessEvent)},
25
  {0x1018,  0x00,  CONST,            u8,    ((const unsigned char *)&rCO_DevIdentityIndx)},
26
  {0x1018,  0x01,  CONST,            u32,    ((const unsigned char *)&rCO_DevVendorID)},
27
  {0x1018,  0x02,  CONST,            u32,    ((const unsigned char *)&rCO_DevProductCode)},
28
  {0x1018,  0x03,  CONST,            u32,    ((const unsigned char *)&rCO_DevRevNo)},    
29
  {0x1018,  0x04,  CONST,            u32,    ((const unsigned char *)&rCO_DevSerialNo)},
30
31
  {0xFFFF,  0x00,  CONST,            u8,    ((const uchar *)&__dummy[0])}    // letzter eintrag
32
  
33
};

Die Mitglieder des Structs werden nun ganz normal mit Beispielsweise:
1
if (_db_object[k].type == u8) { /* tu dies und das*/ }
angesprochen.


Nun möchte ich aber auf meine Variable über den Pointer zugreifen, evtl. 
so:
1
if (_db_object[k].idx == 0x1018) {
2
  _db_object[k].*pROM = 0;  // variable nullen
3
}
Oder eben auf meine Funktion, ungefähr so:
1
if (_db_object[k].idx == 0x100D) {
2
  _db_object[k].pROM();  // funktion aufrufen
3
}
aber bekomme ich nicht gebacken, da ich nicht ganz verstehe wie ich nun 
auf die variable zugreifen kann, bzw. meine Funktion aufrufen kann. Hat 
jemand einen Tipp für mich?

von Klaus W. (mfgkw)


Lesenswert?

Offlineuser schrieb:
> Nun möchte ich aber auf meine Variable über den Pointer zugreifen, evtl.
> so:if (_db_object[k].idx == 0x1018) {
>   _db_object[k].*pROM = 0;  // variable nullen
> }

Vielleicht besser so:
1
   *_db_object[k].pROM = 0;

von Klaus W. (mfgkw)


Lesenswert?

Offlineuser schrieb:
> Oder eben auf meine Funktion, ungefähr so:

Welche Funktion?
Ich sehe hier keine.

Wenn es wirklich auf eine Funktion zeigt, muß aber gecastet werden
(da der Zeiger nicht als Zeiger auf eine Funktion deklariert ist,
sondern als Zeiger auf const uchar).
Um den cast zu zeigen, bräuchte man die genaue Deklaration der Funktion.

von Offlineuser (Gast)


Lesenswert?

Es ist übrigens ein IAR 68HC12 C-Compiler V2.20A/386 mit dem ich hier 
arbeite.

Der meldet bei
1
*_db_object[k].pROM = 0;

Folgendes:
1
*_db_object[k].pROM = 0;                // variable zuerst nullen
2
-------------^
3
"canopen.c",364  Error[122]: Modifiable lvalue required
Keine Ahnung was damit gemeint ist.


@Klaus Wachtler:
Die Funktion
1
void _CO_COMM_NMTE_LifeFactorAccessEvent (void)
 muss man sich im Moment noch dazu denken, die ist noch nicht 
implementiert, da ich zuerst die Variablenaufrufe in den Griff bekommen 
wollte.
Mit casten meinst du in etwa Folgendes?:
1
(const unsigned char *)&(void _CO_COMM_NMTE_LifeFactorAccessEvent (void)

Wäre es übrigens eine Alternative die Tabelle zu erweitern mit separatem 
Pointer auf Funktion, evtl. so:
1
typedef struct
2
{
3
  unsigned short    idx;  // index des eintrags
4
  unsigned char    sub;  // subindex des eintrags
5
  enum attributes  attr;  // attribute
6
  enum types      type;  // variablentyp
7
  const uchar      *pROM;
8
  void ( *pFKT )(void);      //Function pointer
9
  
10
} _CANopen_DICT_Template;
11
12
const uchar __dummy[4] = {0,0,0,0};
13
14
const _CANopen_DICT_Template _db_object[] = {
15
  //idx    sub    attributen        typ      ptr auf variable/funktion
16
  {0x1000,  0x00,  CONST,            u32,    ((const unsigned char *)&rCO_DevType), ((void *)&__dummyFKT())},
17
  {0x1001,  0x00,  RO,              u8,    ((const unsigned char *)&uCO_DevErrReg)), ((void *)&__dummyFKT())},
18
 {0x1002,  0x00,  RO,              u32,    ((const unsigned char *)&uCO_DevManufacturerStatReg)), ((void *)&__dummyFKT())},
19
/*
20
.
21
.
22
*/
23
  {0xFFFF,  0x00,  CONST,            u8,    ((const uchar *)&__dummy[0])), ((void *)&__dummyFKT())}    // letzter eintrag
24
25
};
26
27
void __dummyFKT (void) {
28
}

Mit entsprechendem Aufruf für die Funktion?
1
if (_db_object[k].idx == 0x1002) {
2
  _db_object[k].pFKT();  // funktion aufrufen
3
}

Ich muss zugeben, dass ich bei den ganzen Pointern im Moment ziemlich 
verwirrt bin.

von Karl H. (kbuchegg)


Lesenswert?

Offlineuser schrieb:

> Folgendes:
>
1
> *_db_object[k].pROM = 0;                // variable zuerst nullen
2
> -------------^
3
> "canopen.c",364  Error[122]: Modifiable lvalue required
4
>
> Keine Ahnung was damit gemeint ist.
1
 *(_db_object[k].pROM) = 0;                // variable zuerst nullen

> @Klaus Wachtler:
> Die Funktion
1
void _CO_COMM_NMTE_LifeFactorAccessEvent (void)
 muss
> man sich im Moment noch dazu denken, die ist noch nicht implementiert,

macht nichts.
Trotzdem muss sie ja eine Funktionssignatur haben.
Welchen Returntype hat sie?
Welche und wieviele Argumente nimmt sie?
Welchen Datentyp haben die Argumente?

Das alles musst du wissen wenn du eine Funktion aufrufen willst.
Das ist auch nicht anders, wenn man die Funktion per Pointer aufruft.

> Mit casten meinst du in etwa Folgendes?:
>
1
(const unsigned char *)&(void _CO_COMM_NMTE_LifeFactorAccessEvent
2
> (void)

Nein.
Funktionspointer sind anders.
http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger

> Wäre es übrigens eine Alternative die Tabelle zu erweitern mit separatem
> Pointer auf Funktion, evtl. so:

Ich würde ehrlich gesagt, die ganzen Funktionspointer von den 
Datenpointern komplett trennen. Komplett eigene Struktur. Schon alleine 
die Tatsache, dass du hier wie ein Wilder rumcasten musst, zeigt schon 
dass da was nicht stimmt.
Exzessives Casten ist meistens ein deutliches Indiz, dass der Aufbau der 
Datenstruktur nicht stimmt.


> Ich muss zugeben, dass ich bei den ganzen Pointern im Moment ziemlich
> verwirrt bin.

Das Gefühl habe ich auch. Du übernimmst dich. Du bist noch nicht soweit 
um das in den Griff zu kriegen.

von Offlineuser (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:

> macht nichts.
>
> Trotzdem muss sie ja eine Funktionssignatur haben.
>
> Welchen Returntype hat sie?
>
> Welche und wieviele Argumente nimmt sie?
>
> Welchen Datentyp haben die Argumente?
>
>
>
> Das alles musst du wissen wenn du eine Funktion aufrufen willst.
>
> Das ist auch nicht anders, wenn man die Funktion per Pointer aufruft.

Ist das nicht alles mit:
1
void _CO_COMM_NMTE_LifeFactorAccessEvent (void) {}
 bzw.
1
void __dummyFKT (void) {}
 erledigt? Keine Übergabeparameter und void als return.


> Nein.
>
> Funktionspointer sind anders.
>
> http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger

Ok, wenn ich das richtig verstehe, müsste ich das so realisieren:
1
typedef void (*VoidFnct)( void );
2
const uchar __dummy[4] = {0,0,0,0};
3
4
5
typedef struct
6
{
7
  unsigned short    idx;  // index des eintrags
8
  unsigned char     sub;  // subindex des eintrags
9
  enum attributes   attr;  // attribute
10
  enum types        type;  // variablentyp
11
  const uchar       *pROM;
12
  VoidFnct          Function;
13
} _CANopen_DICT_Template;
14
15
16
const _CANopen_DICT_Template _db_object[] = {
17
  //idx    sub    attributen        typ      ptr auf variable/funktion
18
  {0x1000,  0x00,  CONST,            u32,    ((const unsigned char *)&rCO_DevType), __dummyFKT},
19
  {0x1001,  0x00,  RO,              u8,    ((const unsigned char *)&uCO_DevErrReg), __dummyFKT},
20
 {0x1002,  0x00,  RO,              u32,    ((const unsigned char *)&uCO_DevManufacturerStatReg), __dummyFKT},
21
/*
22
.
23
.
24
*/
25
  {0xFFFF,  0x00,  CONST,            u8,    ((const uchar *)&__dummy[0]),  __dummyFKT}    // letzter eintrag
26
27
};
28
29
void __dummyFKT (void) {
30
}

Mit solchem Aufruf:
1
if (_db_object[k].idx == 0x1002) {
2
  _db_object[k].Function();  // funktion aufrufen
3
}
Sieht das plausibel aus?


> Ich würde ehrlich gesagt, die ganzen Funktionspointer von den
>
> Datenpointern komplett trennen. Komplett eigene Struktur. Schon alleine
>
> die Tatsache, dass du hier wie ein Wilder rumcasten musst, zeigt schon
>
> dass da was nicht stimmt.
>
> Exzessives Casten ist meistens ein deutliches Indiz, dass der Aufbau der
>
> Datenstruktur nicht stimmt.

Komplett eigene Struktur für Funktionen und Variablen möchte ich 
vermeiden. Die Tabelle soll flexibel sein und je nach Eintrag mal das 
eine mal das andere ausführen können.
Aber der Kompromis mit eigenem Eintrag für Variable und Funktion ist ein 
guter Mittelweg, denke ich. Den Versuch der Umsetzung ist ein Absatz 
höher aufgeführt....



>> Ich muss zugeben, dass ich bei den ganzen Pointern im Moment ziemlich
>
>> verwirrt bin.
>
> Das Gefühl habe ich auch. Du übernimmst dich. Du bist noch nicht soweit
>
> um das in den Griff zu kriegen.

Genau deswegen wende ich mich ja an euch und versuche zu lernen und das 
in den Griff zu bekommen.
Danke schon mal an alle an dieser Stelle für ihre Unterstützung!

von Klaus W. (mfgkw)


Lesenswert?

Offlineuser schrieb:
> Mit solchem Aufruf:
> if (_db_object[k].idx == 0x1002) {
>   _db_object[k].Function();  // funktion aufrufen
> }
> Sieht das plausibel aus?

So in etwa, ja.
Dabei muß aber noch __dummyFKT vor der ersten Verwendung deklariert
werden.

In deiner ersten Version hattest du jeweils einen Eintrag in
der struct weniger, indem du pROM mal als Zeiger auf Daten und mal
als Zeiger auf eine Funktion genutzt hattest.
Wenn sichergestellt ist, daß wirklich immer nur der eine oder
der andere Wert genutzt wird und du am Zusammenhang erkennen kannst,
welches der beiden jeweils drin steht, kann man das auch mit einem
Element erledigen. Dazu sollte man dann eine union aus dem
Funktionszeiger und dem Zeiger auf die Daten machen.
Solange der Platz aber nicht zu knapp ist im Programm, würde
ich aber nicht dazu raten. Solche Mehrfachnutzungen machen
das Programm nicht gerade übersichtlicher und dadurch
fehleranfälliger.

Abgesehen davon kommt mir das Programm auch nicht so ganz
elegant vor (das kann aber auch daran liegen, daß mnan nur
wenig davon sieht).
Z.B. würde ich (soweit man den Wert nicht braucht) nicht
ein Dummy-Feld oder eine Dummy-Funktion nehmen, sondern einfach
NULL. Dann darf man natürlich auch nicht darauf zugreifen, aber
es ist offenkundig, daß man den Wert nicht nutzen will.
Will man wirklich den Wert haben und nutzen, ist irgendwas
mit dummy im Namen eher eine schlechte Wahl.
Naja, nicht die einzige schlechte Wahl zur Zeit.

von Offlineuser (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> So in etwa, ja.
> Dabei muß aber noch __dummyFKT vor der ersten Verwendung deklariert
> werden.
Jo, das ist klar.

> In deiner ersten Version hattest du jeweils einen Eintrag in
> der struct weniger, indem du pROM mal als Zeiger auf Daten und mal
> als Zeiger auf eine Funktion genutzt hattest.
> Wenn sichergestellt ist, daß wirklich immer nur der eine oder
> der andere Wert genutzt wird und du am Zusammenhang erkennen kannst,
> welches der beiden jeweils drin steht, kann man das auch mit einem
> Element erledigen. Dazu sollte man dann eine union aus dem
> Funktionszeiger und dem Zeiger auf die Daten machen.
> Solange der Platz aber nicht zu knapp ist im Programm, würde
> ich aber nicht dazu raten. Solche Mehrfachnutzungen machen
> das Programm nicht gerade übersichtlicher und dadurch
> fehleranfälliger.
Ja, stimmt, das sehe ich mittlerweile genau so und habe die Tabelle nun 
um getrennte Variablen und Funktionszeiger erweitert.


> Abgesehen davon kommt mir das Programm auch nicht so ganz
> elegant vor (das kann aber auch daran liegen, daß mnan nur
> wenig davon sieht).
> Z.B. würde ich (soweit man den Wert nicht braucht) nicht
> ein Dummy-Feld oder eine Dummy-Funktion nehmen, sondern einfach
> NULL. Dann darf man natürlich auch nicht darauf zugreifen, aber
> es ist offenkundig, daß man den Wert nicht nutzen will.
> Will man wirklich den Wert haben und nutzen, ist irgendwas
> mit dummy im Namen eher eine schlechte Wahl.
> Naja, nicht die einzige schlechte Wahl zur Zeit.
Das mit Null hatte ich mir auch überlegt und man könnte natürlich auch 
den Pointer vor dem Zugriff auch auf NULL abfragen, außerdem hat die 
tabelle auch einen Eintrag der besagt was auszuführen ist. Naja, mal 
sehen.

Vielen Dank nochmals für eure Unterstützung!

Ich denke ich habe das nun soweit ausgeknobbelt, dass es einwandfrei 
funktioniert (zumindest die hier im Thread angesprochenen Funktionen). 
Und ich habe entsprechend ein kleines Testprogramm geschrieben, das man 
im Simulator laufen lassen kann um dies gegenzuchecken (im MPLAB IDE mit 
C30 Compiler funktioniert alles exact wie gewünscht). Für die 
Interessierten hier noch das Testprogramm:

main.c
1
#include <p30Fxxxx.h>
2
#include <stdio.h>
3
#include "canopen_defs.h"
4
5
6
int main (void)
7
{
8
  unsigned char  ucTest = 0;
9
  unsigned short  usTest = 0;
10
  unsigned long  ulTest = 0, ulTest2 = 0;
11
  
12
  unsigned char *pChar;
13
  
14
  _CANopen_DICT_Template localObject;
15
  
16
  for (;;)
17
  {
18
    //__asm__("NOP");
19
    
20
    // test, wie viele einträge hat die tabelle?
21
    ucTest = sizeof(_db_object)/sizeof(localObject);
22
23
    //---------------------------
24
    // aus dem speicher in can buffer schreiben
25
    //---------------------------
26
    // unsigned char    
27
    ucTest = (unsigned char)(*_db_object[1].pVar);
28
    
29
    // unsigned short, 2 byte
30
    pChar = _db_object[2].pVar;
31
    usTest = (unsigned short)(*pChar++);
32
    usTest += ((unsigned short)(*pChar))<<8;
33
    
34
    // unsigned long, 4 byte
35
    pChar = _db_object[0].pVar;
36
    ulTest =   (unsigned long)(*pChar++);
37
    ulTest += ((unsigned long)(*pChar++))<<8;
38
    ulTest += ((unsigned long)(*pChar++))<<16;
39
    ulTest += ((unsigned long)(*pChar))<<24;
40
    
41
    
42
    //---------------------------
43
    // aus dem Buffer etwas in den speicher schreiben
44
    //---------------------------
45
    // fiktiven CAN-Buffer füllen
46
    ulTest2 = 0x12345678;
47
    // vom CAN-Buffer in speicher holen, unsigned long
48
    pChar = _db_object[2].pVar;
49
    *pChar++ = ulTest2;
50
    *pChar++ = ulTest2>>8;
51
    *pChar++ = ulTest2>>16;
52
    *pChar = ulTest2>>24;
53
54
55
    //---------------------------
56
    // ein paar funktionen ausführen
57
    //---------------------------
58
    _db_object[0].pFkt();
59
    _db_object[3].pFkt();
60
    _db_object[7].pFkt();
61
  }
62
  
63
}
64
65
66
void CO_COBIDAccessEvent (void) {
67
}
68
69
void CO_GuardTimeAccessEvent (void) {
70
}
71
72
void CO_LifeFactorAccessEvent (void) {
73
}
74
75
void CO_HeartBeatAccessEvent (void) {
76
}
77
78
79
80
void __dummyFKT (void) {
81
}


canopen_defs.h
1
enum attributes          // Memory access type
2
{  //              ls
3
  NA      = 0x00,//0b00000000,  // Default, non-existant
4
  CONST    = 0x40,//0b01000000,  // Default, read only from ROM
5
  RW      = 0x60,//0b01100000,  // Default, read/write
6
  RO      = 0x40,//0b01000000,  // Default, read only
7
  WO      = 0x20 //0b00100000,  // Default, write only
8
};
9
  
10
enum types            // Variable type 
11
{
12
  u8    = 0x0F,//0b00001111,
13
  u16  = 0x0B,//0b00001011,
14
  u32  = 0x03,//0b00000011,
15
  fkt  = 0x80 //0b10000000    // funktionsaufruf
16
};
17
18
19
typedef void (*VoidFnct)( void );
20
21
typedef struct
22
{
23
  unsigned short    idx;    // index des eintrags
24
  unsigned char    sub;    // subindex des eintrags
25
  enum attributes  attr;    // attribute
26
  enum types      type;    // variablentyp
27
  unsigned char    *pVar;
28
  VoidFnct          pFkt;    //Function of Entry
29
  
30
} _CANopen_DICT_Template;
31
32
33
  
34
// CANopen object 0x1000
35
const unsigned long rCO_DevType =         0x8934AL;
36
37
// CANopen object 0x1008
38
const unsigned char rCO_DevName[] =       "Sens";
39
40
// CANopen object 0x1009
41
const unsigned char rCO_DevHardwareVer[] =   "V1.0";
42
43
// CANopen object 0x100A
44
const unsigned char rCO_DevSoftwareVer[] =   "V1.0";
45
46
// CANopen object 0x1018
47
const unsigned char rCO_DevIdentityIndx =   0x4;
48
const unsigned long rCO_DevVendorID =       0x12345678L;
49
const unsigned long rCO_DevProductCode =     0x555AAA11L;
50
const unsigned long rCO_DevRevNo =         0x10305070L;
51
const unsigned long rCO_DevSerialNo =       0x87654321L;
52
53
// CANopen object 0x1001
54
unsigned char uCO_DevErrReg;
55
56
// CANopen object 0x1002
57
unsigned long uCO_DevManufacturerStatReg;
58
59
60
unsigned char __dummy[4] = {0,0,0,0};
61
void __dummyFKT (void);
62
void CO_COBIDAccessEvent (void);
63
void CO_GuardTimeAccessEvent (void);
64
void CO_LifeFactorAccessEvent (void);
65
void CO_HeartBeatAccessEvent (void);
66
67
68
_CANopen_DICT_Template _db_object[] = {
69
  //idx    sub    attributen      typ    ptr auf variable                        funktion
70
  {0x1000,  0x00,  CONST,        u32,  (unsigned char *)&rCO_DevType,                  __dummyFKT},
71
  {0x1001,  0x00,  RO,          u8,    &uCO_DevErrReg,                          __dummyFKT},
72
  {0x1002,  0x00,  RO,          u32,  (unsigned char *)&uCO_DevManufacturerStatReg,          __dummyFKT},
73
  {0x1005,  0x00,  RW,        u32 | fkt,  (unsigned char *)&__dummy[0],                  CO_COBIDAccessEvent},
74
  {0x1008,  0x00,  CONST,        u32,  (unsigned char *)&rCO_DevName,                  __dummyFKT},
75
  {0x1009,  0x00,  CONST,        u32,  (unsigned char *)&rCO_DevHardwareVer,              __dummyFKT},
76
  {0x100A,  0x00,  CONST,        u32,  (unsigned char *)&rCO_DevSoftwareVer,              __dummyFKT},
77
  {0x100C,  0x00,  RW,        u16 | fkt,  (unsigned char *)&__dummy[0],                  CO_GuardTimeAccessEvent},
78
  {0x100D,  0x00,  RW,         u8 | fkt,  (unsigned char *)&__dummy[0],                  CO_LifeFactorAccessEvent},
79
  {0x1017,  0x00,  RW,        u16 | fkt,  (unsigned char *)&__dummy[0],                  CO_HeartBeatAccessEvent},
80
  {0x1018,  0x00,  CONST,        u8,    (unsigned char *)&rCO_DevIdentityIndx,              __dummyFKT},
81
  {0x1018,  0x01,  CONST,        u32,  (unsigned char *)&rCO_DevVendorID,                __dummyFKT},
82
  {0x1018,  0x02,  CONST,        u32,  (unsigned char *)&rCO_DevProductCode,              __dummyFKT},
83
  {0x1018,  0x03,  CONST,        u32,  (unsigned char *)&rCO_DevRevNo,                  __dummyFKT},    
84
  {0x1018,  0x04,  CONST,        u32,  (unsigned char *)&rCO_DevSerialNo,                __dummyFKT}
85
};

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.