Guten Morgen Forum :) Allgemeiner Teil: Soweit ich weiss (korrigiert mich wenn ich falsch liege!) garantiert mir der C-Standard, dass globale Variablen immer mit 0 initialisiert werden, es sei denn, der Programmierer gibt einen Wert vor. Dann wird die Variable natuerlich mit diesem Wert initialisiert. Ist das in C++ auch so, oder sagt der C++-Standard etwas anderes dazu? Spezieller Teil: Wo findet die Initialisierung statt? Muss da (in Abhaengigkeit der Platform?) Code vom Compiler fuer generiert werden? Oder findet das ganze im Startup-Code statt (welcher im Embeddedbereich ja oftmals von den Herstellern geliefert wird)? Ich hoffe Ihr koennt mir helfen :) Gruesse
Globale Variablen sind generell zu vermeiden. - Gründe leifert dir google. Falls du Sie unbedingt brauchst, müssen die in der Deklaration initialisiert werden. C / CPP garantieren KEINE initialisierung. In einer nicht-initialisierten Variable findet sich dass, was als letztes an dieser Stelle im RAM stand. Bei int variable; printf(variable) kommt also etwas mehr oder weniger zufälliges raus. Da ist global oder nicht völlig Wurst. Wenn in deinem Framework allerdings globale vorhanden sind, sagt dir die Dokumentation ob die initialisiert sind ober nicht.
Du kannst das meiste davon nachlesen unter: http://en.cppreference.com/w/cpp/language/initialization Auszug daraus: >>...All non-local variables with static storage duration are initialized as >>part of program startup ... und: >>...For all other non-local static and thread-local variables, Zero >>initialization takes place. ...
waflija schrieb: > Globale Variablen sind generell zu vermeiden. Pauschale Aussagen sind generell zu vermeiden! Sie sind je nach konkretem Fall beliebig unsinnig/falsch. Zum Thema: Im Zweifel schadet es nicht Variablen einfach zu initialisieren. Das erleichtert auch die Lesbarkeit des Codes.
Kaj G. schrieb: > Soweit ich weiss (korrigiert mich wenn ich falsch liege!) garantiert mir > der C-Standard, dass globale Variablen immer mit 0 initialisiert werden, Korrekt. C89, 6.5.7 Initialization: If an object that has automatic storage duration is not initialized explicitely, its value is indeterminate. If an object that has static storage duration is not initialized explicitely, it is initialized implicitely as if every member that has arithmetic type were assigned 0 and every member that has pointer type were assigned a null pointer constant. C99, 6.7.8 Initialization: If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static storage duration is not initialized explicitly, then: — if it has pointer type, it is initialized to a null pointer; — if it has arithmetic type, it is initialized to (positive or unsigned) zero; — if it is an aggregate, every member is initialized (recursively) according to these rules; — if it is a union, the first named member is initialized (recursively) according to these rules. > Oder findet das > ganze im Startup-Code statt (welcher im Embeddedbereich ja oftmals von > den Herstellern geliefert wird)? Embedded ist das der Teil, der zwischen dem Resetvektor und main() liegt, was typischerweise Assembler-Startupcode ist. Dort wird üblicherweise das RAM genullt. Der Grund hierfür ist, daß explizit mit 0 initialisierte Variablen in der BSS-Sektion landen und nicht in der DATA-Sektion. Mit anderen Worten, die Initialisierungswerte von mit 0 explizit initialisierten globalen Variablen belegen keinen Platz im ROM, wenn man das so macht. Außerdem werden im Startupcode auch die Initialwerte von globalen Variablen aus dem ROM ins RAM geladen, die nicht null sind. Diesen Startupcode schreibt man sich üblicherweise selber, oft durch Anpassung einer bestehenden Vorlage. waflija schrieb: > int variable; > printf(variable) > > kommt also etwas mehr oder weniger zufälliges raus. Da ist global oder > nicht völlig Wurst. Das ist überhaupt nicht wurst - bei lokalen Variablen stimmt das, aber bei globalen sieht der C-Standard das anders als Du.
waflija schrieb: > Falls du Sie unbedingt brauchst, müssen die in der Deklaration > initialisiert werden. C / CPP garantieren KEINE initialisierung. In > einer nicht-initialisierten Variable findet sich dass, was als letztes > an dieser Stelle im RAM stand. > Bei > > int variable; > printf(variable) > > kommt also etwas mehr oder weniger zufälliges raus. Da ist global oder > nicht völlig Wurst. Wenn in deinem Framework allerdings globale > vorhanden sind, sagt dir die Dokumentation ob die initialisiert sind > ober nicht. Sorry, stimmt nur bedingt. Globale Variablen werden entweder in das .bss Segment abgelegt (uninitialisierte Variablen) oder .data (initialisierte Variablen). Die ersteren werden im Startupcode in einer for Schleife explizit ausgenullt, die Anderen in einer anderen for Schleife von einem gleichgroßen Bereich des Programmspeichers initialisiert. Zumindestens ist das bei Allen Compilern/Startupcodesequenzen im von gcc etc abgeleiteten Architekturen so. Will man nichtflüchtigen Speicher haben, muss man die dort liegenden Variablen explizit in ein Anderes Segment ablegen, das vom Startupcode nicht berührt wird. Über den "Wert" von globalen Variablen braucht man an dieser Stelle nicht zu diskutieren, das sind Stil- und Philosophiefragen. Edit: oops, nop war schneller...
:
Bearbeitet durch User
Der Andere schrieb: > Zum Thema: Im Zweifel schadet es nicht Variablen einfach zu > initialisieren. Das erleichtert auch die Lesbarkeit des Codes. Richtig. Zumal, wenn man statische Variablen explizit mit 0 initialisiert, erkennt der Compiler das und erzeugt keinen zusätzlichen Code. Aber man sagt als Programmierer, daß man die Initialisierung nicht vergessen hat, sondern daß da bewußt 0 hinsoll. Nochwas, das gilt nicht nur für globale Variablen, sondern auch für static-Variablen mit Datei- oder Funktionsscope.
waflija schrieb: > C / CPP garantieren KEINE initialisierung. In > einer nicht-initialisierten Variable findet sich dass, was als letztes > an dieser Stelle im RAM stand. Völliger Blödsinn. Natürlich garantiert C eine Initialisierung von globalen Variablen. > int variable; > printf(variable) > > kommt also etwas mehr oder weniger zufälliges raus. Da ist global oder > nicht völlig Wurst. Wenn in deinem Framework allerdings globale > vorhanden sind, sagt dir die Dokumentation ob die initialisiert sind > ober nicht. Nein, das ist nicht zufällig. Wenn "variable" tatsächlich global ist wird diese mit 0 vom Startup Code bzw der Segment Initialisierung initialisiert. Problematisch ist das nur wenn eine lokale Variabe in einer Funktion benutzt wird weil sie dann auf dem Stack liegt: int main(void) { int hugo; // hugo hat jetzt einen zufälligen Wert }
Zum Thema Embedded sollte noch ergänzend hinzugefügt werden, daß globale C++ Objekte (nicht Pointer, sondern Objekte) mit großer Sorgfalt zu benutzen sind, weil der Startupcode ziemlich sofort nach Reset versucht, die Konstruktoren aufzufufen, und zu dem Zeitpunkt ist das System noch nicht komplett initialisiert, dh Konstruktoren, die ein initialisiertes System voraussetzen, können ordentliches Chaos anrichten.
:
Bearbeitet durch User
Ruediger A. schrieb: > Zum Thema Embedded sollte noch ergänzend hinzugefügt werden, daß globale > C++ Objekte (nicht Pointer, sondern Objekte) mit großer Sorgfalt zu > benutzen sind, weil der Startupcode ziemlich sofort nach Reset versucht, > die Konstruktoren aufzufufen, und zu dem Zeitpunkt ist das System noch > nicht komplett initialisiert, dh Konstruktoren die ein initialisiertes > System voraussetzen können ordentliches Chaos anrichten. Ich finde, Du übertreibst ein wenig! Ja, da kann man etwas falsch machen, aber das ist jetzt auch nicht komplett unüberblickbar. Ich habe bis jetzt leider noch keinen vom Hersteller mitgelieferten Startup Code gesehen, der das für globale Objekte macht. Musste ich bis jetzt Immer selbst implementieren.
Torsten R. schrieb: > Ich habe bis jetzt leider noch keinen vom Hersteller mitgelieferten > Startup Code gesehen, der das für globale Objekte macht. Musste ich bis > jetzt Immer selbst implementieren. meinst du das der Konstruktor von globalen Objekte nicht aufgerufen wird? Das kann ich mir gar nicht vorstellen.
Peter II schrieb: > meinst du das der Konstruktor von globalen Objekte nicht aufgerufen > wird? Das kann ich mir gar nicht vorstellen. Ja, zumindest der gcc Startup code von Nordic und ST tut es ganz sicher nicht.
Guest schrieb: > Problematisch ist das nur wenn eine lokale Variabe in > einer Funktion benutzt wird weil sie dann auf dem Stack liegt: Der C-Standard sagt nicht, daß lokale Variablen auf dem Stack liegen, denn der kennt gar keinen Stack. Sie können ja praktisch gesehen durchaus auch in Registern liegen.
Torsten R. schrieb: > Peter II schrieb: > >> meinst du das der Konstruktor von globalen Objekte nicht aufgerufen >> wird? Das kann ich mir gar nicht vorstellen. > > Ja, zumindest der gcc Startup code von Nordic und ST tut es ganz sicher > nicht. kann man dann überhaupt C++ sinnvoll verwenden? Ich hatte bis jetzt nur kleine Atmel und da ist mir das nicht aufgefallen, dort haben ich globale Objekte und im konstruktor steht auch code drin. Irgendwo muss man sich ja auf ein verhalten verlassen können. gibt es dann wenigsten eine Fehler oder eine Warnung?
Peter II schrieb: > Torsten R. schrieb: >> Ja, zumindest der gcc Startup code von Nordic und ST tut es ganz sicher >> nicht. > > kann man dann überhaupt C++ sinnvoll verwenden? Ich hatte bis jetzt nur > kleine Atmel und da ist mir das nicht aufgefallen, dort haben ich > globale Objekte und im konstruktor steht auch code drin. Irgendwo muss > man sich ja auf ein verhalten verlassen können. Man kann das benötigte Verhalten ja relativ einfach nachrüsten (https://github.com/TorstenRobitzki/bluetoe/blob/master/examples/nrf51/runtime.cpp) > gibt es dann wenigsten eine Fehler oder eine Warnung? Die "Der Hersteller Deines Chips kümmert sich einen Sch.. Dre.. um C++, sei froh, wenn er schon C99 verwendet!"-Warnung? ;-)
Peter II schrieb: > Torsten R. schrieb: >> Peter II schrieb: >> >>> meinst du das der Konstruktor von globalen Objekte nicht aufgerufen >>> wird? Das kann ich mir gar nicht vorstellen. >> >> Ja, zumindest der gcc Startup code von Nordic und ST tut es ganz sicher >> nicht. > > kann man dann überhaupt C++ sinnvoll verwenden? Ich hatte bis jetzt nur > kleine Atmel und da ist mir das nicht aufgefallen, dort haben ich > globale Objekte und im konstruktor steht auch code drin. Irgendwo muss > man sich ja auf ein verhalten verlassen können. Im Embedded-Bereich gibt's leider recht viele, die in ihrem Startup-Code nur C bedenken und C++ nicht, weshalb dann solche Sachen fehlen. > gibt es dann wenigsten eine Fehler oder eine Warnung? Wo sollte die herkommen?
Peter II schrieb: > meinst du das der Konstruktor von globalen Objekte nicht aufgerufen > wird? Das kann ich mir gar nicht vorstellen. GCC erstellt je nach bedarf statische Konstruktoren.
1 | int volatile v; |
2 | const int i = v; |
3 | |
4 | int main() |
5 | {
|
6 | return i; |
7 | }
|
Mit avr-g++ übersetzt :
1 | .global main |
2 | .type main, @function |
3 | main: |
4 | lds r24,_ZL1i |
5 | lds r25,_ZL1i+1 |
6 | ret |
7 | |
8 | .type _GLOBAL__sub_I_v, @function |
9 | _GLOBAL__sub_I_v: |
10 | lds r24,v |
11 | lds r25,v+1 |
12 | sts _ZL1i+1,r25 |
13 | sts _ZL1i,r24 |
14 | ret |
15 | |
16 | .global __do_global_ctors |
17 | .section .ctors,"a",@progbits |
18 | .p2align 1 |
19 | .word gs(_GLOBAL__sub_I_v) |
20 | |
21 | .local _ZL1i |
22 | .comm _ZL1i,2,1 |
23 | .global v |
24 | .section .bss |
25 | .type v, @object |
26 | .size v, 2 |
27 | v: |
28 | .zero 2 |
29 | .ident "GCC: (GNU) 7.0.0 20161202 (experimental) [trunk revision 227650]" |
avr-g++ erstellt also einen statischen Konstruktor _GLOBAL__sub_I_v welcher die Initialisierung von i übernimmt und trägt die Adresse des Konstruktors in die Liste der Funktionen (.ctors) ein, die beim Start-up auszuführen sind (ähnlich wie attribute((constructor)) in C). Ähnlichen aber komplizierteren Code erhält man wenn Klassen entsprechendes benötigen. Mit einem Host-g++ sieht der Code ganz ähnlich aus :
1 | .globl main |
2 | .type main, @function |
3 | main: |
4 | movl _ZL1i(%rip), %eax |
5 | ret |
6 | |
7 | .type _GLOBAL__sub_I_v, @function |
8 | _GLOBAL__sub_I_v: |
9 | movl v(%rip), %eax |
10 | movl %eax, _ZL1i(%rip) |
11 | ret |
12 | |
13 | .section .init_array,"aw" |
14 | .align 8 |
15 | .quad _GLOBAL__sub_I_v |
16 | .local _ZL1i |
17 | .comm _ZL1i,4,4 |
18 | .globl v |
19 | .bss |
20 | .align 4 |
21 | .type v, @object |
22 | .size v, 4 |
23 | v: |
24 | .zero 4 |
25 | .ident "GCC: (GNU) 7.0.0 20161202 (experimental) [trunk revision 227650]" |
Der Eintrag für den Startup-Code (.init_array) sieht natürlich anders aus, da er zum diesem passen muss, aber im Eneffekt passiert das gleiche. Wenn g++ das also nicht macht, dann passt der Startup-Code nicht zum Compiler bzw. das Backend ist Murx, oder es wurde dem Compiler explizit abgewöhnt, was doch reichlich fragwürdig erscheint. ...und das gleiche dann nochmal für die Destruktoren (.fini_array, .dtors, etc)
Nop schrieb: > Guest schrieb: >> Problematisch ist das nur wenn eine lokale Variabe in >> einer Funktion benutzt wird weil sie dann auf dem Stack liegt: > > Der C-Standard sagt nicht, daß lokale Variablen auf dem Stack liegen, > denn der kennt gar keinen Stack. Sie können ja praktisch gesehen > durchaus auch in Registern liegen. Ja natürlich, aber das ändert nichts an der Tatsache das der Variablenwert ohne explizite Initialisierung rein zufällig ist.
Torsten R. schrieb: > Ruediger A. schrieb: >> Zum Thema Embedded sollte noch ergänzend hinzugefügt werden, daß globale >> C++ Objekte (nicht Pointer, sondern Objekte) mit großer Sorgfalt zu >> benutzen sind, weil der Startupcode ziemlich sofort nach Reset versucht, >> die Konstruktoren aufzufufen, und zu dem Zeitpunkt ist das System noch >> nicht komplett initialisiert, dh Konstruktoren die ein initialisiertes >> System voraussetzen können ordentliches Chaos anrichten. > > Ich finde, Du übertreibst ein wenig! Ja, da kann man etwas falsch > machen, aber das ist jetzt auch nicht komplett unüberblickbar. > > Ich habe bis jetzt leider noch keinen vom Hersteller mitgelieferten > Startup Code gesehen, der das für globale Objekte macht. Musste ich bis > jetzt Immer selbst implementieren. naja, vielleicht arbeitest Du mit drastisch anderen Plattformen als ich, aber bei meinen Kunden taucht das Thema regelmäßig auf. Zwei Beispiele: 1. Controller, bei der die .bss Section im SDRAM liegt. Da der SDRAM Controller erst initialisiert werden muss, bevor auf den Speicher zugegriffen werden kann, muss hier der Resetvektor so umgebogen werden, dass erst eine Minimalinitialisierung durchlaufen wird, bevor der Startupcode laufen kann (dieser Teil hat natürlich erstmal nichts mit C++ zu tun, geht aber in eine ähnliche Richtung). 2. Statische C++ UART Objekte. Wenn der Konstruktor hier versucht, auf Peripherieregister zuzugreifen, bevor der UART initialisiert ist (also z.B. die GPIOs entsprechend auf primäre Funktionen konfiguriert wurden), wird vermutlich der folgende Prozessorinitialisierungscode erstmal alles wieder defaultmäßig überbügeln. Das hängt natürlich stark davon ab, in welcher Form die Runtimeinitialisierung genutzt wird. In vielen Plattformen wird der Resetvektor direkt auf die __entry Funktion des C Runtimecodes gesetzt, die dann ihre opake Magie macht und letztendlich auf das main() verzweigt, ab dem die Firmware anfängt zu arbeiten (also z.B. die Prozessorinitialisierung vornimmt). Bei diesen Plattformen ist der Code zwischen __startup und main() meistens irgendeine adaptierte Form vom Standard C/C++ runtime Code, und der MUSS statische Konstruktoren aufrufen, er hat keine Wahl. Bei Anderen Ökosystemen sind die .bss und .data Initialisierungen bereits im vom Hersteller beritgestellten Resetvektorcode enthalten, da hat man dann mehr Einflussmöglichkeiten (muss aber dann natürlich die Konstruktoren selber an geeigneter Stelle aufrufen).
Wird ein ctor nicht aufgerufen, an einer Stelle, wo er aufgerufen werden sollte, so ist das ein nicht-konformer Compiler. Und für class-type Objekte der Speicherklasse static (e.g. program-/übersetzungseinheit-globale variable) muss er aufgerufen werden. Ruediger A. schrieb: > Torsten R. schrieb: > > 2. Statische C++ UART Objekte. Wenn der Konstruktor hier versucht, auf > Peripherieregister zuzugreifen, bevor der UART initialisiert ist (also > z.B. die GPIOs entsprechend auf primäre Funktionen konfiguriert wurden), > wird vermutlich der folgende Prozessorinitialisierungscode erstmal alles > wieder defaultmäßig überbügeln. Was modelliert denn Deine UART-Klasse? Eine HW-Ressource? In diesem Fall sollte man dann wohl auch nicht beliebig viele Exemplare erzeugen können. Also wäre das ggf. ein Singleton (wohl eher nicht) oder ein Monostate? Ein Monostate wiederum wird aber wohl die HW nicht initialisieren... Ich würde also eine all-static Klasse / Template mit deleted-ctor als natürlicher empfinden, als ein Monostate o.ä. Die Initialsierung würde man dann explizit durchführen. Bei vielen HW-Ressourcen geht das ggf. elegant über fold-expressions. Unabhängig davon: bei globals kommt aber noch das "static initialization fiasco" ggf. hinzu...
Wilhelm M. schrieb: > Ich würde also eine all-static Klasse / Template mit deleted-ctor als > natürlicher empfinden, als ein Monostate o.ä. Die Initialsierung würde > man dann explizit durchführen. Bei vielen HW-Ressourcen geht das ggf. > elegant über fold-expressions. > meine Lösung ist in der Regel, auf statische Konstruktoren komplett zu verzichten und statt CSomeObject g_ObjInstance; so etwas machen wie CSomeObject *g_ObjInstance; ... void main(void) { <Prozessorinitialisierung> g_ObjInstance = new CSomeObject(...); } Bei Embedded ist halt Kontrolle ein großes Thema. Abstraktionen übernehmen (fast per Definition) eine Menge Kontrolle (was in Anderen Umgebungen gut ist), von der man aber bei Embedded eine Teilmenge nicht abgeben kann. C++ ist (sinnvoll angewandt) eine enorme Hilfe bei der Entwicklung auch und gerade für Embedded, aber man muss es halt so anwenden, dass man nicht zu viel von der Kontrolle ins Transparentland weggibt, die man braucht, um die spezielle Plattform richtig bearbeiten zu können. All-static Klassen machen für mich grundsätzlich wenig Sinn, weil sie im Prinzip die Einkapselung und Separierung von Instanzen aushebeln. Also nur um damit ein (umschiffbares) Konstrukt wie statische C++ Objekte irgendwie doch noch hinzukriegen würde ich sie erst recht nicht anwenden. Das Beispiel mit dem UART war nur zur Veranschaulichung. Bei Prozessoren mit bis zu 6 physikalischen UARTs (durchaus nicht unüblich) läßt sich je nach Konfigurationsvariante pro Firmware eine Anzahl Instanzen > 0 und <= 6 festnageln. Wenn sich die Zahl während der Laufzeit nicht ändert, ist auf den ersten Blick ein statisches Anlegen jeder jeweils benötigten Instanz ein möglich gangbarer Weg (wobei dann aber natürlich die static-only Implementation wegfällt, weil dann der this pointer notwendigerweise gebraucht wird). Ich wollte für solche Fälle nur veranschaulichen, womit man rechnen muss, wenn man tatsächlich auf statisch initialisierte Objekte nucht verzichten will).
Ruediger A. schrieb: > meine Lösung ist in der Regel, auf statische Konstruktoren komplett zu > verzichten und statt > > CSomeObject g_ObjInstance; > > so etwas machen wie > > CSomeObject *g_ObjInstance; > g_ObjInstance = new CSomeObject(...); man will keine dynamische Speicherwaltung auf kleinen Systemen haben. Ich würde es genauso so NICHT machen.
Ruediger A. schrieb: > Wilhelm M. schrieb: >> Ich würde also eine all-static Klasse / Template mit deleted-ctor als >> natürlicher empfinden, als ein Monostate o.ä. Die Initialsierung würde >> man dann explizit durchführen. Bei vielen HW-Ressourcen geht das ggf. >> elegant über fold-expressions. >> > > meine Lösung ist in der Regel, auf statische Konstruktoren komplett zu > verzichten und statt so war das aber nicht gemeint! all-static bedeutet, dass ich gar keine Instanz erzeuge! > > CSomeObject g_ObjInstance; > > so etwas machen wie > > CSomeObject *g_ObjInstance; > > ... > void main(void) > { > <Prozessorinitialisierung> > g_ObjInstance = new CSomeObject(...); > } Oh, Du hast dyn. Spiecherverwaltung!?! DAnn reden wir wohl nicht mehr über eine "uC" sondern über einen "C" ohne "u" ;-) Nein, auf uC dyn. Speicherallokation ist das verschiedenen Gründen einb nicht so guter Weg. > Das Beispiel mit dem UART war nur zur Veranschaulichung. Bei Prozessoren > mit bis zu 6 physikalischen UARTs (durchaus nicht unüblich) läßt sich je > nach Konfigurationsvariante pro Firmware eine Anzahl Instanzen > 0 und > <= 6 festnageln. Am besten m.E. mit einem Template: Uart<0> ... Uart<5> > Wenn sich die Zahl während der Laufzeit nicht ändert, > ist auf den ersten Blick ein statisches Anlegen jeder jeweils benötigten > Instanz ein möglich gangbarer Weg (wobei dann aber natürlich die > static-only Implementation wegfällt, weil dann der this pointer > notwendigerweise gebraucht wird). Den braucht man ja gerade nicht in dem Fall.
Ruediger A. schrieb: > meine Lösung ist in der Regel, auf statische Konstruktoren komplett zu > verzichten... Da es in Konstruktoren grundsätzlich zu Problemen kommen kann (wenn sie vor main() aufgerufen werden), ist es eine Alternative sie in Funktionen zu kapseln (imho auch eine Empfehlung von Scott Meyers). Bspw.:
1 | static Object make() { /* code to create object */ } |
2 | |
3 | static Object& get() |
4 | {
|
5 | static auto object = make() ; |
6 | return object ; |
7 | }
|
Ggf. Smart Pointer statt Referenzen. Vorteil: Gegenseitige Abhängigkeiten (sofern es diese gibt) werden korrekt aufgelöst. Exceptions des Konstruktors können abgefangen werden. Neutral: Das Laufzeitverhalten ändert sich da es ein Delay beim ersten Aufruf gibt. Nachteil: Der Code wird umfangreicher und nicht gerade besser lesbar.
Wilhelm M. schrieb: > > Oh, Du hast dyn. Spiecherverwaltung!?! DAnn reden wir wohl nicht mehr > über eine "uC" sondern über einen "C" ohne "u" ;-) > > Nein, auf uC dyn. Speicherallokation ist das verschiedenen Gründen einb > nicht so guter Weg. > uhm, das wird jetzt aber eine komplett Andere Diskussion... verstehe ich es richtig, daß Du Dich grundsätzlich gegen dynamische Speicherverwaltung in Embedded aussprichst, oder findest Du nur meine Codefragment mit dem new() unter einem dynamischen Speicherverwaltungssystem keine gute Idee? Ist leider etwas unklar...
Mikro 7. schrieb: > Ruediger A. schrieb: >> meine Lösung ist in der Regel, auf statische Konstruktoren komplett zu >> verzichten... > > Da es in Konstruktoren grundsätzlich zu Problemen kommen kann (wenn sie > vor main() aufgerufen werden), ist es eine Alternative sie in Funktionen > zu kapseln (imho auch eine Empfehlung von Scott Meyers). Ja, das ist absolut üblich und nennt sich lazy / deferred-instantiation. Es ist so normal, dass es einem manchmal gar nicht mehr einfällt. Vor allem, wenn man templates einsetzt, erspart es zudem Schreibarbeit ;-) > Bspw.: > >
1 | > static Object make() { /* code to create object */ } |
2 | >
|
3 | > static Object& get() |
4 | > { |
5 | > static auto object = make() ; |
6 | > return object ; |
7 | > } |
8 | >
|
> Ggf. Smart Pointer statt Referenzen. das ergibt aber wieder dyn. alloc. > > Vorteil: Gegenseitige Abhängigkeiten (sofern es diese gibt) werden > korrekt aufgelöst. Exceptions des Konstruktors können abgefangen werden. Noch Vorteil: wird die Instanz nicht gebraucht, wird die auch nicht erzeugt. > > Neutral: Das Laufzeitverhalten ändert sich da es ein Delay beim ersten > Aufruf gibt. > > Nachteil: Der Code wird umfangreicher und nicht gerade besser lesbar.
Mikro 7. schrieb: >
1 | > static Object make() { /* code to create object */ } |
2 | >
|
3 | > static Object& get() |
4 | > { |
5 | > static auto object = make() ; |
6 | > return object ; |
7 | > } |
8 | >
|
1 | Object& get() |
2 | {
|
3 | static Object object; |
4 | return object ; |
5 | }
|
:
Bearbeitet durch User
Ruediger A. schrieb: > Wilhelm M. schrieb: >> >> Oh, Du hast dyn. Spiecherverwaltung!?! DAnn reden wir wohl nicht mehr >> über eine "uC" sondern über einen "C" ohne "u" ;-) >> >> Nein, auf uC dyn. Speicherallokation ist das verschiedenen Gründen einb >> nicht so guter Weg. >> > > uhm, das wird jetzt aber eine komplett Andere Diskussion... ja, das hat mit urspr. Frage nach der Initialsierung von storage-class static Objekten nichts zu tun. Dies ist ja wahl beantwortet. > verstehe ich > es richtig, daß Du Dich grundsätzlich gegen dynamische > Speicherverwaltung in Embedded aussprichst, oder findest Du nur meine > Codefragment mit dem new() unter einem dynamischen > Speicherverwaltungssystem keine gute Idee? Ist leider etwas unklar... Grundsätzlich. Es sei denn, man hat eben MBs von RAM. Dann wird man wohl auch eine Plattform haben, für die die libstdc++ vollständig realisiert ist. Meine Vermutung ging in Richtung kleine uCs ohne dyn. alloc. Zudem ist ja gerade bei der Modellierung reiner HW-Ressourcen gar nicht dynamisch (s.a. Beispiel USART).
Wilhelm M. schrieb: > das ergibt aber wieder dyn. alloc. Nein, das Objekt muss nur kopierbar sein (für eine HW Abstraktion meist nicht denkbar) oder Move-bar sein.
Torsten R. schrieb: > Wilhelm M. schrieb: > >> das ergibt aber wieder dyn. alloc. > > Nein, das Objekt muss nur kopierbar sein (für eine HW Abstraktion meist > nicht denkbar) oder Move-bar sein. Ja, da hatte ich Dich falsch verstanden: ich dachte an make_unique<>()/make_shared<>(). Aber wozu in dem o.g. Bsp. SmartPtr?
:
Bearbeitet durch User
Wilhelm M. schrieb: > Aber wozu in dem o.g. Bsp. SmartPtr? das Beispiel von Mikro enthält keine smart pointer.
Mikro 7. schrieb: >
1 | > static Object make() { /* code to create object */ } |
2 | >
|
3 | > static Object& get() |
4 | > { |
5 | > static auto object = make() ; |
6 | > return object ; |
7 | > } |
8 | >
|
> Ggf. Smart Pointer statt Referenzen.
Es ging um diese Bemerkung!
Wilhelm M. schrieb: > > >> verstehe ich >> es richtig, daß Du Dich grundsätzlich gegen dynamische >> Speicherverwaltung in Embedded aussprichst, oder findest Du nur meine >> Codefragment mit dem new() unter einem dynamischen >> Speicherverwaltungssystem keine gute Idee? Ist leider etwas unklar... > > Grundsätzlich. Es sei denn, man hat eben MBs von RAM. Dann wird man wohl > auch eine Plattform haben, für die die libstdc++ vollständig realisiert > ist. > > Meine Vermutung ging in Richtung kleine uCs ohne dyn. alloc. > Definiere "klein." Es ist mit der entsprechenden Middleware probemlos möglich, FW für Controller der Größenordnung 256k RAM und 512k Flash mit RTOS und dynamischer Speicherverwaltung zu fahren. Ich habe sogar mal die Combo 128k Flash/32K RAM so zum Laufen bekommen, allerdings mit der "Sparvariante" FreeRTOS::heap_1.c (also nur malloc(), kein free()). Klar gibt es viele Anwendungen, in denen sämtlicher Speicherbereich vollständig deterministisch ist, und dann braucht man keine dyn. SV. Aber schon wenn so Dinge drin sind wie Kommunikationen mit variabler Telegrammgröße, hilft es enorm, nicht worst case voralloziieren zu müssen. > Zudem ist ja gerade bei der Modellierung reiner HW-Ressourcen gar nicht > dynamisch (s.a. Beispiel USART). Richtig, aber wenn z.B. konfigurationsabhängig mal 1, mal 2, mal... n USARTs zu bedienen sind, macht es gerade in kleinen Architekturen einen großen Unterschied, ob ich z.B. USART Empfangsringbuffer statisch für alle theoretisch zu bedienenden USARTs präventiv anlege oder nur für die USARTs, die ich jeweils brauche. Aber solche Sachen führen nun wirklich recht weit weg und sollten vermutlich Offline weiterdiskutiert werden...
Wilhelm M. schrieb: >> Ggf. Smart Pointer statt Referenzen. > > Es ging um diese Bemerkung! Falls man mehr braucht als nur den Konstruktor. Bspw. ein std::fstream den man erfolgreich mit einem Default geöffnet haben möchte und wo sich das Kapseln in eine separate Klasse nicht "lohnt".
Ruediger A. schrieb: > Wilhelm M. schrieb: >> >> >>> verstehe ich >>> es richtig, daß Du Dich grundsätzlich gegen dynamische >>> Speicherverwaltung in Embedded aussprichst, oder findest Du nur meine >>> Codefragment mit dem new() unter einem dynamischen >>> Speicherverwaltungssystem keine gute Idee? Ist leider etwas unklar... >> >> Grundsätzlich. Es sei denn, man hat eben MBs von RAM. Dann wird man wohl >> auch eine Plattform haben, für die die libstdc++ vollständig realisiert >> ist. >> >> Meine Vermutung ging in Richtung kleine uCs ohne dyn. alloc. >> > > Definiere "klein." Steht da: ohne dyn. alloc. >> Zudem ist ja gerade bei der Modellierung reiner HW-Ressourcen gar nicht >> dynamisch (s.a. Beispiel USART). > > Richtig, aber wenn z.B. konfigurationsabhängig mal 1, mal 2, mal... n > USARTs zu bedienen sind, Laufzeit? Oder Compilezeit? > macht es gerade in kleinen Architekturen einen > großen Unterschied, ob ich z.B. USART Empfangsringbuffer statisch für > alle theoretisch zu bedienenden USARTs präventiv anlege oder nur für die > USARTs, die ich jeweils brauche. Brauche ich doch auch gar nicht. Nur für die, die ich habe und benutze.
:
Bearbeitet durch User
Mikro 7. schrieb: > Wilhelm M. schrieb: >>> Ggf. Smart Pointer statt Referenzen. >> >> Es ging um diese Bemerkung! > > Falls man mehr braucht als nur den Konstruktor. Bspw. ein std::fstream > den man erfolgreich mit einem Default geöffnet haben möchte und wo sich > das Kapseln in eine separate Klasse nicht "lohnt". Wo ist da jetzt der Zusammenhang? Es ging doch um die Vermeidung dyn. alloc. In dem Zusammenhang habe ich die Verwendung von SmartPtr nicht verstanden.
Wilhelm M. schrieb: >> >> Richtig, aber wenn z.B. konfigurationsabhängig mal 1, mal 2, mal... n >> USARTs zu bedienen sind, > > Laufzeit? Oder Compilezeit? Bei dem Kunden zur Laufzeit (in einem Konfigurationsblock im Flash sind die aktiven Interfaces hinterlegt - in Installation x vielleicht 2, in y 3). Zum Startup wird der Konfigurationsblock ausgelesen und die Anzahl benötigter Interfaces initialisiert. > >> macht es gerade in kleinen Architekturen einen >> großen Unterschied, ob ich z.B. USART Empfangsringbuffer statisch für >> alle theoretisch zu bedienenden USARTs präventiv anlege oder nur für die >> USARTs, die ich jeweils brauche. > > Brauche ich doch auch gar nicht. Nur für die, die ich habe und benutze. Eben. Wenn das erst dynamisch zur Laufzeit bestimmen kann (siehe Beispiel oben), habe ich das Wissen, wieviel Speicher ich brauche, erst zur Laufzeit, und dann tue ich mich ohne dynamische SV schwer ;-)
Ruediger A. schrieb: > Eben. Wenn das erst dynamisch zur Laufzeit bestimmen kann (siehe > Beispiel oben), habe ich das Wissen, wieviel Speicher ich brauche, erst > zur Laufzeit, und dann tue ich mich ohne dynamische SV schwer ;-) Ok, wenn Du Deine Konfiguration dynamisch anpassen musst. Vielleicht hast Du am Systembus ja auch hot-swappable devices. Dann sollte man vielleicht den Focus auf spezielle Allokatoren richten.
Johann L. schrieb: > Wenn g++ das also nicht macht, dann passt der Startup-Code nicht zum > Compiler bzw. das Backend ist Murx, oder es wurde dem Compiler explizit > abgewöhnt, was doch reichlich fragwürdig erscheint. Leider wurschtelt in der ARM-Welt (anders als bei AVR) so ziemlich jeder mit eigenem Startup-Code und eigenen Linkerscripts herum, die oft genug beliebig falsch sein können. Du glaubst gar nicht, was einem da alles übern Weg laufen kann. ;-) Das fängt an bei „vergessenen“ Sternen in den linker input sections (sodass mit -ffunction-sections compilierte Objekte bspw. aus der Bibliothek dann teilweise nicht erfasst werden) und geht bis dahin, dass der tolle Schreiberling des Linkerscripts den initialen Stackpointer nicht auf direkt hinter das Ende des RAMs gesetzt hat (ARM macht immer pre-decrement, und das ist so dokumentiert), sondern „vorsichtshalber“ vier Byte darunter. Das merkt man erst dann, wenn man das erste Mal printf() mit einem double-Argument benutzt, dann rennt man nämlich plötzlich in einen alignment fault, weil „double“ auf 64 Bit ausgerichtet sein muss, dass aber mit diesem verkorksten Stack nicht mehr garantiert ist. ARM hat zwar wohl viel beim Compiler selbst getan, aber so eine „Rundum-Sorglos-Option“ wie -mmcu= beim AVR haben sie einfach versäumt. Das hätte aber auch nur ARM selbst in die Hand nehmen können, die einzelnen Lizenznehmer interessieren sich nicht für die Konsistenz der Toolchain, die rasseln einfach irgendwas in ihre bevorzugte IDE hinein, und der Kunde muss dann mit dem Salat leben.
Torsten R. schrieb: > Peter II schrieb: > >> meinst du das der Konstruktor von globalen Objekte nicht aufgerufen >> wird? Das kann ich mir gar nicht vorstellen. > > Ja, zumindest der gcc Startup code von Nordic und ST tut es ganz sicher > nicht. Der Assembler Startup von ST ruft __libc_init_array auf. Innerhalb jener finden Konstruktur-Aufrufe statt. Jeder der behauptet das C/C++ auch nur irgendeine Initialisierung garantiert, hat noch nie ein bare-metal Projekt auf die Beine gestellt. Wenn ich lustig bin boot ich einen Cortex M in 2x Zeilen Assembler und spring dann in main, ohne auch nur einen Finger zu rühren... Und um nach 50 teils sinnlosen, teils komplett irrelevanten Antworten auch noch die ursprüngliche Fragestellung zu beantworten: Kaj G. schrieb: > Spezieller Teil: > Wo findet die Initialisierung statt? Muss da (in Abhaengigkeit der > Platform?) Code vom Compiler fuer generiert werden? Oder findet das > ganze im Startup-Code statt (welcher im Embeddedbereich ja oftmals von > den Herstellern geliefert wird)? Zur Compilezeit bekannte Initialisierungswerte landen (zusammen mit Programm-Code und sonstigen Konstanten) in einem als ".text" benannten Bereich. Damit der Startup-Code diese verschiedenen Bereiche im Flash wieder findet exportiert das Linkerscript einen Haufen Symbole. Diese Symbole werden für die jeweiligen Anfangs- und Endadressen der benötiten Daten erzeugt. Die "Standardbezeichnungen" sind meist
1 | _sdata (__data_start__) |
2 | _edata (__data_end__) |
3 | |
4 | __preinit_array_start
|
5 | __preinit_array_end
|
6 | |
7 | __init_array_start
|
8 | __init_array_end
|
9 | |
10 | __fini_array_start
|
11 | __fini_array_end
|
Der Bereich _sdata bis _edata ist bei ein simpler "copy/paste" Block, sprich dieser Bereich wird am Anfang schlichtweg 1:1 ins RAM kopiert. __preinit und __init sind Arrays vom Typ void(*)(). Die enthaltenen Function Pointer rufen Initialisierungs-Code auf, der vom Compiler erzeugt wird und für Konstrukturen gedacht ist. Über genau jenes Array zu iterieren ist übrigens die Aufgabe des GCC builtin "__libc_init_array". Der Bereich __fini ist das Destruktur Pendant zu __preinit und __init und wird für µC eher selten gebraucht.
Vincent H. schrieb: > Torsten R. schrieb: > > Jeder der behauptet das C/C++ auch nur irgendeine Initialisierung > garantiert, hat noch nie ein bare-metal Projekt auf die Beine gestellt. Natürlich garantiert C++ die Initialisierung. Das steht so im Standard! Punkt! Wenn Du natürlich an dem Compiler/Linker irgendetwas verbiegst, dann eben nicht mehr.
Wilhelm M. schrieb: > Vincent H. schrieb: >> Torsten R. schrieb: >> >> Jeder der behauptet das C/C++ auch nur irgendeine Initialisierung >> garantiert, hat noch nie ein bare-metal Projekt auf die Beine gestellt. Bitte das mit dem Zitieren noch mal üben!!!
Torsten R. schrieb: > Wilhelm M. schrieb: >> Vincent H. schrieb: >>> Torsten R. schrieb: >>> >>> Jeder der behauptet das C/C++ auch nur irgendeine Initialisierung >>> garantiert, hat noch nie ein bare-metal Projekt auf die Beine gestellt. > > Bitte das mit dem Zitieren noch mal üben!!! Arrg, bitte um Entschuldigung - noch nicht genügend Kaffee ;-)
Jörg W. schrieb: > > ARM hat zwar wohl viel beim Compiler selbst getan, aber so eine > „Rundum-Sorglos-Option“ wie -mmcu= beim AVR haben sie einfach versäumt. > Das hätte aber auch nur ARM selbst in die Hand nehmen können, die > einzelnen Lizenznehmer interessieren sich nicht für die Konsistenz der > Toolchain, die rasseln einfach irgendwas in ihre bevorzugte IDE > hinein, und der Kunde muss dann mit dem Salat leben. ...das kann man leider so nicht stehen lassen - eine "Rundum Sorglos Option" würde notwendigerweise mit manchen Architekturen nicht kompatibel sein und deswegen nur eine Teilmenge der technisch möglichen Hardwaredesigns unterstützen (Beispiel .bss im SDRAM, siehe oben). Ist das uralte Dilemma zwischen Kontrolle und Abstraktion, das sich wie ein roter Faden durch die Embedded Welt zieht. In der Vorstellungswelt Mancher würde auf jedem Embedded System der Welt von der Chipkarte bis zur LkW Steuerung Android oder kompatibel laufen, und man könnte/dürfte (von einer Handvoll Gurus abgesehen) nur noch Java programmieren, bräuchte sich aber dafür um Architekturen/Systeme/Startup/OS etc. keine Gedanken mehr zu machen (Für mich eine Horrorvision). Auf der Anderen Seite diejenigen, die am Liebsten Ihre Controller noch selbst designed würden und selbst dynamischer Speicherverwaltung (von RTOS ganz zu schweigen) mißtrauisch gegenüberstehen (Für mich eher retro und nicht zukunfsträchtig, auch wenn ich die Idee charmant finde). Viel von der Zukunft der Embedded Welt wird davon abhängen, wo auf dem Spektrum sich ein "Standard" ansiedeln wird.
Kaj G. schrieb: > Soweit ich weiss ... garantiert mir > der C-Standard, dass globale Variablen immer mit 0 initialisiert werden, > es sei denn, der Programmierer gibt einen Wert vor. Wilhelm M. schrieb: > Natürlich garantiert C++ die Initialisierung. Das steht so im Standard! > Punkt! Soso. Ihr beiden meint, daß euch ein Standard irgend etwas garantiert, ja? Mal im Klartext: Ein Standard ist allenfalls ein Haufen Druckerschwärze auf einem Stück Papier - mehr nicht. Ein Standard kann GARNICHTS garantieren. Allenfalls eine Empfehlung formulieren. Es ist auch völig schnurz, ob und was in .bss oder .data oder sonstwo in einem .elf File hinkommt. Das, was in einen µC gebrutzelt wird, ist rein binär und kommt für gewöhnlich aus einem Hexfile oder einem schlichten Binärfile. So. Punkt. Es gibt ne Menge dummer Eierköpfe, die da meinen, sich auf obengenannte Zusagen verlassen zu können - weil es ja in einem Standard steht. Chan's FAT-Filesystem gehört z.B. dazu. Und wenn man sowas unreflektiert benutzt, dann passiert es einem gelegentlich, daß man damit auf die Nase fällt. Ich kann eigentlich jedem nur dazu raten, auf einem µC-System grundsätzlich davon auszugehen, daß jede Variable solange uninitialisiert ist, bis man sie tatsächlich selbst initialisiert hat. Sich drauf zu verlassen, daß da ab urbe condita eine Null drin steht, ist grob fahrlässig. W.S.
W.S. schrieb: > Ich kann eigentlich jedem nur dazu raten, auf einem µC-System > grundsätzlich davon auszugehen, daß jede Variable solange > uninitialisiert ist, bis man sie tatsächlich selbst initialisiert hat. > Sich drauf zu verlassen, daß da ab urbe condita eine Null drin steht, > ist grob fahrlässig. du geht aber auch von Standards selbstverständlich aus. Prüfst du ob 1+1 = 2 ist? du verlässt dich bestimmt auf memset - ist auch nur ein Standard, kann ja eventuell überall etwas anderes machen. Wenn man C(++) Programmiert, muss man sich auf den Standard verlassen können, sonst macht das überhaupt keine sinn damit zu arbeiten.
Peter II schrieb: > Wenn man C(++) Programmiert Nicht nur wenn man programmiert: Das ist in jedem technischen Bereich so. Wenn ich eine M8-Mutter kaufe, dann kann ich mich darauf verlassen, dass die Mutter auf eine M8-Schraube passt und auch darauf, das der Schraubenschluessel passt. Da muss ich keine Angst haben, dass die M8-Mutter ploetzlich nur auf eine M6- oder eine M10-Schraube passt oder ich mir womoeglich erst noch selber eine passende Schrauben basteln muss, weil es keine passende gibt. Genau fuer sowas sind Standards da! Wenn du dich nicht daran haeltst, ist das dein Problem! Ist dein System nicht standardkonform, hast du ganz andere Probleme als eine nicht initialisierte Variable. Wenn ich mich nicht auf den Standard verlassen kann, wie kann ich mir dann sicher sein, das i = 0 auch wirklich i auf 0 setzt und nicht auf 42?
W.S. schrieb: > Ich kann eigentlich jedem nur dazu raten, auf einem µC-System > grundsätzlich davon auszugehen, daß jede Variable solange > uninitialisiert ist, bis man sie tatsächlich selbst initialisiert hat. Das hilft dir überhaupt nichts. Diese deine „Initialisierung“ ist genauso viel oder wenig wert wie das Ausnullen der anderen Variablen: wenn der Startup-Code das Umkopieren aus dem Flash vermasselt oder der Linkerscript es nicht mit in die zu kopierenden Daten übernommen hat, dann hilft auch ein (überflüssigen) Hinschreiben von „= 0“ rein gar nichts: die Variable ist danach nicht mehr und nicht weniger „initialisiert“ als vorher. Wenn du dich aber drauf verlassen kannst, dass das „= 0“ (oder eben auch ein „= 42“) tatsächlich eine Wirkung hat, dann kannst du dich bei C oder C++ (und darum ging's im Thread-Titel) auch drauf verlassen, dass globale und statische Variable mit 0 vorbelegt sind. Die Eierköpfe sind aber natürlich immer die anderen … Ruediger A. schrieb: > eine "Rundum Sorglos Option" würde notwendigerweise mit manchen > Architekturen nicht kompatibel sein und deswegen nur eine Teilmenge der > technisch möglichen Hardwaredesigns unterstützen (Beispiel .bss im > SDRAM, siehe oben). Sie funktioniert für alle nicht Spezialfälle auf dem AVR erwiesenermaßen verdammt gut. Für Spezialfälle kann man sich ja immer noch was eigenes zimmern, aber es ist einfach K*cke, dass man beim ARM sich immer seinen eigenen Mist zusammenstoppeln muss, und dabei natürlich beliebig viele Fehler reinbringt (die sich oft genug auch durch die im Netz verfügbaren Musterexemplare ziehen).
Jörg W. schrieb: > aber es ist einfach K*cke, dass man > beim ARM sich immer seinen eigenen Mist zusammenstoppeln muss, und > dabei natürlich beliebig viele Fehler reinbringt (die sich oft genug > auch durch die im Netz verfügbaren Musterexemplare ziehen). Das stimmt, hat aber auch mit der weit verbreiteten "ich krame mir das Nächstbeste von irgendeinem wohlmeinenden Amateur ins Netz gestellte Codefragment heraus und bin zufrieden, wenn es zu funktionieren scheint" Mentalität zusammen (die logischerweise sehr eng mit der Java Baukastenphilosophie zusammenhängt). Leider wird die auch oft von Ökosystemherstellern zur Kostereduzierung verfolgt, also was sich in Beispielcode von Herstellern findet, ist nicht notwendigerweise besser als open source contributions...
Ruediger A. schrieb: > Leider wird die auch oft von Ökosystemherstellern zur Kostereduzierung > verfolgt, also was sich in Beispielcode von Herstellern findet, ist > nicht notwendigerweise besser als open source contributions. Ja, das ist das, was ich meinte: eine sauber gepflegte Kombination aus Startup-Code und Linkerscript, die ARM zusammen mit der Toolchain herausgibt, wäre hier sehr viel sinnvoller gelaufen. avr-libc zeigt, dass man trotzdem sehr viele Freiheiten einbauen kann für Modifikationen (Lage von Stack oder Heap, zusätzliche Init-Sections, Variable, die bei Reset in Ruhe gelassen werden). AVR-GCC / avr-libc sind nun zwar nicht von Atmel gepflegt worden, aber eben immer so einigermaßen Hand in Hand parallel entwickelt. Bei ARM hat man natürlich keinerlei Interesse der Lizenznehmer (= Halbleiterhersteller), sowas zu tun, aber es hätte im Interesse der ARM Ltd. selbst liegen können, das zu machen.
Meiner Ansicht nach gehört - im Gegensatz zu einer auf einem OS aufsetzenden Applikation - der Startupcode untrennbar mit zu einem embedded Programm. Deshalb schreibt man den selbst, dann weiß man was er macht und auch, wem man in den Hintern treten muß, wenn er nicht das macht, was er soll.
Markus F. schrieb: > Meiner Ansicht nach gehört - im Gegensatz zu einer auf einem OS > aufsetzenden Applikation - der Startupcode untrennbar mit zu einem > embedded Programm. > > Deshalb schreibt man den selbst, dann weiß man was er macht und auch, > wem man in den Hintern treten muß, wenn er nicht das macht, was er soll. für globale Variabel spielt es keine Rolle ob man ein BS hat oder eine Anwendung.
W.S. schrieb: > Soso. > Ihr beiden meint, daß euch ein Standard irgend etwas garantiert, ja? > > Mal im Klartext: Ein Standard ist allenfalls ein Haufen Druckerschwärze > auf einem Stück Papier - mehr nicht. Ein Standard kann GARNICHTS > garantieren. Allenfalls eine Empfehlung formulieren. Das bedeutet, dass Du dem gcc, clang, you-name-it Compiler, der das Label Standard-konform trägt, grundsätzlich nicht traust, und jedesmal den Assembler-Code kontrollierst. Nein, wahrscheinlich den Binärcode, weil Du ja nicht sicher sein kannst, dass der Assembler richtig funktioniert. Nein, wahrscheinlich die Bitmaske auf dem Chip, weil Du ja nicht wissen kannst, ob der Hersteller das richtig umgesetzt hat. Denn auch die Hersteller-Doku ist ja nur ein Stück Papier ... Leute, wo sind wir denn?
:
Bearbeitet durch User
Wilhelm M. schrieb: > Leute, wo sind wir denn? In einer Welt, in der Chiphersteller keine standard-konformen Linkerscripts und Startup-Code ausliefern.
Jörg W. schrieb: > Das hilft dir überhaupt nichts. > > Diese deine „Initialisierung“ ist genauso viel oder wenig wert wie > das Ausnullen der anderen Variablen Schreibe nicht so einen albernen Unfug! Ich werde jetzt wirklich BÖSE. Und du solltest dich ob solcher Worte schämen. Meine Art, mich eben nicht auf Obengenanntes zu verlassen ist eben mehr wert, denn sie verläßt sich NICHT auf verordnete Vorbelegungen, weil das von Grund auf unsicher ist. Stichwort: Wiederaufsetzen des Systems nach Absturz, Exceptions usw. Jeder normalverständige Programmierer kennt diese Regel, sich nicht auf Vorbelegungen durch irgendwen zu verlassen. Genau das wird hier regelmäßig gröblichst mißachtet - weil ja alle so unsäglich schlau sind - oder sich selbst dafür halten, da ja wie gesagt der Standard sagt... Palmström, etwas schon an Jahren, wird an einer Straßenbeuge und von einem Kraftfahrzeuge überfahren. .... Und er kommt zu dem Ergebnis: »Nur ein Traum war das Erlebnis. Weil«, so schließt er messerscharf, »nicht sein kann, was nicht sein darf!« Eben. Soviel zu Standards. Peter II schrieb: > du geht aber auch von Standards selbstverständlich aus. Prüfst du ob 1+1 > = 2 ist? Ja, hab ich - mit zwei Fingern. Und - OH WUNDER - es stimmt tatsächlich. Erwiesenermaßen! Kaj G. schrieb: > Wenn ich eine M8-Mutter kaufe, dann kann ich mich darauf verlassen, dass > die Mutter auf eine M8-Schraube passt und auch darauf, das der > Schraubenschluessel passt Nö. erstens ist das ein alberner und unpassender Vergleich und zweitens hab ich grad neulich bei diversen Aufspannschrauben für Fräsarbeiten Muttern in SW17, SW18 und SW19 vorgefunden. Soviel zu passenden Schraubenschlüsseln. Jetzt könnte ich hingegen so argumentieren, daß man sich - zumindest hier in Berlin - auch nicht mehr drauf verlassen kann, daß LKW-Fahrer sich an die Straßenverkehrsregeln halten... Ein Glück für mich persönlich, daß unsereiner in Spandau war. Also: Werdet mal nicht albern - so kurz vor Weihnachten. Zum 1.April ist das ja ok, aber grad jetzt nicht. W.S.
Von ARM Ltd. gepflegte Beispiel-Linkerscripts und Beispiel Startupcode finden sich in /usr/share/gcc-arm-embedded/samples und die sehen verdächtig so aus als ob sie die Grundlage für das waren was man in den diversen Hersteller-Codebasen findet. > Sternchen vergessen Fehler passieren. Hast Du den Bug gemeldet? Wurde er nicht behoben? > Sorglos-Paket Die jeweiligen Sorglos-Pakete sind die komplett IDEs der jeweiligen Hersteller die man sich hier gerne den ganzen Tag lang gegenseitig wärmstens empfiehlt bzw. nachfragt. Da klickst Du dreimal mit der Maus und fertig ist das Sorglos-Projekt. Natürlich muss man sich im klaren sein was das Sorglos-Paket denn alles enthalten soll: Mit dem Startup-Code allein hast Du noch keinen Sorglos-Zustand erreicht, bei weitem nicht. Du musst mindestens auch noch die Clock-Initialisierung mitliefern damit das gleiche Feeling aufkommt wie damals bei AVR. Und dann musst Du das auch noch so flexibel implementieren daß man aus allen denkbaren 42 Dutzend Optionen pro Prozessor(-Variante!) wählen kann um alles vollständig abzudecken. Es ist schon verständlich daß man hier bei ARM irgendwo eine Grenze zieht und das Herstellerspezifische an die einzelnen Hersteller delegiert (oder optional auch an die Anwender). Wenn die Hersteller nicht zeitnah auf Bug-Reports reagieren (würde eine zentrale Instanz bei ARM das tun?) dann liegt das erstmal nicht an der Arbeitsteilung an sich sondern am jeweiligen Verhalten der für die einzelnen Teile Verantwortlichen. Es hat sich auch gezeigt daß verteilte Softwareentwicklung wesentlich weniger schwerfällig ist und zu mehr Wahlmöglichkeiten des Anwenders führt Ich sehe also hier nicht wirklich ein Problem. Der Anwender kann (nach aufsteigendem Skill-Level sortiert): * Das vorhandene "Sorglos-Paket" (IDE oder SDK) des Herstellers verwenden * Fehler im "Sorglos-Paket" an den Hersteller melden * Eine alternative Projektvorlage verwenden die er bei github findet * Wenn er einen Fehler findet kann er den beim jeweiligen Autor melden * Er kann Fehler auch selber beheben und einen Pull-Request schicken * Er kann selber eine Vorlage schreiben und für sich behalten * Er kann selber eine schreiben und für andere veröffentlichen
W.S. schrieb: > Soso. > Ihr beiden meint, daß euch ein Standard irgend etwas garantiert, ja? Er garantiert natürlich nicht, dass kaputte und verkorkste Toolchains richtig funktionieren. Das Problem ist dann aber die Toolchain. Mit irgendwelchen Annahmen dazu, was der Compiler/Linker aus meinem Code macht, muss ich ja starten. W.S. schrieb: > Jörg W. schrieb: >> Das hilft dir überhaupt nichts. >> >> Diese deine „Initialisierung“ ist genauso viel oder wenig wert wie >> das Ausnullen der anderen Variablen > > Schreibe nicht so einen albernen Unfug! > Ich werde jetzt wirklich BÖSE. > Und du solltest dich ob solcher Worte schämen. Warum? Er hat recht. Wenn du die Variable explizit mit 0 initialisierst, ist genau der selbe Teil des Startup-Code dafür verantwortlich, die 0 da reinzubringen, wie wenn du sie nicht explizit initialisierst. Wenn genau der Teil des Startup-Code kaputt ist (wie bei zu vielen µC-Toolchains leider der Fall), so dass die implizite Initialisierung nicht funktioniert, wird die explizite ganz genauso nicht funktionieren. > Peter II schrieb: >> du geht aber auch von Standards selbstverständlich aus. Prüfst du ob 1+1 >> = 2 ist? > > Ja, hab ich - mit zwei Fingern. Und - OH WUNDER - es stimmt tatsächlich. > Erwiesenermaßen! Er meinte vermutlich nicht, ob du das mit deinen Fingern nachgezählt hast, sondern ob du explizit getestet hast, dass dein Compiler das auch hinbekommt. Ist ja schließlich der selbe Standard, nach dem auch die Initialisierung geregelt ist. Wenn du dich auf den nicht verlässt, gibt's quasi gar nichts, das du in C noch sicher hinschreiben kannst.
Rolf M. schrieb: > Wenn genau > der Teil des Startup-Code kaputt ist (wie bei zu vielen µC-Toolchains > leider der Fall) Bei welchen zum Beispiel?
W.S. schrieb: > Jeder normalverständige Programmierer kennt diese Regel, sich nicht > auf Vorbelegungen durch irgendwen zu verlassen. Was für ein Unsinn. Jeder normalverständige Programmierer kennt diese Regel, sich nicht auf ZUSAMMENGEFRICKELTE TOOLS durch irgendwen zu verlassen.
Johann L. schrieb: > W.S. schrieb: >> Jeder normalverständige Programmierer kennt diese Regel, sich nicht >> auf Vorbelegungen durch irgendwen zu verlassen. > > Was für ein Unsinn. > > Jeder normalverständige Programmierer kennt diese Regel, sich nicht > auf ZUSAMMENGEFRICKELTE TOOLS durch irgendwen zu verlassen. Jeder normalverständige Programmierer kennt zumindest die Handbücher seiner Tools. Meistens sind dort auch Abweichungen vom Standard dokumentiert.
W.S. schrieb: > Meine Art, mich eben nicht auf Obengenanntes zu verlassen ist eben > mehr wert, denn sie verläßt sich NICHT auf verordnete Vorbelegungen Doch, das tut sie. Weil jedenfalls der GCC nämlich intelligent genug ist, daß er es bemerkt, wenn Du eine statische Variable mit 0 initialisierst. Die legt er dann nämlich nicht etwa in die Data-Sektion, sondern in die Bss-Sektion. Das kann man mit dem Mapfile auch leicht nachweisen. Das spart Platz im ROM, deswegen macht GCC das so. Mit anderen Worten, GCC verläßt sich darauf, daß die Bss-Sektion so genullt wird, wie es der C-Standard auch vorgibt. Abgesehen davon bist Du doch ansonsten auch ein Verfechter der Lowlevel-Eigenimplementation. Also ist es doch Dein eigener Assemblercode im Startupfile, der das besorgt, wo liegt denn das Problem überhaupt?
Nop schrieb: > Weil jedenfalls der GCC nämlich intelligent genug ist, daß er es > bemerkt, wenn Du eine statische Variable mit 0 initialisierst. Selbst, wenn er es nicht wäre: wenn man sich nicht auf das Füllen mit 0 verlassen kann, gibt es keinen Grund zu der Annahme, dass man sich auf das Füllen mit anderen expliziten Initialwerten verlassen könnte. Beide Code-Teile (im Startupcode) stehen gleich nebeneinander. Wenn einer davon kaputt ist, dann ist es nicht weniger wahrscheinlich, dass es der andere auch ist. Da W. S. aber ohnehin selbst ernannter C-Nichtkenner ist, sei ihm die Ignoranz nachgesehen. Er sollte dann nur aufhören, über Dinge zu schreiben, die er gar nicht kennen will.
Ich hatte hier auch schon mal eine Diskussion ob die Kenntnis des Startupcodes notwediger Teil des Entwickelns ist oder nicht. Nachdem mir die ganzen IDE-Junkies Blödheit unterstellt haben, hab ich es aufgegeben. Seht euch doch mal an was die großen IDE Hersteller aus dem Startupcode machen oder gemacht haben. Da gibt es einen oder besser ein Template für den Startup-Code und das Linkerscript das für alle von der IDE unterstützten Controller irgendwie passen muss. Das eigentliche Linkerscript erzeugen Generatoren, Besonderheiten muss der Präprozessor abhandeln. Standards gibt es da keine. Man kann sich jetzt entscheiden, ob man von den Zusammenhängen nichts verstehen will weil es ja so kompliziert oder unnötig ist oder ob man sein Projekt doch lieber selbst im Griff haben will. Bei zweitem würde o.g. Frage gar nicht auftreten. Oder anders, wenn ich weiß dass in meinem Startup-Code die Variablen nach C/C++ Standard initialisiert werden, kann ich mir eine Initialisierung mit 0 sparen. Und ob Konstruktoren vernünftig gerufen werden weiß ich dann auch. Auch habe ich die Möglichkeit erst die prozessorinternen Bereiche zu initalisieren, danach externes SDRAM in Betrieb zu nehmen und dann die Variablen und Objekte in den Segmenten die den SDRAM zugeordnet sind zu behandeln. Hin und wieder werden bei cortexen auch mal Interrupt-Handler in den RAM gelegt um Flashwaitstates zu sparen. Dann ist die Kenntnis des Linkerscripts unerlässlich. Damit ist die Portierbarkeit des Projektes von einer IDE zur nächsten meistens Geschichte. Mann kann sich aber auch gleich eigene Linkerscripte und Startupcode schreiben der dann nicht mehr von irgendwelchen IDE Einstellungen abhängig ist. Bei größeren Projekten wo das im Gesamtzeitaufwand für das Projekt ehr zweitrangig ist, ist das sowieso die bessere Wahl. Und die die z.B. Software für weisse Ware schreiben und RAM-, FLASH- und andere Tests zwangsweise in ihren Code einbauen müssen. können sich ohnehin nicht auf den "Segnungen" moderner IDE's verlassen. Und wenn man den Code schon nicht selber schreibt, kann man ja mal einen Breakpoint auf den "ResetHandler" oder "reset_handler" oder reset-handler" oder auf das was die IDE meint ein guter Name dafür zu sein, setzen und mal Schrittweise zu verfolgen was da passiert. Schon allein damit gehört man nicht mehr zu den Unwissenden und sich Draufverlassenden.
temp schrieb: > Schon > allein damit gehört man nicht mehr zu den Unwissenden und sich > Draufverlassenden. Dagegen hilft aber - ich schrieb es bereits - lesen. Ich weiß ja nicht, mit welchen Gammel-IDEs und Gammel-Compilern manche Leute hier arbeiten (müssen), aber zumindest in meinen bescheidenen Anwendungsfällen hat ein Blick ins ABI-Manual noch immer geholfen. Das bleibt ansonsten ja auch nicht aus. Man kann ein Linker-Skript auch nicht einfach am Compiler vorbeischreiben. Danke einfach mal an Trampolines.
Jörg W. schrieb: > Selbst, wenn er es nicht wäre: wenn man sich nicht auf das Füllen > mit 0 verlassen kann, gibt es keinen Grund zu der Annahme, dass man > sich auf das Füllen mit anderen expliziten Initialwerten verlassen > könnte. Beide Code-Teile (im Startupcode) stehen gleich nebeneinander. > Wenn einer davon kaputt ist, dann ist es nicht weniger wahrscheinlich, > dass es der andere auch ist. > > Da W. S. aber ohnehin selbst ernannter C-Nichtkenner ist, Oh, danke für die Blumen, aber du scheinst in aller Bescheidenheit mal so ausgedrückt ein Brett vor dem Kopfe zu haben. Deshalb erkläre ich dir das mal im Detail anhand eines UART-Treibers: Da hat man einen Ringpuffer und zwei zugehörige Indexvariablen, einer ist der Schreib-Index, der andere der Lese-Index. Soweit klar? gut. Beim Initialisieren des UART's fällt natürlich auch an, daß man den Ringpuffer ebenfalls initialisieren sollte. Konkret heißt das, daß Schreib- und Lese-Index sinnvoll zurückgesetzt werden müssen. Ich schreibe dort beim Initialisieren des seriellen Kanals dediziert in beide Indizes Null hinein, so daß beide auf Puffer[0] weisen. Und das tue ich nicht im Startupcode, sondern im seriellen Treiber. Immer noch klar? ok. Jetzt kommst du und faselst von Codeteilen im Startupcode. Nonsense sowas. Wer sich auf die "Standards" verläßt, würde hier sagen, daß per Standard ja ohnehin in beiden Indizes bereits eine Null drinstehen müßte (Palmström!) - aber ICH verlasse mich auf so etwas nicht und jeder andere verständige C- (odern Nicht-C-) Programmierer tut sowas ebenfalls nicht. Hast du das jetzt ENDLICH verstanden? Nochwas zu den üblichen Startupcodes: Die sehen zumindest bei ARM, Cortex sich alle verdammt ähnlich: Zumeist wird dort ziemlich direkt in main gesprungen, manchmal sogar per BX und nicht per BLX. Dazu kommt, daß die Default-Handler eigentlich immer nur aus einem B. bestehen. Sehr viel seltener findet man Startups, die tatsächlich Variablen vorinitialisieren. Kurzum, auf wirklich umfassend gute Startupcodes darf man sich eben NICHT verlassen, egal wie laut jetzt alle Anderen "ABER DIE STANDARDS!" schreien. So, nochwas: Im allgemeinen erwartet man von Moderatoren, daß sie sich etwas weniger daneben benehmen als die übrigen User, aber darauf ist - wie man sehen kann - etwa so viel Verlaß wie auf das Ablöschen und Vorbelegen von Variablen. In dem Sinne: Frohe Weihnachten! Es ist alles nur halb so schlimm. W.S.
W.S. schrieb: > So, nochwas: Im allgemeinen erwartet man von Moderatoren, daß sie sich > etwas weniger daneben benehmen als die übrigen User, aber darauf ist - > wie man sehen kann - etwa so viel Verlaß wie auf das Ablöschen und > Vorbelegen von Variablen. Bedeutet das, daß Jörg, weil er Mitautor einer sich an Standards haltenden AVRlibc ist, nicht moderieren darf? Oder muß er, weil er moderiert, das Entwickeln von OSS einstellen? Und wenn's ums Verständnis geht: wer dem Startupcode nicht zutraut, .bss-Daten auf 0 zu setzen, der darf dem gleichen Code auch nicht zutrauen, die im Flash hinterlegten Initialisierungsdaten ins RAM zu kopieren. Muß man auch nicht, denn man kann das leicht überprüfen, so man dann die Sprache der Ziel-HW versteht. Wenn man dann noch seiner Festplatte zutraut, daß der Startup-Code morgen noch der Selbe ist...
W.S. schrieb: > Sehr viel seltener findet man Startups, die > tatsächlich Variablen vorinitialisieren. Ich hab noch keinen gesehen der das nicht tut. Zeig doch mal einen!
W.S. schrieb: > So, nochwas: Im allgemeinen erwartet man von Moderatoren, daß sie sich > etwas weniger daneben benehmen als die übrigen User Dafür sorgst du als einer der "übrigen User" ja schon, indem du mal wieder (wie üblich) pauschal alle zu Idioten erklärst, die nicht deiner Meinung sind - garniert mit den leider ebenfalls üblichen Beleidigungen: W.S. schrieb: > Es gibt ne Menge dummer Eierköpfe, die da meinen, sich auf obengenannte > Zusagen verlassen zu können W.S. schrieb: > Schreibe nicht so einen albernen Unfug! W.S. schrieb: > du scheinst in aller Bescheidenheit mal so ausgedrückt ein Brett vor dem > Kopfe zu haben. W.S. schrieb: > Nonsense sowas. Mit so einem Diskussionsstil bist du der letzte hier, der das Recht hätte, anderen was von wegen "daneben benehmen" vorzuhalten.
Wir könnten ja bloss so zum Spass und zur Wahrung des Friedens der kommenden Feiertage mal versuchen, die Sprache und den Implementierung auseinander zu halten. Denn die Sprachen C und C++ garantieren es, aber in den jeweiligen Implementierungen davon kann es Löcher geben. Eine dem Sprachstandard nicht folgende Implementierung darf man als fehlerhaft bezeichnen. In diesem Fall ist es aber nicht C, das den Fehler hat, sondern die Implementierung. Wenn man das rückwirkend auf die Sprache bezieht, dann landet man irgendwann bei der Frage, ob man sich drauf verlassen kann, dass in if(0) wirklich nichts ausgeführt wird. Ergo: Eine Liste, welche Entwicklungsumgebungen in dieser Frage Probleme haben ist sinnvoller als verklausulierte Fundamentalkritik an der Sprache. Und ggf. auch welche Chip-Anpassungen darin, denn die Entwicklungsumgebungen im Embedded-Bereich haben ja spezielle Anpassungen an die diversen Zielsysteme und die müssen nicht alle gleich sein, wenn also Cortex M7 ok ist, STR9 aber nicht. Oder brauchen wir wirklich den dreiunddröllstigen Thread über C/C++ vs ASM, mit den üblichen Verdächtigen auf beiden Seiten?
:
Bearbeitet durch User
Johann L. schrieb: > Jeder normalverständige Programmierer kennt diese Regel, sich nicht > auf ZUSAMMENGEFRICKELTE TOOLS durch irgendwen zu verlassen. Ausser natürlich, man hat die Tools selber zusammengefrickelt. ;-)
W.S. schrieb: >> Diese deine „Initialisierung“ ist genauso viel oder wenig wert wie >> das Ausnullen der anderen Variablen > > Schreibe nicht so einen albernen Unfug! > Ich werde jetzt wirklich BÖSE. Im Unterschied zu dir weiss er wovon er schreibt.
A. K. schrieb: > Johann L. schrieb: >> Jeder normalverständige Programmierer kennt diese Regel, sich nicht >> auf ZUSAMMENGEFRICKELTE TOOLS durch irgendwen zu verlassen. > > Ausser natürlich, man hat die Tools selber zusammengefrickelt. ;-) So siehts aus. Denn dann hat man sich ja höchstpersönlich selbst vom ordnungsgemäßen Zustand der 49 Kugeln überzeugt. Und wenns dann immer noch nicht geht weiß man zumindest wer schuld ist und kann den kurzfristig auf dem kurzen Dienstweg selbst abwatschen.
W.S. schrieb: > Kurzum, auf wirklich umfassend > gute Startupcodes darf man sich eben NICHT verlassen Ich wiederhole meine Frage: ansonsten verläßt Du Dich auch nicht auf irgendwelche HALs, SPLs, IDEs und sonstwas, sondern befürwortest Lowlevel-Eigenimplementation, weil Du dann weißt, was abläuft. Wieso nicht beim Startup-Assembler?
Wir können ja auch gerne mal konkret werden: Der Startupcode, den ich gerade verwende [1], macht es richtig:
1 | ... |
2 | |
3 | CopyDataInit: |
4 | ldr r3, =_sidata |
5 | ldr r3, [r3, r1] |
6 | ... |
7 | |
8 | FillZerobss: |
9 | movs r3, #0 |
10 | str r3, [r2], #4 |
11 | ... |
Wenn W.S. eine kaputte Implementierung kennt, könnte er die hier gern mal benennen.... [1] ~/.ac6/SW4STM32/firmwares/STM32Cube_FW_F4_V1.13.1/Drivers/CMSIS/Device/S T/STM32F4xx/Source/Templates/gcc/startup_stm32f446xx.s
:
Bearbeitet durch Moderator
here schrieb: > STM32Cube Und die springt hinterher auch nach __main in der standard library wo die statischen Konstruktoren aufgerufen werden und mitnichten direkt in die main. Das widerlegt auch den anderen der gestern behauptet hat bei ST wäre dies nicht der Fall.
here schrieb: > Wir können ja auch gerne mal konkret werden: > Der Startupcode, den ich gerade verwende [1], macht es richtig: >
1 | > ... |
2 | > |
3 | > CopyDataInit: |
4 | > ldr r3, =_sidata |
5 | > ldr r3, [r3, r1] |
6 | > ... |
7 | > |
8 | > FillZerobss: |
9 | > movs r3, #0 |
10 | > str r3, [r2], #4 |
11 | > ... |
12 | > |
> > Wenn W.S. eine kaputte Implementierung kennt, könnte er die hier gern > mal benennen.... > > [1] > ~/.ac6/SW4STM32/firmwares/STM32Cube_FW_F4_V1.13.1/Drivers/CMSIS/Device/S T/STM32F4xx/Source/Templates/gcc/startup_stm32f446xx.s Ich hab noch kein ST Linkerscript gesehen, dass es erlaubt Variablen im CCRAM / SRAM2 zu initialisieren. Somit ist dieser Startup-Code auch nicht "Standard" koform.
Vincent H. schrieb: > Ich hab noch kein ST Linkerscript gesehen, dass es erlaubt Variablen im > CCRAM / SRAM2 zu initialisieren. Wie kriegst du diese Variablen da rein, dabei im Standard bleibend?
A. K. schrieb: > Vincent H. schrieb: >> Ich hab noch kein ST Linkerscript gesehen, dass es erlaubt Variablen im >> CCRAM / SRAM2 zu initialisieren. > > Wie kriegst du diese Variablen da rein, dabei im Standard bleibend? Ganz einfach, Linkerscript und Startup-Code müssen angepasst werden.
Vincent H. schrieb: >> Wie kriegst du diese Variablen da rein, dabei im Standard bleibend? > > Ganz einfach, Linkerscript und Startup-Code müssen angepasst werden. Eben. Wie geliefert verhalten sich beide konform zum Standard. Und wer eines davon an eigene Wünsche anpasst ist auch dafür verantwortlich, das andere passend zu modifizieren.
Es ging mir konkret um das hier -> > Wir können ja auch gerne mal konkret werden: > Der Startupcode, den ich gerade verwende [1], macht es richtig: "Richtig" heißt in diesem Zusammenhang wohl, dass der Startup-Code mit dem Standard übereinstimmt. Wenn ich aber eine globale Variable anlegen kann, die nicht initialisiert wird, dann macht der Code es auch nicht "richtig".
Vincent H. schrieb: > "Richtig" heißt in diesem Zusammenhang wohl, dass der Startup-Code mit > dem Standard übereinstimmt. Wenn ich aber eine globale Variable anlegen > kann, die nicht initialisiert wird, dann macht der Code es auch nicht > "richtig". Wir drehen uns im Kreis. Ich wollte darauf hinaus, dass du vermutlich nicht in der Lage bist, eine Variable in einem solchen RAM zu platzieren, ohne im Code Sprachelemente ausserhalb des Standards zu verwenden, oder im Entwicklungssystem Teile wie ein Linkerscript gegenüber der Vorgabe zu verändern. Wenn du aber das Linkerscript veränderst, oder z.B. "__attribute__" in GCC verwendest, dann bist du für die Folgen verantwortlich. Linkerschript und Startup-Code gehören unmittelbar zusammen. Unfachmännischer Umgang damit kann zu unerwünschten Resultaten führen.
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.