Forum: Compiler & IDEs Zusicherung der initialisierung von globalen Variablen in C und C++


von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

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 In­i­ti­a­li­sie­rung 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

von waflija (Gast)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Danke mh :)

von Der Andere (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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
von Nop (Gast)


Lesenswert?

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.

von Guest (Gast)


Lesenswert?

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
}

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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?

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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? ;-)

von Rolf M. (rmagnus)


Lesenswert?

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?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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)

von Guest (Gast)


Lesenswert?

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.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

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.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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
von Wilhelm M. (wimalopaan)


Lesenswert?

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

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Wilhelm M. schrieb:

> Aber wozu in dem o.g. Bsp. SmartPtr?

das Beispiel von Mikro enthält keine smart pointer.

von Wilhelm M. (wimalopaan)


Lesenswert?

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!

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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

von Mikro 7. (mikro77)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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 ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

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


Lesenswert?

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.

von Vincent H. (vinci)


Lesenswert?

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 In­i­ti­a­li­sie­rung 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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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 ;-)

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

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?

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


Lesenswert?

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

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

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

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


Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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
von Vincent H. (vinci)


Lesenswert?

Wilhelm M. schrieb:
> Leute, wo sind wir denn?

In einer Welt, in der Chiphersteller keine standard-konformen 
Linkerscripts und Startup-Code ausliefern.

von W.S. (Gast)


Lesenswert?

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 Bernd K. (prof7bit)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Nase (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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?

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


Lesenswert?

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.

von temp (Gast)


Lesenswert?

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.

von Nase (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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

von Bernd K. (prof7bit)


Lesenswert?

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!

von Rolf M. (rmagnus)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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
von (prx) A. K. (prx)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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?

von here (Gast)


Lesenswert?

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
von Bernd K. (prof7bit)


Lesenswert?

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.

von Vincent H. (vinci)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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?

von Vincent H. (vinci)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Vincent H. (vinci)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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
Noch kein Account? Hier anmelden.