Forum: Compiler & IDEs Warum verbraucht eine Variable so viel Speicher?


von Michael D. (etzen_michi)


Angehängte Dateien:

Lesenswert?

Ich bin grad ein wenig am Rumspielen (Learning by doing) bezüglich C.

Nun habe ich mir ein kleines Programm geschrieben und mir ist 
aufgefallen das dieses mit einem festen Wert (define) ca. 12% des 
Speichers eines ATmega8 und mit einer Variablen (uint8_t) ca. 80% des 
Speichers verbraucht. Warum das?

Ich nutze AVR Studio 4.16
Der Speicherverbrauch ist auf allen Optimierungsstufen nahezu gleich 
(ausser Q0).
1
#include "files.h"
2
3
//#define Blinkgeschwindigkeit 100
4
uint8_t Blinkgeschwindigkeit=100;
5
uint8_t temp=0;
6
7
int main () {
8
9
Konfiguration();
10
SetColor(Gruen);
11
12
while(1)    {
13
if(temp>=7) {
14
    temp=0;
15
    }
16
temp++;
17
SetColor(temp);
18
_delay_ms(Blinkgeschwindigkeit);
19
20
21
    }
22
23
24
}

Noch eine Frage OT nebenbei: Was ist der unterschied zwischen "uint" und 
"uint8_t"?

von blablub (Gast)


Lesenswert?

Das ist der Klassiker, kommt jede Woche >=1 Mal...

_delay_ms + Variable --> float-Library wird eingebunden, Programm wird 
riesig und die Zeiten stimmen auch nicht mehr. Frag mal die Suche.

uint = unsigned int (Größe kann alles sein, ich type mal auf "standard 
int" = 8 Bit auf nem AVR bzw. 32 auf nem PC)
uint8 = unsigned char (8 Bit)

von blablub (Gast)


Lesenswert?

Ach und "_t" ist dann ein typedef, ist einfach ne Konvention.

Und "learning by doing" ist bei C so eine Sache...

von Floh (Gast)


Lesenswert?

> 8 Bit auf nem AVR
Nope, sind 16 bit auf nem AVR.
:-)

Das Problem wurde schon genannt, die Funktion delay darf nicht mit 
Variablen aufgerufen werden. Dann lieber so:
1
void delay_var(int t)
2
{
3
  for(;t;t--)
4
    _delay_ms(1);
5
}

von blablub (Gast)


Lesenswert?

Floh schrieb:
>> 8 Bit auf nem AVR
> Nope, sind 16 bit auf nem AVR.
> :-)
Argh, stimmt, da war doch neulich erst was von wegen -mint8 oder so. 
Naja, ich hab seit Monaten keinen mehr angefasst, gilt das als 
Entschuldigung? :-)

> Das Problem wurde schon genannt, die Funktion delay darf nicht mit
> Variablen aufgerufen werden. Dann lieber so:
>
>
1
> void delay_var(int t)
2
> {
3
>   for(;t;t--)
4
>     _delay_ms(1);
5
> }
6
>
Jenau!

von Mark B. (markbrandis)


Lesenswert?

blablub schrieb:
> _delay_ms + Variable --> float-Library wird eingebunden

Hm, was ist eigentlich der Sinn dabei?

von Michael D. (etzen_michi)


Lesenswert?

Hätte die Suche ja mal bemühen können; das ich nichts gefunden hätte 
(habe grad mal bissle gesucht) is nu auch keine Entschuldigung mehr.

Vielen Dank für die Info und den Lösungsvorschlag.


Bezüglich Learning by doing mache ich das so dass ich mich durch ein C 
Tutorial für den PC durcharbeite und immer wieder kleine Programme für 
den AVR schreibe um mich mit den Funktionen und Realisierungen vertraut 
zu machen. (Wie muss was genau geschrieben werden, wie kann ich welches 
Problem in C lösen ....)

von Karl H. (kbuchegg)


Lesenswert?

Mark Brandis schrieb:
> blablub schrieb:
>> _delay_ms + Variable --> float-Library wird eingebunden
>
> Hm, was ist eigentlich der Sinn dabei?

Dass du auch 2.8 Millisekunden warten lassen kannst.

Ist der Optimizer eingeschaltet und ist das Argument eine Konstante, 
dann macht der Compiler die Berechnung für die Anzahl der 
Schleifendurchläufe, so dass der _delay_ms tatsächlich 2.8 Millisekunden 
dauert. Und optimiert dabei dann auch gleich den Einsatz der Floating 
Point Library wieder weg.

von Mark B. (markbrandis)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dass du auch 2.8 Millisekunden warten lassen kannst.
>
> Ist der Optimizer eingeschaltet und ist das Argument eine Konstante,
> dann macht der Compiler die Berechnung für die Anzahl der
> Schleifendurchläufe, so dass der _delay_ms tatsächlich 2.8 Millisekunden
> dauert. Und optimiert dabei dann auch gleich den Einsatz der Floating
> Point Library wieder weg.

Ja gut, nur dass eine Integer-Variable einen Wert wie 2.8 niemals 
annehmen kann und das Einbinden der float-Library daher in diesem Fall 
sinnlos ist, das sollte der Compiler/Linker an und pfirsich schon 
erkennen können. :-)

von Edi R. (edi_r)


Lesenswert?

Mark Brandis schrieb:
> Ja gut, nur dass eine Integer-Variable einen Wert wie 2.8 niemals
> annehmen kann

Es soll ja auch keine Integer-Variable sein, sondern eine Konstante - 
und zwar durchaus auch eine Fließkomma-Konstante. Trotz 
Fließkomma-Konstante wird dann die Float-Lib nicht eingebunden.

_delay_ms(2.8) geht also.

von citb (Gast)


Lesenswert?

Mark Brandis schrieb:
> a gut, nur dass eine Integer-Variable einen Wert wie 2.8 niemals
> annehmen kann und das

Vielleicht erkennt der das auch und nimmt deswegen float...?

citb

von citb (Gast)


Lesenswert?

Wieder was gelernt.
Der Tag wird gut!

citb

von Martin (Gast)


Lesenswert?

blablub schrieb:
> Ach und "_t" ist dann ein typedef, ist einfach ne Konvention.

Nein, uint8_t ist ein Typ, den der Compiler bereitstellen muß. Wie er 
das macht (per typedef oder nicht), ist seine Sache.

von Michael (Gast)


Lesenswert?

blablub schrieb:
> Ach und "_t" ist dann ein typedef, ist einfach ne Konvention.

Keine Konvention, sondern Typen, die in stdint.h deklariert sind, z.B.
1
typedef unsigned char uint8_t;

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


Lesenswert?

Michael schrieb:
> blablub schrieb:
>> Ach und "_t" ist dann ein typedef, ist einfach ne Konvention.
>
> Keine Konvention, sondern Typen, die in stdint.h deklariert sind, z.B.
>
1
typedef unsigned char uint8_t;

In diesem Falle: ja.  Aber auch sonst ist das Anhängen von _t an
Typnamen eine gängigen Konvention, die man gut und gern auch für
selbstdefinierte Typen benutzen kann.  Vorteil dabei ist, dass nicht
nur der Mensch sofort erkennen kann, dass es sich um eine
Typdefinition handelt, sondern dass es auch diverse Editoren für das
Syntaxhighlighting erkennen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mark Brandis schrieb:
> Ja gut, nur dass eine Integer-Variable einen Wert wie 2.8 niemals
> annehmen kann und das Einbinden der float-Library daher in diesem Fall
> sinnlos ist, das sollte der Compiler/Linker an und pfirsich schon
> erkennen können. :-)

Wie soll denn ein Compiler erkennen, daß ein Programm sinnvoll ist oder 
unsinnig? Es kann durchaus sinnvoll sein, eine Int-Variable durch eine 
float-Berechnung zu erhalten.

Ein Compiler oder Tools wie Lint können zwar fragwürdige Konstrukte 
anmeckern, aber es wird niemals Tools geben können, sie "sinnvolle" von 
"sinnlosen" Programmen unterscheiden osder die Intention es 
Programmierers erahnen. Und selbst wenn es sowas mal geben sollte — bis 
dahin gilt "what you get is what you request".

von Mark B. (markbrandis)


Lesenswert?

Dass die Funktion _delay_ms() überhaupt anscheinend float oder double 
als Argument akzeptiert, ist meiner Meinung nach nicht sinnvoll. Für den 
Fall, dass jemand tatsächlich mal 2,8 Millisekunden warten wollte, gibt 
es auch Festkommaarithmetik (dann z.B. mit einer Funktion 
_delay_microseconds() oder ähnlichem), die wird doch auch und gerade 
hier im Forum gerne gepredigt - zu Recht. :-)

So intelligent darf ein Build-System für Mikrocontroller dann schon 
sein, dass es auf einer Zielhardware mit knappem Speicher nicht einfach 
mal eben so unnötig Bibliotheken einbindet. Wenn Ressourcen knapp sind, 
kann oder sollte man nicht verschwenderisch sein.

von Karl H. (kbuchegg)


Lesenswert?

Mark Brandis schrieb:
> Dass die Funktion _delay_ms() überhaupt anscheinend float oder double
> als Argument akzeptiert, ist meiner Meinung nach nicht sinnvoll.

Warum nicht?
Was ist so schlimm daran, wenn jamend wirklich 2.8ms warten lassen will.

> Für den
> Fall, dass jemand tatsächlich mal 2,8 Millisekunden warten wollte, gibt
> es auch Festkommaarithmetik (dann z.B. mit einer Funktion
> _delay_microseconds() oder ähnlichem), die wird doch auch und gerade
> hier im Forum gerne gepredigt - zu Recht. :-)

Natürlich.
Aber der springende Punkt ist doch: Von der Floating Point Arithmetik 
bleibt im Endeffekt nichts übrig. Wenn man es richtig macht. Und wie es 
richtig ist, ist ausreichend dokumentiert. Ob der Compiler zur 
Berechnung der Wiederholungen Floating Point benutzt oder Festkomma ist 
mir sowas von egal. Von mir aus kann der rechnen bis er schwarz wird, 
mich interessiert das Compilat das hinten raus kommt.

Wem das nicht gefällt, der kann immer noch eine Version mit 
Festkommaarithmetik hochziehen. Das Problem ist in beiden Fällen das 
gleiche: Halbwegs genaue kleine Zeiten kriegst du nur, wenn die 
Berechnung der Wiederholungen selbst keine Zeit kostet. Also muss da der 
Optimizer ran, um die Berechnung wegzuoptimieren. Da kannst du 
genausogut Floating Point Arithmetik nehmen.

Jedes Werkzeug ist immer nur so gut, wei derjenige, der es bedient. Die 
zu beachtenden Punkte bei _delay_ms sind alle gut dokumentiert und es 
ist nicht weiter schwer, diese Punkte einzuhalten und zu beherzigen. Nur 
kennen muss man sie. Und da sind wir dann wieder bei dem 
Programmier-Hauptthema hier im Forum: Doku gelesen wird nur im absoluten 
Notfall.

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


Lesenswert?

Ich habe die Gleitkommaarithmetik da reingebracht ;-), und meine
Argumente sind ansonsten natürlich auch die, die Karl Heinz hier schon
gebracht hat: wer die Voraussetzung, dass diese Funktionen nur mit
Optimierung Sinn haben, nicht verstanden hat, dem ist eigentlich nicht
zu helfen.

Der Computer soll für den Menschen da sein und nicht umgekehrt.  Wenn
ich 2,8 ms warten will, dann soll sich bitte der Computer den nicht
vorhandenen Kopf zerbrechen, statt dass ich zuvor den Taschenrechner
zücken muss, um das in eine "computergerechte" Form zu bringen.
_delay_us und _delay_ms sind ja "convenience functions", und damit sie
dem gerecht werden, sollen sie bitteschön auch "convenient" zu
benutzen sein.  Gleitkommazahlen sind dem, wie der Mensch denkt,
einfach ein Stück näher.

Ansonsten sind wir bei dem, was wir davor schon hatten, und bei dem
die Nutzer immer wieder genervt haben, wie sie denn nun eigentlich die
entsprechende Konstante ausrechnen sollen: _delay_loop_1 und
_delay_loop_2.

Es gibt übrigens einen weiteren Grund für mich, dass man dort
Gleitkommazahlen benutzen kann: wenn ich einen Quarz habe, auf dem
"7,37 MHz" steht, dann möchte ich diese Zahl in meinen Quelltext
übernehmen können, ohne erst drüber nachzudenken, wie viele Nullen ich
jetzt anhängen muss, damit die Freunde der "Gleitkommazahlen sind
übel"-Fraktion befriedigt werden.  Daher kann man dann

#define F_CPU 7.37E6

schreiben.  (Leider hat der Autor des zweiten Nutzers von F_CPU,
<util/setbaud.h> das nicht geschnallt: wenn man deren Funktionen
nutzen will, muss F_CPU eine ganzzahlige Konstante sein.)

von Mark B. (markbrandis)


Lesenswert?

Jörg Wunsch schrieb:
> Der Computer soll für den Menschen da sein und nicht umgekehrt.

Das freilich ist dann nicht gegeben, wenn der Computer den 
Flash-Speicher so mit Library-Code vollhaut dass der Mensch es dann eben 
doch wieder zurechtbiegen muss, damit genügend Platz für das eigentliche 
Programm bleibt. ;-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mark Brandis schrieb:
> Jörg Wunsch schrieb:
>> Der Computer soll für den Menschen da sein und nicht umgekehrt.
>
> Das freilich ist dann nicht gegeben, wenn ...

... man keine Dokumentation liest.

von Mark B. (markbrandis)


Lesenswert?

Johann L. schrieb:
>> Das freilich ist dann nicht gegeben, wenn ...
>
> ... man keine Dokumentation liest.

Jein. Einerseits hast Du recht, andererseits darf eine Sache durchaus 
auch so sein wie man es intuitiv erwarten würde. Wenn ich nirgendwo in 
meinem Code Gleitkommavariablen verwende, dann erwarte ich rein logisch 
betrachtet zunächst einmal nicht, dass das Build-System trotzdem die 
Gleitkomma-Bibliothek hinzulinkt.

Dass viele Benutzer darüber stolpern, ist doch der beste Beweis dafür, 
dass es so wie es ist eben nicht optimal designed ist, bzw. zumindest 
widerspricht es der normalen Intuition des Durchschnitts-Users.

von (prx) A. K. (prx)


Lesenswert?

Eine von dir vielleicht "intuitiv" genannte Programmierung von Delays 
kriegst du bei AVRs nur mit dem, was Microchip dafür zur Verfügung 
stellt. Nämlich Delay-Routinen, die eine wählbare Anzahl Taktzyklen 
warten, über verschiedenen Funktionen mit Zehnerpotenzen skaliert. Das 
ist nämlich die einzige Variante, die völlig ohne Umrechnung auskommt.

Leider ist dieses Verfahren grässlich frequenzanbhängig und dadurch sehr 
benutzerunfreundlich. Sobald du absolute Zeiten angeben willst und damit 
die Taktfreqenz in eine Umrechnung eingeht, wird die Sache schwierig. 
Wie ebenfalls viele Leute bereits bewiesen haben, die sich über seltsame 
Delays bei _delay_us(n) mit nicht-konstantem n wunderten.

Ob du dir bei falscher Parametrisierung nun an unpassendster Stelle 
Fliesskommarechnung reinziehst, oder 32/64-Bit Integerrechnung, ist 
letztlich schnuppe, weil beides gleich falsch. Aber bei 
Fliesskommarechnung stehen die Chancen besser, dass du es frühzeitig 
merkst.

Hat man eine schnelle Multiplikation, dann kann man Delayroutinen auch 
so schreiben, dass diese Umrechnung an kritischer Stelle stattfindet, 
ohne das Delay dabei nennenswert zu beeinflussen. Das ist beispielsweise 
bei ARMs möglich. Bei AVRs aber mangels schneller Multiplikation nicht, 
jedenfalls nicht mit allen Modellen und in grossem Wertebereich.

Vorschlag: Die Delay-Routinen werden mit einem #if geschützt, dessen 
zugehöriges #define vor dem #include selbst einzügen muss, analog zum 
Takt. Tut man das nicht, dann exististieren sie nicht. Sinnvoller Name 
wäre beispielsweise
1
  #define ICH_HABE_DIE_DELAY_DOKU_GELESEN_UND_BESTAETIGE_HIERMIT_DASS_ICH_WEISS_WAS_ICH TUE

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


Lesenswert?

A. K. schrieb:
> Eine von dir vielleicht "intuitiv" genannte Programmierung von Delays
> kriegst du bei AVRs nur mit dem, was Microchip dafür zur Verfügung
> stellt. Nämlich Delay-Routinen, die eine wählbare Anzahl Taktzyklen
> warten, über verschiedenen Funktionen mit Zehnerpotenzen skaliert. Das
> ist nämlich die einzige Variante, die völlig ohne Umrechnung auskommt.

Yep, das gab's in der avr-libc ja schon immer, nennt sich dort
_delay_loop_1 und _delay_loop_2.  _delay_us und _delay_ms sind nur
deshalb gebaut worden, weil das den Nutzern zu umständlich war.

Ansonsten löst sich die Diskussion eigentlich, wenn man wenigstens die
Compilerwarnungen ansieht, gerade in Wohlgefallen auf.  Man nehme
folgenden Code, der die beiden typischen Fehler beinhaltet, die man
in diesem Zusammenhang machen kann (Optimierung abgeschaltet bzw.
keine Konstante übergeben):
1
#define F_CPU 3000000
2
#include <util/delay.h>
3
4
void
5
delay_100(void)
6
{
7
  _delay_ms(100);
8
}
9
10
void
11
delay(int ms)
12
{
13
  _delay_ms(ms);
14
}

Compiliere ich ihn nun ohne Optimierung, gibt's eine Warnung:
1
/usr/lib/gcc/avr/4.5.1/../../../../avr/include/util/delay.h:94:3: warning: #warning "Compiler optimizations disabled; functions from <util/delay.h> won't work as designed"

Wer sowohl Warnungen als auch Doku ignoriert, ich glaube, dem ist
nach menschlichem Ermessen nicht mehr zu helfen.

Schalte ich die Optimierung ein, dann bekomme ich:
1
/usr/lib/gcc/avr/4.5.1/../../../../avr/include/util/delay.h: In function 'delay':
2
/usr/lib/gcc/avr/4.5.1/../../../../avr/include/util/delay.h:152:28: error: __builtin_avr_delay_cycles expects an integer constant.

von Mark B. (markbrandis)


Lesenswert?

Jörg Wunsch schrieb:
> Wer sowohl Warnungen als auch Doku ignoriert, ich glaube, dem ist
> nach menschlichem Ermessen nicht mehr zu helfen.

Da stimme ich zu, weiß aber auch wie die Realität aussieht - wenn man 
fünfzehen Jahre alte Warnings im Code sieht, um die sich nie jemand 
gekümmert hat... es passiert leider. :-(

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mark Brandis schrieb:
> Jörg Wunsch schrieb:
>> Wer sowohl Warnungen als auch Doku ignoriert, ich glaube, dem ist
>> nach menschlichem Ermessen nicht mehr zu helfen.
>
> Da stimme ich zu, weiß aber auch wie die Realität aussieht - wenn man
> fünfzehen Jahre alte Warnings im Code sieht, um die sich nie jemand
> gekümmert hat... es passiert leider.

Du kümmerst dich seit 15 Jahren nicht um deine Warnings und verlangst 
stattdessen, daß die Tools per Intuition erahnen, ob du wirklich das 
willst was hingeschrieben steht oder doch was anderes? Vielleich keine 
exakten Delays und Rundung anstatt exakter Berechnung? Oder doch nicht?

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


Lesenswert?

Davon abgesehen: die Warnung bekommt man ja nur, wenn man nicht
optimiert.  Wer ein seit 15 Jahren in Produktion befindliches Projekt
laufen hat und dort auf einem AVR ohne Optimierung arbeitet, nun, der
hat irgendwie ganz andere Probleme. ;-)

von Mark B. (markbrandis)


Lesenswert?

Johann L. schrieb:
> Du kümmerst dich seit 15 Jahren nicht um deine Warnings und verlangst
> stattdessen, daß die Tools per Intuition erahnen, ob du wirklich das
> willst was hingeschrieben steht oder doch was anderes? Vielleich keine
> exakten Delays und Rundung anstatt exakter Berechnung? Oder doch nicht?

Wer lesen kann, ist klar im Vorteil: Es gibt Warnungen in 15 Jahre altem 
Code, der nicht von mir stammt. Die Leute, die den geschrieben haben, 
sind auch längst nicht mehr da - so wie es halt immer ist.

von Mark B. (markbrandis)


Lesenswert?

Und bevor die nächste dumme Frage kommt: Ja, die Warnungen die man 
einfach entfernen kann (z.B. nicht mehr benutzte lokale Variablen) habe 
ich entfernt. Manchmal steckt hinter einer Warnung aber auch ein 
logischer Fehler - ein (sinngemäßes) Beispiel:

1
a==b;
2
warning: statement with no effect

und da man nicht die Intention des ursprünglichen Programmierers kennt, 
ist es gar nicht so einfach zu entscheiden was man macht: Den Code an 
der Stelle so lassen wie er ist? Unschön, weil er im Grunde genommen 
fehlerhaft ist. Den Code ändern? Tja, nur wie, wenn man nicht weiß wie 
er eigentlich hätte sein sollen, und keine Requirements/Dokumentation 
aus dieser Zeit existieren in denen man nachschauen könnte. Wenn man in 
dem Beispiel den Vergleich in eine Zuweisung ändert, funktioniert die 
Software vielleicht nicht mehr so wie sie soll...

Es ist schon ein Kreuz mit dem Legacy Code. :-(

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


Lesenswert?

Nun, für das gezeigte Statement würde ich mir den erzeugten Code
ansehen: wenn, wie erwartet, der Compiler das statement with no
effect ohnehin wegwirft und das Ergebnis trotzdem funktioniert,
dann hätte ich auch keine Bauchschmerzen, das rauszuschmeißen.
(Ein VCS werdet ihr ja haben, das die Historie dann entsprechend
dokumentiert.)

von Karl H. (kbuchegg)


Lesenswert?

In diesem konkreten Fall:

Rausschmeissen würde ich es nicht unbedingt.
Aber zumindest auskommentieren, mit einem Kommentar dazu warum das 
entfernt wurde (weil es eben sowieso keinen Effekt hat)


Das kommt schon mal vor, dass man Fehler findet, die seit Anbeginn der 
Welt im Code drinnen sind, wenn auch selten. (Persönlicher Rekord: ~14 
Jahre)

D.h. Falls dieser Vergleich ein Tippfehler ist und eigentlich eine 
Zuweisung hätte sein sollen, dann halte mich mir mit Auskommentieren 
zumindest das Wissen im Code, dass in dieser Stelle ein zweifelhaftes 
Statement stand. So wird nicht vergessen, dass da möglicherweise eine 
Zuweisung fehlt - was bis einfach nur noch nie jemandem aufgefallen ist, 
bzw. anscheinend keine Auswirkungen hat.
Ich hab auch schon Code gesehen, der ziemlich offensichtlich keinen 
Zweck erfüllt und wenn man dann mit dem Programmierer spricht, kommt man 
drauf, dass er sich den nur eingebaut hat, damit er eine Position hat, 
an die er einen Breakpoint platzieren kann und er vergessen hat, den 
Code wieder rauszunehmen.

Wenn die Dinge nicht so klar sind, bleibt nur: analysieren was die 
Absicht gewesen sein könnte und dann entscheiden was zu tun ist.

Im absoluten Zweifelsfall den Code so umändern, dass zwar die Warnung 
verschwindet, aber der Compiler die Sequenz immer noch gleich übersetzt.

Noch in jeder Firma, in der ich bisher war galt die Devise: Compiliert 
wird mit einem sehr hohen Warning Level und Warnungen werden als Fehler 
betrachtet. Code der nicht fehlerfrei compiliert, darf unter Androhung 
von hohen Strafen auf keinen Fall in die Source Code Verwaltung 
eingecheckt werden.

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.