Hi,
ich suche im Moment nach Möglichkeiten um meinen Code zu optimieren. Ein
Ansatzpunkt ist nach diesem Forum das Weglassen der Initialisierung von
Variablen mit 0, da das der Complier (gcc) automatisch macht.
Bei mir funktioniert das aber nicht! Ich habe in jeder Funktion am
Anfang die Definition von Variablen so gemacht:
1
uint8_tvar=0;
Nachden ich die Zuweisung auf Null rausgenommen habe, hat der Complier
diverse Warnings mit "...possible uninitialized use of .." geworfen. Und
mein Code hat nicht mehr funktioniert.
Mir ist also nicht klar, wie das mit dem Weglassen der Initialisierung
gemeint war ... ?
Bitte klärt mich mal auf...
Gruß
Andreas
Simon Küppers wrote:
> Allerdings versteh ich nicht, welchen Nachteil es jetzt hat, globale> Variablen explizit auf 0 zu initialisieren...
Auf dem AVR wird der Startwert der Variablen im Flash abgelegt, auf was
anderes hat der Compiler ja keinen Zugriff (Sonderfall EEProm mal außen
vorgelassen).
d.H. für eine Globale Variable
1
inttest1=42;
muss Code generiert werden, der nach dem Reset die entsprechenden
RAM-Zellen initialisiert.
(Diese variablen landen in der .data-Section)
Bei Variablen ohne Initialisierung, also
1
inttest2;
ist dieser Init-Code einfacher, die Variable landet in der .bss section,
nach einem Reset wird einfach die ganze Section mit 0x00 überschrieben.
Alte GCC-Versionen haben allerdings auch bei
1
inttest3=0;
die test3 Variable in die .data-Section gelegt, und den ungünstigeren
Init-Code dafür generiert.
AFAIK ist der aktuelle GCC aber schlau genug, eine Initialisierung auf
null einfach zu ignorieren, also
Ernst Bachmann wrote:
> Simon Küppers wrote:>> Allerdings versteh ich nicht, welchen Nachteil es jetzt hat, globale>> Variablen explizit auf 0 zu initialisieren...>> Auf dem AVR wird der Startwert der Variablen im Flash abgelegt, auf was> anderes hat der Compiler ja keinen Zugriff (Sonderfall EEProm mal außen> vorgelassen).
Nunja, das eine Byte (Okay, 2 oder 4 bei anderen Datentypen) stört im
Flash doch nicht wirklich.
> d.H. für eine Globale Variable>
1
>inttest1=42;
2
>
> muss Code generiert werden, der nach dem Reset die entsprechenden> RAM-Zellen initialisiert.
Ja, aber dieser Initialisierungscode wird auch ohne erzeugt...
> Bei Variablen ohne Initialisierung, also>
1
>inttest2;
2
>
> ist dieser Init-Code einfacher, die Variable landet in der .bss section,> nach einem Reset wird einfach die ganze Section mit 0x00 überschrieben.
Eben, ob der Speicherbereich nun aus einer Tabelle aus dem Flash
überschrieben wird, oder direkt mit null dürfte (beim Startup) nicht
wirklich viel ausmachen
> AFAIK ist der aktuelle GCC aber schlau genug, eine Initialisierung auf> null einfach zu ignorieren, also>
1
>inttest3=0;
2
>
> genauso wie>
1
>inttest3;
2
>
> zu behandeln.
Ja, aber durch das Weglassen des Initializers wird der Code
undurchsichtiger. Wenn der Programmierer eine statische Variable in
einer Funktion nicht initialisiert, würde ich erstmal dumm gucken, bis
ich auf die "Eigenart" (bzw. Norm) der Compiler stoße, dass diese die
Variable sowieso mit 0 initialisieren.
Hätte der Programmierer es sofort hingeschrieben, wärs (mir jedenfalls)
klarer gewesen.
> Genaueres steht in der avr-libc FAQ:> http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_varinit
Danke, werde ich mir mal anschauen.
> /Ernst
Edit: Nagut, ich stehe wirklich sehr alleine mit meiner Meinung... :|
1
Now if some programmer "wants to make doubly sure" their variables really
2
get a 0 at program startup, and adds an initializer just containing 0 on
3
the right-hand side, they waste space. While this waste of space applies to
4
virtually any platform C is implemented on, it's usually not noticeable on
5
larger machines like PCs, while the waste of flash ROM storage can be very
6
painful on a small microcontroller like the AVR.
7
8
So in general, variables should only be explicitly initialized if the
9
initial value is non-zero.
Keine Ahnung, aber irgendwie wehre ich mich gerade innerlich dagegen
(Ja, Programmieren hat für mich was mit Gefühlen zu tun ;). Wenn das
Programm nicht mit dem Willen des Geisters vom Programmierer
übereinstimmt, dann fühlt man sich schlecht.)
Ist ja eigentlich relativ egal, ich hab das nur einmal beim schreiben
eines Bootloaders gebraucht, der grad um ein paar Bytes nichtmehr in die
nächstkleinere Bootsektion gepasst hat.
Da hab ich dann allerdings gleich die crt0 komplett ohne Initialisierung
dazugelinkt, also auch das initialisieren der globals auf 0 weggelassen.
Wenns für den Programmablauf wichtig ist, dass eine Variable mit 0
startet, schreib ich das "=0;" auch dahinter, aber meistens wird die
Variable eh erst geschrieben, und dann gelesen. Hat für mich also eher
Dokumentationscharakter.
Nur bei statischen Variablen innerhalb von Funktionen störts mich etwas,
z.B.
1
voidsomething(...){
2
staticintcounter=0;
3
counter++;
4
if(counter==42)...
5
}
schaut halt auf den ersten Blick so aus, als würde die condition nie
wahr...
/Ernst
Aha...
Globale Variablen werden automatisch mit 0 initialisiert und lokale
nicht - Wo ist da die Logik ?
SRAM:
Sehe ich das richtig, dass meine lokalen Variablen nur temporär Platz im
SRAM belegen? D.h. sie werden bei jedem Aufruf der Funktion erzeugt und
nach dem "return" wieder vernichtet ?
Ich sollte also versuchen möglichst viele Variablen lokal zu nutzen auch
auf den Nachteil hin, dass der Compiler dann immer einen Ladecode
(Variableninitialisierung, Strings etc.) generiert, was Laufzeit kostet
? Interessant ist diese Überlegung ja für meine Strings aus dem Menü.
Wenn ich mir eine "Print"-Funktion schreibe, die die Strings als lokale
Variablen enthält, dann werden die Strings bei jedem Aufruf vom Flash
ins SRAM geladen. Am Ende der Funktion wird dann der Speicher wieder
freigegeben... ?! Srimmt das so ?
FLASH:
Wenn ich hier was sparen möchte, dann kommt wohl nur der Verlagern der
Strings in das EEPROM in Frage ... Ob das im Sinne des Erfinders ist ?
Gebt doch mal Feedback zu meinen Gedanken ....
Gruß
Andreas
UBoot-Stocki wrote:
> Aha...>> Globale Variablen werden automatisch mit 0 initialisiert und lokale> nicht - Wo ist da die Logik ?
Siehe einen Absatz weiter unten...
> SRAM:> Sehe ich das richtig, dass meine lokalen Variablen nur temporär Platz im> SRAM belegen? D.h. sie werden bei jedem Aufruf der Funktion erzeugt und> nach dem "return" wieder vernichtet ?
So ist es. "Vernichtet" in dem Sinne wird sie zwar nicht, aber sie
existiert für das Programm nicht mehr und der betroffene Speicherplatz
wird wieder freigegeben, während globale und statische Variablen das
ganze Programm über exisitieren. Wenn das System jede lokale Variable,
die in irgendeinem Unterprogramm ihr dasein fristet, bei jedem Aufruf
dieses Unterprogramms automatisch mit Null initialisieren würde, dann
führt das dazu, dass die Initialisierung auf Prozessor-Ebene jedes Mal
mit zusätzlichem Code verbunden wäre (und zwar eben automatisch, ohne
dass der Programmierer da irgendwas dran ändern könnte), denn es muss ja
der Müll, den das vorherige Unterprogramm in dem Speicher hinterlassen
hat, entfernt werden. Deshalb werden lokale Variablen eben nicht
automatisch initialisiert und man überlässt es dem Programmierer, zu
entscheiden, mit welchem Wert er die Variablen initialisiert. Wird die
Variable nicht explizit initialisiert, dann steht da u.U. noch das drin,
was das letzte Unterprogramm mit seinen lokalen Variablen da
hinterlassen hat.
> Ich sollte also versuchen möglichst viele Variablen lokal zu nutzen auch> auf den Nachteil hin, dass der Compiler dann immer einen Ladecode> (Variableninitialisierung, Strings etc.) generiert, was Laufzeit kostet> ?
Eben den muss er dann nicht selber generieren, da lokale Variablen eben
nicht automatisch initialisiert werden. Du kannst also in einem
Unterprogramm eine lokale Variable deklarieren und ihr irgendwann im
späteren Verlauf einen beliebigen Wert zuweisen, ohne dass der Compiler
da bei der Deklaration gleich schon irgendwas reinschreibt und so
zusätzlichen Code erzeugt.
> Interessant ist diese Überlegung ja für meine Strings aus dem Menü.> Wenn ich mir eine "Print"-Funktion schreibe, die die Strings als lokale> Variablen enthält, dann werden die Strings bei jedem Aufruf vom Flash> ins SRAM geladen. Am Ende der Funktion wird dann der Speicher wieder> freigegeben... ?! Srimmt das so ?
Das stimmt so. Da wäre allerdings wirtlich die Frage angebracht, ob das
Laden der Strings ins SRAM überhaupt Sinn macht. Wenn die Strings
konstant sind (und auch bleiben), dann lass sie im Flash und greife über
die Adresse darauf zu. Konstante Werte im SRAM sind i.d.R. ziemlich
sinnfrei.
> FLASH:> Wenn ich hier was sparen möchte, dann kommt wohl nur der Verlagern der> Strings in das EEPROM in Frage ... Ob das im Sinne des Erfinders ist ?
Wenn es sich um einen AVR handelt, dann ist das höchstens vom Platz im
Flash her interessant. Allerdings solltest Du Dir dann vielleicht mal
anschauen, wie der Zugriff aufs EEPROM durchgeführt wird. Der ist
nämlich mit etwas speziellerem Code verbunden, da das EEPROM im
Unterschied zu Flash und RAM nicht direkt als Speicher angesprochen
werden kann, sondern als Peripherie-Einheit über I/O-Register angebunden
ist. Dadurch handelst Du Dir beim Zugriff zusätzlichen Code und außerdem
Zeitverluste ein.
> Globale Variablen werden automatisch mit 0 initialisiert und lokale> nicht - Wo ist da die Logik ?
Bei globalen Variablen wird einfach beim Programmstart einmal alles mit
0 gefüllt, das nicht explizit initialisiert wird. Bei lokalen Variablem
nüßte das für diese bei jedem Aufruf der Funktion aufs neue gemacht
werden.
> Sehe ich das richtig, dass meine lokalen Variablen nur temporär Platz> im SRAM belegen? D.h. sie werden bei jedem Aufruf der Funktion erzeugt> und nach dem "return" wieder vernichtet ?
Ja, außer wenn sie static sind. Dann verhalten sie sich bis auf die
eingeschränkte Sichtbarkeit wie globale Variablen.
> Ich sollte also versuchen möglichst viele Variablen lokal zu nutzen> auch auf den Nachteil hin, dass der Compiler dann immer einen Ladecode> (Variableninitialisierung, Strings etc.) generiert, was Laufzeit kostet> ?
Der "Ladecode" besteht eigentlich aus nicht viel mehr als einer
Addition, außer eben wenn die Variablen initialisiert werden.
> Interessant ist diese Überlegung ja für meine Strings aus dem Menü.> Wenn ich mir eine "Print"-Funktion schreibe, die die Strings als lokale> Variablen enthält, dann werden die Strings bei jedem Aufruf vom Flash> ins SRAM geladen.
Sie werden einmal beim Start des Programms vom Flash ins RAM kopiert. Ob
sie bei jedem Funktionsaufruf nochmal kopiert werden, kommt drauf an,
wie du die Variablen definierst. Das "string literal" (Das was in den
Anführungszeichen steht) hat statische Lebensdauer. Wenn du einen Zeiger
darauf als lokale Variable anlegst (const char* c = "Hello World"), wird
nur der Zeiger einmal pro Funktionsaufruf erzeugt und zerstört. Nur wenn
du es als Array anlegst (char c[] = "Hello World"), wird kopiert.
> FLASH:> Wenn ich hier was sparen möchte, dann kommt wohl nur der Verlagern der> Strings in das EEPROM in Frage ... Ob das im Sinne des Erfinders ist ?
In der Regel hat man ja deutlich mehr Platz im Flash als im EEPROM. Das
EEPROM würde ich außerdem eher für Daten nutzen, die sich im laufenden
Programm ändern können und die persistent gespeichert werden müssen.