Forum: Compiler & IDEs Probleme mit Initialisierung von Variablen mit Wert 0


von UBoot-Stocki (Gast)


Lesenswert?

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_t var=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

von Johannes M. (johnny-m)


Lesenswert?

Nur globale und statische Variablen werden bei Programmstart automatisch 
mit 0 initialisiert! Lokale Variablen haben keinen definierten 
Startwert.

von Simon K. (simon) Benutzerseite


Lesenswert?

Allerdings versteh ich nicht, welchen Nachteil es jetzt hat, globale 
Variablen explizit auf 0 zu initialisieren...

von Εrnst B. (ernst)


Lesenswert?

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
int test1=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
int test2;
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
int test3=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
1
int test3=0;
genauso wie
1
int test3;
zu behandeln.


Genaueres steht in der avr-libc FAQ:
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_varinit


/Ernst

von Simon K. (simon) Benutzerseite


Lesenswert?

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
> int test1=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
> int test2;
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
> int test3=0;
2
>
> genauso wie
>
1
> int test3;
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.)

von Εrnst B. (ernst)


Lesenswert?

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
void something(...) {
2
  static int counter=0;
3
  counter++;
4
  if (counter==42) ...
5
}

schaut halt auf den ersten Blick so aus, als würde die condition nie 
wahr...

/Ernst

von UBoot-Stocki (Gast)


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

> 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.

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.