Forum: Compiler & IDEs Variableninitialisierung


von olaf (Gast)


Lesenswert?

Mir ist gerade etwas interessantes aufgefallen das ich mal zur 
Diskussion stellen wollte.

Mein Compiler: gcc-Version 4.7.4 (GCC)
Controller:    GiantGecko
StartupCode:   startup_efm32gg.S

Wir alle verwenden globale Variablen ja selten. Aber gelegentlich kommt 
es ja mal vor das dies sinnvoll ist. Und noch seltener kommt es vor das 
es sinnvoll sein kann das eine Variable beim start einen bestimmten Wert 
hat.

Ich habe hier folgendes Programmiert:

#define SYSTEMREADY 4711
#define SYSTEMSTOP  0
volatile static int resetval=SYSTEMSTOP;

Das fuehrte zu folgendem interessanten verhalten. Mein Programm 
funktioniert beim einschalten des Microcontrollers, aber nach einem 
Reset oder beim flashen ueber den Debuger nicht mehr weil der 
Startupcode die Variablen nicht initialisiert.

Das entsprach schon mal so gar nicht meiner Erwartung, was aber wohl 
daran lag das ich bisher meine Startcodes immer selber geschrieben habe.

Also mal kurz den Code gelesen. In der Tat es gibt da ein Define.
nimmt man das in seine CFLAGS auf "-D__NO_SYSTEM_INIT=1" so wird 
initialisiert. Allerdings mit einer interessanten Ausnahme. 
Initialisiert man mit 0 wie oben wird immer noch nicht initialisiert.
Erst wenn man es so macht:

#define SYSTEMREADY 4711
#define SYSTEMSTOP  0xdead
volatile static int resetval=SYSTEMSTOP;

..bekommt man das erwartete Verhalten.

Hab ich diese charmante Eigenschaft in den letzten 30Jahren 
C-Programmierung uebersehen? Oder ist das eine spezielle Eigenart von 
ARM oder Silabs?
Haettet ihr das gewusst? :-)

Olaf

von A. S. (Gast)


Lesenswert?

Also mit 0 ist der Wert beim Start ungleich 0?... Bist Du sicher? Oder 
kennst Du den Unterschied nicht?

von W.S. (Gast)


Lesenswert?

olaf schrieb:
> Wir alle verwenden globale Variablen ja selten. Aber gelegentlich kommt
> es ja mal vor das dies sinnvoll ist.
> ...
> Ich habe hier folgendes Programmiert:...

Aha.
Wohl jeder von uns verwendet gelegentlich globale Variablen. Sowas ist 
schlichtweg die Normalität - und du brauchst dich dafür nicht verschämt 
zu verstecken.

Aber was du da ("hier") an "Folgendem" programmiert hast, ist zumindest 
mir unverständlich. Wo finden denn deine zwei Zahlen 0 und 4711 
tatsächlich eine Verwendung? Ansonsten krieg ich bei sowas:
"volatile static int resetval=SYSTEMSTOP;"
regelmäßig nen dicken Hals.

Mein wirklich ernstgemeinter Rat ist:
Erstens:
Dir den tatsächlich in Verwendung befindlichen Startupcode mal kritisch 
anzuschauen.

Zweitens:
Dich nie und nimmer auf als selbstverständlich angenommene 
Initialisierungen zu verlassen und deine Variablen vor ihrer Verwendung 
dediziert selbst zu initialisieren. Das gilt sowohl für globale als auch 
für lokale Variablen. Den immer am den Spruch von Max (Sam&Max): "ich 
traue ihm nur soweit, wie ich ihn selber schmeißen kann". Das gilt für 
alle Variablen gleichermaßen.

Drittens:
Das Wort "static" zu vermeiden - egal wo.

Viertens:
Immer Deklarationen und Belegungen zu trennen. Also nicht wie oben 
schreiben, sondern sinngemäß so:
1
extern volatile int IrgendwasIrgendwo;
2
...
3
 IrgendwasIrgendwo = 47110815;

W.S.

von Olly T. (twinpeaks)


Lesenswert?

Hört sich an, als würde der Startup-Code das .BSS Segment nicht nullen, 
in denen der Compiler nichtinitialisierte globale Variablen (und 
explizit auf Null gesetzte) unterbringt.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Mit Verlaub, Du hast schon eine seltsame Art, Fragen zu stellen. 
Vielleicht bin ich ja zu blöd, aber ich musste deinen Text tatsächlich 
mehrmals lesen, bis ich verstanden habe, worum's dir eigentlich geht.

Aber erstmal die Antwort auf deine Frage: ja, hätten wir gewusst.

Und dann dazu, was Du (glaube ich zumindest) meinst:

Der Startup-Code gehört mit zum Programm. Und egal, ob man den selber 
schreibt, sich irgendwo zusammenklaut oder was Fertiges benutzt, man 
sollte in jedem Fall wenigstens halbwegs wissen, was er macht. 
Anscheinend wollte der Schreiber deines Startup-Codes zwischen Kaltstart 
und Reset unterschieden haben. Kann man machen, muss man aber nicht.

Das Minimum, was der Startup-Code machen muss, ist das .data-Segment ins 
RAM kopieren und das .bss-Segment löschen.

Der Compiler steckt alles was != 0 initialisiert wird, ins 
.data-Segment, im .bss-Segment landet der Rest (es sei denn, Du hast 
zusätzlich noch eigene Segmente).

Das ist eigentlich schon alles, was Du wissen musst, um das Verhalten zu 
verstehen.

von Markus F. (mfro)


Lesenswert?

W.S. schrieb:
> Drittens:
> Das Wort "static" zu vermeiden - egal wo.
>
> Viertens:
> Immer Deklarationen und Belegungen zu trennen. Also nicht wie oben
> schreiben, sondern sinngemäß so:extern volatile int IrgendwasIrgendwo;
> ...
>  IrgendwasIrgendwo = 47110815;

Erstens und zweitens würde ich noch durchgehen lassen, aber drittens und 
viertens ist Quatsch.

static verwendet man genau dort (wie alle anderen Sprachelemente von C 
auch), wo es sinnvoll ist. Das geht natürlich nur (aber das gilt 
wiederum nicht nur für C, sondern auch für alle anderen 
Programmiersprachen und überhaupt alle Werkzeuge im Universum), wenn man 
auch verstanden hat, wozu es gut ist und wie man damit umgeht.

Deklaration und Initialisierung trennt nur der dogmatisch, der seinem 
Startup-Code nicht traut.

von olaf (Gast)


Lesenswert?

> Das Minimum, was der Startup-Code machen muss, ist das .data-Segment ins
> RAM kopieren und das .bss-Segment löschen.

Das ist ja genau der Punkt. Der Startcode kopiert das .data Segment per 
default nicht, aber es laesst sich einschalten. Das kann natuerlich 
beabsichtigt sein.

Compiler legt aber Variablen die man mit 0 initialisiert hat ins bss 
Segment, was auch noch eine gewisse Logic hat, spart es doch etwas 
Flash, aber die loescht er dann nicht. Vielleicht gibt es da auch noch 
ein eigenes define fuer. Muss ich nochmal genauer schauen.

Aber es stimmt schon, am besten ist es man kuemmert sich um alles 
selber.

Olaf

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

olaf schrieb:
> Ich habe hier folgendes Programmiert:
>
> #define SYSTEMREADY 4711
> #define SYSTEMSTOP  0
> volatile static int resetval=SYSTEMSTOP;
>
> Das fuehrte zu folgendem interessanten verhalten. Mein Programm
> funktioniert beim einschalten des Microcontrollers, aber nach einem
> Reset oder beim flashen ueber den Debuger nicht mehr weil der
> Startupcode die Variablen nicht initialisiert.

Dann ist es ein Bug im Startup-Code oder dieser spielt nicht richtig mit 
dem Linker-Script zusammen.  Schau dir die verfügbaren Infos an: Wo und 
wie legt der Compiler das Datum ab (abhäbgig von Compiler-Optionen und 
Code in: common, .bss, .data oder wegoptimiert), was sagt das Map-File, 
welche Symbole definiert das ld-Script und wie werden diese vom 
Start-Code verwendet, etc.

> Das entsprach schon mal so gar nicht meiner Erwartung, was aber wohl
> daran lag das ich bisher meine Startcodes immer selber geschrieben habe.

Aha.

> In der Tat es gibt da ein Define. nimmt man das in seine
> CFLAGS auf "-D__NO_SYSTEM_INIT=1" so wird initialisiert.

Und wo wirkt das?

Als Makro im Quelltext?
Als Makro in einem präprozessiertes Linker-Sktipt?
Als Makro im Startup-Code?

> Initialisiert man mit 0 wie oben wird immer noch nicht initialisiert.

Eruier erst mal, wo das Objekt landet.  Datenablage ist anhängig von 
Compiler-Optimierung und LTO sowie von: -f[no-]data-sections, 
-f[no-]common, -f[no-]zero-initialized-in-bss, etc.

> Erst wenn man es so macht:
>
> #define SYSTEMSTOP  0xdead
> volatile static int resetval=SYSTEMSTOP;
>
> ...bekommt man das erwartete Verhalten.

Vermutlich ein Bug im Start-Code, wo nur .bss genullt wird, nicht aber 
COMMON, oder COMMON fehlt im Linker-Skript und die Orphan-Platzierung 
ist anders als erwartet.

Ohne mehr Info kann man aber nur raten.

> Hab ich diese charmante Eigenschaft in den letzten 30Jahren
> C-Programmierung uebersehen?

Sieht man nur bei falschen Start-Code, falschem Linker-Script oder 
nicht-konformen Tools :-)

W.S. schrieb:
> Zweitens:
> Dich nie und nimmer auf als selbstverständlich angenommene
> Initialisierungen zu verlassen und deine Variablen vor ihrer
> Verwendung dediziert selbst zu initialisieren.

In dieser Allgemeinheit ist die Aussage für globale Variablen einfach 
Käse.

> "ich traue ihm nur soweit, wie ich ihn selber schmeißen kann".
> Das gilt für alle Variablen gleichermaßen.

Und warum verwendest du dann überhaupt Compiler? Wenn die sich eh an 
keinen Standard halten, lass einfach deine Finger davon und nimm was 
Vernünfiges.

> Drittens:
> Das Wort "static" zu vermeiden - egal wo.

So ein Unsinn.

Dass du keine Ahnung hast, was static in C bedeutet, hast du in 
Beitrag "C Anfängerfrage (static innerhalb einer Funktion)" ja prächtig demonstriert. 
Nicht weiter erwähnenswert, wenn du dazu lernen würdest — was offenbar 
nicht der Fall ist (und stattdessen posaunst auch noch noch damit rum).

von Falk B. (falk)


Lesenswert?

Beim Code Composer Studio von TI hatte ich auch die nette Überraschung, 
daß das .bss Segment NICHT automatisch gelöscht wurde. Das steht auch so 
im Compilerhandbuch, daß man das im Startup-Code selber machen muss. Ich 
hab dann was passendes im TI-Forum gefunden und eingebaut. Seitdem ist 
das Ganze wieder C-Standard konform. Aber verwirrend war das schon, vor 
allem weil alle Beispiele von TI mit dem Würg-Around der manuellen 
Initialisierung ALLER Variablen arbeiten . . .

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

W.S. schrieb:
> Drittens:
> Das Wort "static" zu vermeiden - egal wo.

Nachdem Du kürzlich so gründlich dargelegt hast, daß Du nicht verstehst, 
wozu es da ist, kann ich diesen Ratschlag nachvollziehen.

von olaf (Gast)


Lesenswert?

> Dann ist es ein Bug im Startup-Code oder dieser spielt nicht richtig mit
> dem Linker-Script zusammen.

Im oben der oben von mir angefuehrten Startupdatei gibt es ganz einfach 
keinen code der bss loescht.

Das ist wohl auch schon anderen mal aufgefallen:

http://www.keil.com/support/docs/2821.htm

> Und wo wirkt das?

Hier mal ein ausschnitt aus der startup Datei:

Reset_Handler:
#ifndef __NO_SYSTEM_INIT
    ldr    r0, =SystemInit
    blx    r0
#endif
    ldr    r1, =__etext
    ldr    r2, =__data_start__
    ldr    r3, =__data_end__

    subs   r3, r2
    ble    .flash_to_ram_loop_end
.flash_to_ram_loop:
    subs   r3, #4
    ldr    r0, [r1, r3]
    str    r0, [r2, r3]
    bgt    .flash_to_ram_loop
.flash_to_ram_loop_end:
    ldr    r0, =main

Ich hab da gerade noch folgendes eingebaut:

# Clear .bss section (Zero init)
    mov    r0, #0
    ldr    r2, =__bss_start__
    ldr    r3, =__bss_end__
    subs   r3, r2
    ble    .zero_to_bss_loop_end
.zero_to_bss_loop:
    subs   r3, #4
    str    r0, [r2, r3]
    bgt    .zero_to_bss_loop
.zero_to_bss_loop_end:

Aechz..das erstemal seit >10J mal wieder Assembler programmiert. :-)
Jetzt sieht aber alles gut aus.

Olaf

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Dich nie und nimmer auf als selbstverständlich angenommene
> Initialisierungen zu verlassen und deine Variablen vor ihrer Verwendung
> dediziert selbst zu initialisieren.

Das ist Unsinn, weil der Compiler sich beim Optimieren darauf verlassen 
darf, daß ein Startupcode existiert, der globale Variablen 
standardkonform initialisiert. Sprich, solche expliziten 
Null-Initialisierungen darf er einfach wegoptimieren. Und dann steht man 
so richtig im Regen, weil man irrtümlich denkt, man hätte es richtig 
gemacht. Deswegen ist die von Dir vorgeschlagene Lösung schlichtweg 
grober Pfusch.

Wegen genau solcher möglichen Probleme im Startupcode schreibt man den 
auch am besten selber, denn dann weiß man, daß es richtig gemacht ist. 
Ist jetzt ja auch kein Hexenwerk mit den Importen aus dem Linkerscript.

Außerdem verwendet man selbstverständlich static bei globalen Variablen, 
weil das zumindest mal den Scope begrenzt.

von Nop (Gast)


Lesenswert?

Nop schrieb:

> Sprich, solche expliziten Null-Initialisierungen darf er einfach
> wegoptimieren.

(Nachtrag: in dem Fall nicht, wegen volatile, aber das macht die 
Pfuschlösung nicht besser.)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

olaf schrieb:
> Johann L. schrieb:
>> Dann ist es ein Bug im Startup-Code oder dieser spielt nicht richtig mit
>> dem Linker-Script zusammen.
>
> Im oben der oben von mir angefuehrten Startupdatei gibt es ganz einfach
> keinen code der bss loescht.

Der Startup-Code scheint ja zur Anwendung zu gehören, zumindest wird er 
mit  dieser zusammen Compiliert / Assembliert.  Dann muss also der 
Anwender wissen, was er tut.

Wenn das Device wirklich sooo winzig ist, dass die paar Bytes zum 
Initialisieren von .bss eine Rolle spielen, dann gibt es bessere 
Ansätze, welche die Tools wählen können:

* Über Schalter kann der Start-Code ausgelassen bzw. nicht gegen diesen 
gelinkt werden, z.B. -nocrt0, -nostartfiles, etc. wenn eigener Code 
stattdessen verwendet wird.

* Es gibt spezielle, vordefinierte, leere weak-Funktionen, die der 
Startup-Code aufruft, und die von der Anwendung "überschrieben" werden 
können um eigene Initialisierungen zu erledingen, etwa __init 
oder ___main.

* Es gibt spezielle Sections, über welche Startup-Code hinzugefügt 
werden kann, z.B. .initN oder __attribute((constructor)).

* Zur Initialisierung von .bss und .data wählt avr-gcc folgenden Ansatz: 
Es gibt 2 unanhängige Routinen in der libgcc, die diese Aufgaben 
übernehmen, aber ohne sie zu referenzieren wird dieser Code natürlich 
nicht verwendet.  Die Referenzierung erledigt der Compiler: er weiß für 
jedes Modul, ob es eine Initialisierung von .bss oder .data triggert und 
linkt dann gegen den entsprechenden Code.  Wenn .bss nicht verwendet 
wird, dann wird der Code zum 0-en von .bss auch nicht gelinkt.

* Soll Code im Static Storage nicht initialisiert werden (z.B. weil er 
einen Warmstart überleben soll) dann gibt es 
z.B. __attribute((section(".noinit"))).  Beispiel sieht man in den 
Linker-Scripts für avr-gcc; eine Behandlung in Compiler oder Linker ist 
nicht notwendig.

Prinzipiell kann man diesen Init-Teil des CRT auch weglassen und muss 
sich dann selber überlegen, wie man die dann nicht mehr konforme 
C-Implementation konform macht.  Oder wie man um die Nichtkonformität 
herum hackt.

Das Herum-Hacken mittels händischer Belegung hat folgende entscheidende 
Nachteile:

* Es gibt keinen kanonischen Ort dafür, etwa für Module-static oder 
Function-static.  Diese Variablen sind nicht von main aus erreichbar, 
und um sich zu merken, ob diese Variablen bereits initialisiert sind, 
bräuchte man weitere Variablen, um sich den Zustand "Initialisiert oder 
nicht" zu merken.

* Selbst wenn man auf static verzichtet, führt dies zu unliebsamen 
Querabhängigkeiten zwischen den Objekten und dem Code, der diese 
initialisiert; man muss ständig darum bedacht sein, beide Codestellen 
konsisitent zu halten.

* Hat man nur sehr wenige Objekte und kleinen Code, kann eine händlische 
Initialisierung zu ein paar Byte kleinerem Code führen — falls es 
wirklich eine Rolle spielen sollte, das letzte Byte an Codegröße 
rauszuquetschen.  I.d.R. wird der Code aber deutlich größer und 
schlechter wartbar.

* const ist nicht mehr verwendbar, da const nicht vom der Anwendung 
geschrieben werden kann (und wenn doch, dann ist es Undefined 
Behaviour).

* In C++ ist const eine echte Obermenge von read-only.  Der Compiler 
kann einen artifiziellen, statischen Constructor zur Initialisierung 
dieser Objekte erstellen und ein const zur Laufzeit schreiben.  Wird 
dieser Code nicht ausgeführt (weil Start-Code nicht dazu passt), ist das 
komplette Programm dysfunktional.

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Das ist Unsinn, weil der Compiler sich beim Optimieren darauf verlassen
> darf, daß ein Startupcode existiert, der globale Variablen
> standardkonform initialisiert.

Du bist jetzt schon der gefühlt 100ste, der nix anderes antworten kann 
als "Unsinn". Nein, es ist wirklich kein Unsinn, sondern nur von dir und 
all den anderen nicht verstandener Sinn. Sozusagen das ständige 
Repetieren auswendig gelernter Leitsätze.

Ich habe ja garnichts gegen Leitsätze - aber das Leben ist anders, siehe 
unten. Mir ist wirklich kein C-Compiler bekannt, der selbst bei 
ausgiebigster Optimierung ganze Anweisungen, mit denen einer Variable 
ein Wert (0 oder sonstwas) zugewiesen wird, einfach wegläßt. Selbst der 
knöselige GCC macht sowas nicht.

So.
Und nun weswegen das Ganze?
Wenn am PC ein Programm abschmiert, wird es vom BS rausgeworfen und 
eventuell neu geladen und gestartet. Damit hat man immer die 
Anfangsbedingungen, die man laut Schulbuch erwartet.

Wenn man hingegen auf nem µC sein Programm laufen hat, dann ist es 
durchaus wahrscheinlich, daß man einen Neu-Anlauf einiger Teile mal 
durchführen muß, weil sich eben was in der Außenwelt geändert hat. Ein 
BS, das einem die Aufräumarbeiten abnimmt, hat man da nicht.

Nun könnte man die Generalkeule schwingen und nen kompletten Neuanlauf 
erzwingen, z.B. per Watchdog oder softwareausgelöstem Reset - je nach 
vorhandenem Controller.

Aber das ist fast immer die unschönste Lösung. Also initialisiert man 
mit nem Aufruf der zugehörigen InitXYZ() die betroffenen Teile und die 
Sache ist erledigt.

Aber dazu muß man es eben so halten, wie ich das beschrieben habe: 
kein "static", sondern Globalvariable, denn sie muß ja vom InitXYZ() 
auch erreichbar sein. Kein long abc=4711; sondern Trennung von 
Deklaration und Wertzuweisung, denn es muß ja beim Aufruf der 
Initialisierung des Moduls ohnehin getan werden. Kein Glauben, daß beim 
Anlauf in jeder Variable 0 oder eine Vorbelegung drinsteht, sondern das 
Wissen, daß dort bunter Käse drinsteht, solange InitXYZ() das nicht 
gerichtet hat.

Ich hatte vor langer Zeit genau diese tollen Probleme mit dem Fat 
Filesystem von Chan. Der war auch so ein Gläubiger der Ausnullung, 
weswegen es regelmäßig zum Datencrash kam, wenn ein Benutzer die 
SD-Karte gewechselt hat, ohne brav zuvor auszumounten, denn die 
SD-Initialisierung (!!!) des FF verließ sich auf das Ausgenulltsein 
eines Pointers. Eben deswegen mein Zitat von Sam.

Kurzum: Das Zitieren des Schulbuches ist das eine, das richtige Leben 
ist was anderes.

W.S.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

W.S. schrieb:
> Wenn man hingegen auf nem µC sein Programm laufen hat, dann ist es
> durchaus wahrscheinlich, daß man einen Neu-Anlauf einiger Teile mal
> durchführen muß, weil sich eben was in der Außenwelt geändert hat. Ein
> BS, das einem die Aufräumarbeiten abnimmt, hat man da nicht.
>
> Nun könnte man die Generalkeule schwingen und nen kompletten Neuanlauf
> erzwingen, z.B. per Watchdog oder softwareausgelöstem Reset - je nach
> vorhandenem Controller.
>
> Aber das ist fast immer die unschönste Lösung. Also initialisiert man
> mit nem Aufruf der zugehörigen InitXYZ() die betroffenen Teile und die
> Sache ist erledigt.
>
> Aber dazu muß man es eben so halten, wie ich das beschrieben habe:
> kein "static", sondern Globalvariable, denn sie muß ja vom InitXYZ()

Ok, du hast also eine sehr spezielle Anwendung mit seht speziellen 
Anforderungen, so dass dir die vom CRT vorgenommene Initialisierung des 
Static Storage nix nützt bzw. in einigen kalten Pfaden händisch 
initialisiert werden muss.

Aber daraus zu folgern, dass die gleiche Herangehensweise für alle 
anderen Anwendungen zuträfe, und daraus enstsprechende Ratschläge 
abzuleiten ist einfach hanebüchen.

> Nop schrieb:
>> Das ist Unsinn, weil der Compiler sich beim Optimieren darauf verlassen
>> darf, daß ein Startupcode existiert, der globale Variablen
>> standardkonform initialisiert.
>
> Mir ist wirklich kein C-Compiler bekannt, der selbst bei
> ausgiebigster Optimierung ganze Anweisungen, mit denen einer Variable
> ein Wert (0 oder sonstwas) zugewiesen wird, einfach wegläßt. Selbst der
> knöselige GCC macht sowas nicht.

ACK.  Ich wüsste nicht, wie ein Compiler das ausnutzen sollte.  Hier hat 
"Nop" was aus der Unsinn-Zaubertüte gezaubert.

> Ich hatte vor langer Zeit genau diese tollen Probleme mit dem Fat
> Filesystem von Chan. Der war auch so ein Gläubiger der Ausnullung,
> weswegen es regelmäßig zum Datencrash kam, wenn ein Benutzer die
> SD-Karte gewechselt hat, ohne brav zuvor auszumounten, denn die
> SD-Initialisierung (!!!) des FF verließ sich auf das Ausgenulltsein
> eines Pointers. Eben deswegen mein Zitat von Sam.

Das hat nix mit "Glaube an Ausnullung" zu tun, sondern liest sich wie 
ein Bug im FS.

> Kurzum: Das Zitieren des Schulbuches ist das eine, das richtige Leben
> ist was anderes.

Ok, dein "richtiges" Leben besteht also nur aus Anwendungen mit der von 
dir geforderten Warmstartfähigkeit einzelner Komponenten...

von W.S. (Gast)


Lesenswert?

Johann L. schrieb:
> Aber daraus zu folgern, dass...

Nun, das ist die wissenschaftlich begründete Herangehensweise. Wenn eine 
Sache, die man zuvor generell ausgeschlossen hat, sich eben doch 
wenigstens einmal (und das verifizierbar) ereignet, dann ist das 
generelle Ausschließen schlichtweg ein Fehler. Hier in diesem Thread 
eben ein Denkfehler, der da sagt: "wenn der Compiler und der Startupcode 
völlig richtig und bugfrei sind, dann ist es völlig ausgeschlossen 
daß.." - nun, ich habe es erlebt, das ist das "Eine Mal", was 
ausreichend ist zum Stürzen der vorherigen Behauptung - und andere 
erleben es offenbar auch gelegentlich und fragen dann hier an, warum sie 
ihre schöne C-Welt nicht mehr verstehen.

Und noch was Praktisches dazu: Wenn man ausgiebig mit voreingestellten 
globalen Variablen arbeitet, dann müssen die ja im Flash vorgehalten 
werden und es braucht Code, um sie in den RAM zu befördern. Wenn man 
static Variablen benutzt, dann müssen die ebenfalls so vorgehalten 
werden. Das kostet Platz. Vermutlich nicht sonderlich viel, aber wenn 
man es aufrechnet gegen das dedizierte Initialisieren in einer 
Init-Funktion, dann wird sich da wohl fast eine Patt-Situation ergeben. 
Der Unterschied ist der, den Max nannte. Er besteht darin, daß man bei 
der Initxyz()-Version (die ich präferiere) sich auf garnix zu 
verlassen braucht und es kostet trotzdem fast nix mehr an Code und Zeit. 
Kurzum, meine Herangehensweise ist von Hause aus stabiler und 
zuverlässiger. Aber all diejenigen, die hier das Schulbuch zitieren 
wollen, werde es wohl nicht einsehen.

W.S.

von Markus F. (mfro)


Lesenswert?

Johann L. schrieb:
>> Mir ist wirklich kein C-Compiler bekannt, der selbst bei
>> ausgiebigster Optimierung ganze Anweisungen, mit denen einer Variable
>> ein Wert (0 oder sonstwas) zugewiesen wird, einfach wegläßt. Selbst der
>> knöselige GCC macht sowas nicht.
>
> ACK.  Ich wüsste nicht, wie ein Compiler das ausnutzen sollte.  Hier hat
> "Nop" was aus der Unsinn-Zaubertüte gezaubert.

Genau das ist aber doch hier - wenn ich nicht völlig falsch liege - 
passiert? Der Compiler hat eine Initialisierung mit 0 festgestellt und - 
statt die Zuweisung stehen zu lassen - die Variable stattdessen ins .bss 
gesteckt. Zuweisung? - wegoptimiert.

von Oliver S. (oliverso)


Lesenswert?

W.S. schrieb:
> Ich hatte vor langer Zeit genau diese tollen Probleme mit dem Fat
> Filesystem von Chan. Der war auch so ein Gläubiger der Ausnullung,
> weswegen es regelmäßig zum Datencrash kam, wenn ein Benutzer die
> SD-Karte gewechselt hat, ohne brav zuvor auszumounten, denn die
> SD-Initialisierung (!!!) des FF verließ sich auf das Ausgenulltsein
> eines Pointers. Eben deswegen mein Zitat von Sam.

Du willst und kannst den Unterschied zum eigentlichen Thema anscheinend 
nicht verstehen. Das hast du ja nun in vielen Beiträgen ausreichend 
bewiesen. Ein nicht genullter Pointer bei SD-Kartenwechsel ist ein 
Programmierfehler, weil der Programmierer dies schlicht vergessen hat.

Eine Clib, die das bss-Segment nach einem Kaltstart nicht nullt, ist 
fehlerhaft, weil nicht der C-Spezifikation entsprechend.

Das eine hat mit dem anderen nichts zu tun.

Ob jetzt bei einem Warmstart die Initialisierungen der clib duechlaufen 
wird, oder nicht, muß im Einzelfall geklärt und berücksichtig werden. 
Warmstart kann ja alles mögliche sein.

Daraus jetzt abzuleiten, daß man immer und überall besser seine 
Variablen vollständig von Hand ininitalieren muß, ist abstrus. Man muß 
einfach nur wissen, was man tut.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

W.S. schrieb:
> Johann L. schrieb:
>> Aber daraus zu folgern, dass...
>
> Nun, das ist die wissenschaftlich begründete Herangehensweise.

Die "Logik" deiner Schlussfolgerung ist, aus einem Spezialfall eine 
Allgemeingültigkeit abzuleiten — nämlich basierend deiner App mit 
bestimmter Warmstartfähigkeit einen Kreuzzug gegen static und crt vom 
Zaun zu brechen.

Aus einer allgemeingültigen Aussage kann man immer einen enthaltenden 
Spezialfall folgern, aber die Umkehrung gilt i.A. nicht.

Daraus, dass 2 eine Prinzahl ist, kann man weder folgern, dass alle 
ganzen Zahlen Primzahlen sind, noch dass alle Primzahlen gerade sind, 
noch dass alle Primzahlen < 10 sind.

von Peter D. (peda)


Lesenswert?

W.S. schrieb:
> Vermutlich nicht sonderlich viel, aber wenn
> man es aufrechnet gegen das dedizierte Initialisieren in einer
> Init-Funktion, dann wird sich da wohl fast eine Patt-Situation ergeben.

Der Startup-Code lädt in einer Schleife vom Flash ins RAM, d.h. je 
Bytevariable wird auch nur ein Byte Flash benötigt. Die Kopierroutine 
fällt nur einmalig an.
Jede explizite Zuweisung benötigt aber jeweils 6 Byte (LDI+STS).
Es lohnt sich also, zu überlegen, wo man den 6-fachen Codeoverhead 
wirklich braucht und wo nicht.

von Peter S. (Gast)


Lesenswert?

Also ich würde Ausführungen zum Thema C-Programmierung von jemandem, der 
dazu rät static "egal wo" zu meiden, mit äußerster Vorsicht genießen :D

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.