Forum: Compiler & IDEs for(;;){} Schleife


von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Ein Beispiel hat Andreas schon genannt: Hardware-Reset mittels Watchdog.
>
> Phase 1: Watchdog wird konfiguriert und aktiviert.
>
> Phase 2: Es wird gewartet, bis der Watchdog zuschlägt und einen
> Hardware-Reset triggert. Das kann je nach Hardware ein paar ms oder
> zumindest mehrere µs dauern.

Und wann macht das Ding dann etwas? Immer nur zwischen Phase 1 und 2?

> Beispiel 2: Eine Applikation, die all ihre Aufgaben in ISRs erledigt. Am
> Ende von main, nach Konfiguration aller IRQs, steht dann eine leere
> Endlosschleife.

Das muss man aber so nicht machen. Mann kann das auch anders lösen. 
Deswegen ist das kein Argument.

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


Lesenswert?

Wilhelm M. schrieb:
> Und wann macht das Ding dann etwas? Immer nur zwischen Phase 1 und 2?

Es geht darum, gezielt einen Hardware-Reset auszulösen auf Plattformen, 
die das nicht direkt können (bpsw. AVR). Nachdem man den Wachhund 
gezündet hat, soll natürlich einfach mal nichts mehr passieren, bis er 
dann zuschlägt. Das "nichts mehr" macht man in einer harten 
Endlosschleife.

Anderer realer Fall: MCU im Fehlerfall (bspw. bei einem abort()) 
anhalten. Wenn man mit dem Debugger dran sitzt, kann man dann an der 
Stelle unterbrechen und versuchen nachzuvollziehen, wie es zum Fehler 
gekommen ist. Auch das braucht einfach eine harte Endlosschleife. 
Ähnlich, wenn man auf einer MCU aus main() zurückkehrt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> Ein Beispiel hat Andreas schon genannt: Hardware-Reset mittels Watchdog.
>>
>> Phase 1: Watchdog wird konfiguriert und aktiviert.
>>
>> Phase 2: Es wird gewartet, bis der Watchdog zuschlägt und einen
>> Hardware-Reset triggert. Das kann je nach Hardware ein paar ms oder
>> zumindest mehrere µs dauern.
>
> Und wann macht das Ding dann etwas?

In Phase 1 macht es etwas: Initialisierung etc.

In Phase 2 macht es etwas: JMP auf sich selbst bis die Zeit zum 
Watchdog-Reset verstrichen ist.

Falls du nicht weißt was'n Watchdog ist: 
https://de.wikipedia.org/wiki/Watchdog

>> Beispiel 2: Eine Applikation, die all ihre Aufgaben in ISRs erledigt. Am
>> Ende von main, nach Konfiguration aller IRQs, steht dann eine leere
>> Endlosschleife.
>
> Das muss man aber so nicht machen.

Na das ist man ein Argument. Bravo!

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Und wann macht das Ding dann etwas? Immer nur zwischen Phase 1 und 2?
>
> Es geht darum, gezielt einen Hardware-Reset auszulösen auf Plattformen,
> die das nicht direkt können (bpsw. AVR). Nachdem man den Wachhund
> gezündet hat, soll natürlich einfach mal nichts mehr passieren, bis er
> dann zuschlägt. Das "nichts mehr" macht man in einer harten
> Endlosschleife.

Mir ist schon klar, was ein WatchDog erreichen soll. Nur wo "sitzt" in 
dem Beispiel die Applikation?

> Anderer realer Fall: MCU im Fehlerfall (bspw. bei einem abort())
> anhalten. Wenn man mit dem Debugger dran sitzt, kann man dann an der
> Stelle unterbrechen und versuchen nachzuvollziehen, wie es zum Fehler
> gekommen ist. Auch das braucht einfach eine harte Endlosschleife.
> Ähnlich, wenn man auf einer MCU aus main() zurückkehrt.

In diesem Fall kommt es aber so gar nicht darauf an, mit welcher 
Frequenz die Loop durchlaufen wird. Ggf. wird man darin eh irgend etwas 
tun, ggf. sogar ein Pin wackeln. Also hier muss man kein UB haben. 
Deswegen ist das auch kein Argument.

von Georg M. (g_m)


Lesenswert?

Jörg W. schrieb:
> Thread hier befasst sich ja nun auch an vielen Stellen
> mit (Un-)Schönheiten von C als solches.

Hoffentlich nicht fruchtlos.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Johann L. schrieb:
>>> Ein Beispiel hat Andreas schon genannt: Hardware-Reset mittels Watchdog.
>>>
>>> Phase 1: Watchdog wird konfiguriert und aktiviert.
>>>
>>> Phase 2: Es wird gewartet, bis der Watchdog zuschlägt und einen
>>> Hardware-Reset triggert. Das kann je nach Hardware ein paar ms oder
>>> zumindest mehrere µs dauern.
>>
>> Und wann macht das Ding dann etwas?
>
> In Phase 1 macht es etwas: Initialisierung etc.
>
> In Phase 2 macht es etwas: JMP auf sich selbst bis die Zeit zum
> Watchdog-Reset verstrichen ist.

Wenn das Ding nur auf den WatchDog wartet, ohne da jemals eine LED 
angeht, dann kann ich auch einfach einen ohmschen Widerstand nehmen, um 
den Strom zu verbrauchen.

>>> Beispiel 2: Eine Applikation, die all ihre Aufgaben in ISRs erledigt. Am
>>> Ende von main, nach Konfiguration aller IRQs, steht dann eine leere
>>> Endlosschleife.
>>
>> Das muss man aber so nicht machen.
>
> Na das ist man ein Argument. Bravo!

Du stehst also auf dem Standpunkt: ich WILL aber unbedingt so eine 
Endlos-Schleife in C++, und ich weiß aber, dass das UB ist, und ich WILL 
aber, das es trotzdem geht. Mmh, hört sich an wie Vorschulalter ...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> Wilhelm M. schrieb:
>>> Johann L. schrieb:
>>>> Ein Beispiel hat Andreas schon genannt: Hardware-Reset mittels Watchdog.
>>>>
>>>> Phase 1: Watchdog wird konfiguriert und aktiviert.
>>>>
>>>> Phase 2: Es wird gewartet, bis der Watchdog zuschlägt und einen
>>>> Hardware-Reset triggert. Das kann je nach Hardware ein paar ms oder
>>>> zumindest mehrere µs dauern.
>>>
>>> Und wann macht das Ding dann etwas?
>>
>> In Phase 1 macht es etwas: Initialisierung etc.
>>
>> In Phase 2 macht es etwas: JMP auf sich selbst bis die Zeit zum
>> Watchdog-Reset verstrichen ist.
>
> Wenn das Ding nur auf den WatchDog wartet, ohne da jemals eine LED
> angeht, dann kann ich auch einfach einen ohmschen Widerstand nehmen, um
> den Strom zu verbrauchen.

Ach komm, gib dich mal nicht dümmer als du bist.  Du verstehst doch sehr 
wohl wozu das gut ist. Und ja, die Applikation macht mehr als nur 
einen Reset.  Der Reset wird nur bei bestimmten Vorbedingungen 
gebraucht.

Denkbare Anwendung wäre ein Bootloader, der nach Aktualisiserung der 
Anwendung diese anspringt. Ein Hardware-Reset resettet die ganze 
Hardware, was hier gewünscht ist.

> so eine Endlos-Schleife in C++, und ich weiß aber, dass das UB ist,
> und ich WILL aber, das es trotzdem geht.

Ja. Weil in neueren C++ Versionsn for(;;); UB ist, möchte ich wissen, 
welches Konstrukt der empfohlene, portable Weg ist, was alte Verhalten 
zu erreichen.

Ist das so viel verlangt?

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Ja. Weil in neueren C++ Versionsn for(;;); UB ist, möchte ich wissen,
> welches Konstrukt der empfohlene, portable Weg ist, was alte Verhalten

Das war <= C++03 (vor 20 Jahren)

> zu erreichen.

Nochmal: for(;;){} ist UB. Punkt.

> Ist das so viel verlangt?

Das habe ich nun schon x-mal geschrieben, welche Alternativen zur 
Verfügung stehen. Und das sind dann welche, die die Latenz für die ISR 
gar nicht beeinflussen. Daher: nimm sie, oder lass es sein.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> Ja. Weil in neueren C++ Versionsn for(;;); UB ist, möchte ich wissen,
>> welches Konstrukt der empfohlene, portable Weg ist, was alte Verhalten
>
> Das war <= C++03 (vor 20 Jahren)
>
>> zu erreichen.
>
> Nochmal: for(;;){} ist UB. Punkt.

Klar.  Und genau deshalb frage ich ja nach einem ERSATZ.

> Das habe ich nun schon x-mal geschrieben, welche Alternativen zur
> Verfügung stehen.

Bisher habe ich zumindest in diesem Thread von dir keine einzige 
Alternative gefunden. Wortreiche Umschreibungen und Characterisierungen 
ja, aber wie lautet denn nun der magische Code?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich muss zugeben mir ergeht es aktuell wir Gerhard. Der Thread hat mich 
vollkommen durcheinander gebracht. Gewohnte Fachbegriffe haben hier eine 
andere Bedeutung erlangt. Ich komme einfach nicht mehr mit. Ich gebe es 
auf. Ich programmiere weiter wie immer und hoffe das alles funktioniert. 
Ich muss mich darauf verlassen das der gcc keinen ungewohnten Mist 
macht. Ich danke allen die Antworten auf meine Fragen gegeben haben, 
auch wenn ich diese im Zusammenhang hier nur noch teilweise verstehe und 
einsortieren kann. Weitere Nachfragen würden in einer weiteren 
nebenläufigen Endlosschleife enden. Das möchte ich niemanden zumuten. 
Ich melde mich aus dem Thread ab.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Bisher habe ich zumindest in diesem Thread von dir keine einzige
> Alternative gefunden. Wortreiche Umschreibungen und Characterisierungen
> ja, aber wie lautet denn nun der magische Code?

z.B.:

Beitrag "Re: for(;;){} Schleife"

oder

Beitrag "Re: for(;;){} Schleife"

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> Bisher habe ich zumindest in diesem Thread von dir keine einzige
>> Alternative gefunden. Wortreiche Umschreibungen und Characterisierungen
>> ja, aber wie lautet denn nun der magische Code?
>
> z.B.:
>
> Beitrag "Re: for(;;){} Schleife"
>
> oder
>
> Beitrag "Re: for(;;){} Schleife"

C++ definiert sleep() und noop() ?

Nicht-portable Lösungen sind ja naheliegend, wie __asm volatile ("":); 
für g++.

Aber das ist offenschtlich NICHT portabel.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Johann L. schrieb:
>>> Bisher habe ich zumindest in diesem Thread von dir keine einzige
>>> Alternative gefunden. Wortreiche Umschreibungen und Characterisierungen
>>> ja, aber wie lautet denn nun der magische Code?
>>
>> z.B.:
>>
>> Beitrag "Re: for(;;){} Schleife"
>>
>> oder
>>
>> Beitrag "Re: for(;;){} Schleife"
>
> C++ definiert sleep() und noop() ?

Das spielt schon in dem Moment keine Rolle mehr, wo Du ISRs einsetzt. 
Denn die sind auch nicht konform.

>
> Nicht-portable Lösungen sind ja naheliegend, wie __asm volatile ("":);
> für g++.
>
> Aber das ist offenschtlich NICHT portabel.

Du argumentierst doch im Kreis: einerseits willst Du ISRs oder 
Exception-Handler oder WDT einsetzen, die nicht portabel sind, und 
andererseits beschwerst Du Dich über sleep() und noop(), was nicht 
portabel sei?

Wenn Du komplett auf nicht-potable Sachen wie ISRs, sleep(), noop(), 
etc. verzichtest, dann hast Du ein Programm ohne sichtbaren Effekt. Das 
ist sinnlos, und nichts anderes sagt diese Regel aus C++.

Beitrag #7485073 wurde vom Autor gelöscht.
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wie gesagt, eine leere Endlosschleife (natürlich ein nicht-UB Ersatz 
dafür) KANN sinnvoll sein, egal ober der Rest des Codes ISRs oder was 
auch immer verwendet oder nicht.

Nach deiner Lesart sind ISRs nicht sprachkonform, und daher kann Code, 
der ISRs verwendet, beliebige nicht-konfirme Konstrukte verwenden.

Ich denke auch nicht, dass du noch mit einer Lösung um die Ecke kommst.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wie gesagt, eine leere Endlosschleife (natürlich ein nicht-UB Ersatz
> dafür) KANN sinnvoll sein, egal ober der Rest des Codes ISRs oder was
> auch immer verwendet oder nicht.

Beispiel? Und - um Deine Worte zu gebrauchen - keine wortreichen 
Umschreibungen.

> Nach deiner Lesart sind ISRs nicht sprachkonform, und daher kann Code,
> der ISRs verwendet, beliebige nicht-konfirme Konstrukte verwenden.

Tut er bereits durch die ISRs. Daher darfst Du ruhig Dein asm("") 
einsetzen, das zwar implementation-defined ist, aber auf wohl den 
meisten Compilers tut.

> Ich denke auch nicht, dass du noch mit einer Lösung um die Ecke kommst.

Die verlinkte noop() Variante ist komplett konform (man könnte auf das 
eine Byte auch noch verzichten).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> Nach deiner Lesart sind ISRs nicht sprachkonform, und daher kann Code,
>> der ISRs verwendet, beliebige nicht-konfirme Konstrukte verwenden.
>
> Tut er bereits durch die ISRs.

Der Unterschied ist aber, dass die für ISRs erforderlichen Mittel von 
der Implementation zur Verfügung gestellt und garantiert werden.

Von daher sind das alles Strohmann-Nebelkerzen deinerseits.
> Die verlinkte noop() Variante ist komplett konform (man könnte auf das
> eine Byte auch noch verzichten).

Ok hatte die Definition übersehen.

Wobei es hier auch schon mal ne ewige endlos-Diskussion darüber gab, ob 
(void) var; for eine volatile Variable noch den neueren Standards 
entsprechen die auch an volatile rumgeschraubt haben.

Und wie man das 1 Byte einsparen könnte, sehe ich auch nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Johann L. schrieb:
>>> Nach deiner Lesart sind ISRs nicht sprachkonform, und daher kann Code,
>>> der ISRs verwendet, beliebige nicht-konfirme Konstrukte verwenden.
>>
>> Tut er bereits durch die ISRs.
>
> Der Unterschied ist aber, dass die für ISRs erforderlichen Mittel von
> der Implementation zur Verfügung gestellt und garantiert werden.

Dann kannst Du genau diese "Mittel" auch in der trivialen Endlos-Loop 
verwenden, also z.B. asm("") der Implementierung.

> Von daher sind das alles Strohmann-Nebelkerzen deinerseits.

Nein, ich habe Dir das jetzt mehrmals schlüssig dargelegt.

Wie gesagt:

Wilhelm M. schrieb:
> Du stehst also auf dem Standpunkt: ich WILL aber unbedingt so eine
> Endlos-Schleife in C++, und ich weiß aber, dass das UB ist, und ich WILL
> aber, das es trotzdem geht. Mmh, hört sich an wie Vorschulalter ...


>> Die verlinkte noop() Variante ist komplett konform (man könnte auf das
>> eine Byte auch noch verzichten).
>
> Ok hatte die Definition übersehen.

Also, erst lesen, dann meckern.

> Wobei es hier auch schon mal ne ewige endlos-Diskussion darüber gab, ob
> (void) var; for eine volatile Variable noch den neueren Standards
> entsprechen die auch an volatile rumgeschraubt haben.

Es gibt nur die Warnung (gcc), dass die Variable tatsächlich nicht 
benutzt wurde. Das ist auch gut so. Denn wäre dass ein SFR, wäre es 
bestimmt ein Fehler.

> Und wie man das 1 Byte einsparen könnte, sehe ich auch nicht.

Ich gehe davon aus, das der fragliche Code sicher irgendwo (lokal in 
main, global, ...) ein beliebiges Objekt definiert, was man 
volatile-referenzieren kann.

von Klaus H. (klummel69)


Lesenswert?

Diese Thread hat sich echt interessant entwickelt. Danke für die vielen 
Aspekte.

Die Beispiele von Johann hatte ich schon öfters in realem Code. Eure 
Diskussion aktuell geht aber meiner Meinung nach am wesentlichen vorbei:
Vielen Entwicklern – nicht nur Anfängern – sind die Konstrukte, die 
Undefined Behavior erzeugen nicht bewusst, sei es, dass sie sich nie 
damit beschäftigt haben, sei es, dass sie es vergessen haben.

Ich habe mit Softwareentwicklern mal ein Quiz veranstaltet. Es war 
erschreckend wie viele Programmierer UB Fälle nicht erkannt haben.
Wenn C++(03) eine for(;;); Schleife wegen UB rausoptimiert, ohne eine 
Warnung zu generieren, ist das die schlechteste Vorgehensweise. Gerade 
dann, wenn es "ähnlichen" C Code gibt, der so etwas nicht raus 
optimiert.

Es gilt eigentlich die Goldene Regel: Kenne Deine Werkzeuge!
Die Praxis zeigt: der Mensch macht Fehler, ist vergesslich und macht 
Annahmen, die nicht stimmen.

In diesem Kontext sollte ein Compiler eigentlich auf UB hinweisen. Tut 
er hier nicht, und das ist IMHO ein Problem.
Warum C++03 damals die Schleife als UB angesehen hat, hat mich immer 
gewundert.

Wenn die (embedded) Praxis viele Beispiele hat, macht es keinen Sinn das 
Verhalten zu ändern.

von Gerhard O. (gerhard_)


Lesenswert?

Moin,

Ich finde diese UB Möglichkeiten und Unsicherheiten nicht gerade sehr 
erbauend. Ich kann nicht beurteilen, inwiefern moderne Tools anderer 
Produkte sich daranhalten, die sich an die neuesten Standards halten. 
Fakt ist, daß alle meine älteren Tools dieses speziell aufgebrachten 
Problem nicht haben und diese Konstrukte, wie traditionell erwartet, 
richtig funktionieren. Abgesehen davon, merkt man ja beim Testen sofort, 
ob der Compiler sich solche Freiheiten erlaubt hat.

Man gewinnt ja über die Jahre hinaus viele Erfahrungen mit den 
Werkzeugen mit denen man zu tun hat und kennt die besonderen Probleme 
und vermeidet die "bösen" Konstrukte.

Ich befasse mich auch mit einigen anderen uC anderer Hersteller und die 
Werkzeuge sind auch nicht mehr die Jüngsten. Jedenfalls wären mir diese 
"Freiheiten" schon längst beim Testen aufgefallen. Noch nie hatte ich 
mit den hier diskutierten Schleifenkonstrukten jemals ein Problem und 
mir hat noch kein Compiler eine Test Watchdog Trigger Endlosschleife 
wegoptimiert. Das wäre beim Testen sofort aufgefallen.

Inwieweit UB ein praktisches Problem ist oder eher ein Akademisches, 
kann ich nicht wirklich beurteilen. Ausschlaggebend für mich ist, daß 
die Sprache anhand der bereitgestellten Dokus, so wie dort beschrieben, 
richtig funktioniert. Früher schien das zu genügen um produktiv in der 
Praxis wirken und bestehen zu können.

Wie ist es in der UNI? Lernt man dort hauptsächlich die Sprachen und 
deren Anwendung, oder studiert man extensiv Compiler Standard Dokus? 
Sicher, ab und zu muß man sie bei der Fehlersuche konsultieren, aber das 
sollte nur selten notwendig sein.

Hätte es wirklich viel Sinn, alle diese Compiler Standard 
Schrulligkeiten lernen und im Kopf behalten zu müssen? Ich glaube, nein.

Es kommt halt letzen Endes darauf an, wie zuverlässig das Gesamtprodukt 
sein muß, um diese Unsicherheiten überprüfen zu wollen.

Letzten Endes kommt es auf ausreichend gutes und komplettes Testen der 
Anwendung an.

Letztlich bin ich also der Meinung, daß Eure ganze Diskussion fast 
vollkommen akademisch ist und in der Praxis nur unter besonderen 
Umständen Gewicht hat, wenn man genug Praxis Erfahrung mit den 
Werkzeugen hat.

Das größte Problem mit C++ ist, daß es viel zu komplex und "fancy" 
geworden ist und dieser Trend fortgehend weitergeht. Viele Eigenschaften 
sind so implizit und versteckt, daß nur noch ein ausgedehntes Studium 
darüber Klarheit beschafft.

In klassischen C, da nur ein Mittelding zwischen ASM und Hochsprachen 
sein wollte, waren viel klarere Verhältnisse. Der Trouble mit C++ ist, 
daß man es heute als "Mädchen für alles" hochgezogen hat und derart 
komplex geworden ist, daß nur noch hartgesottene Spezialisten, die 
täglich ihr Brot damit verdienen, sich damit ausreichend auskennen. Zum 
gelegentlichen Programmieren sollte man die esoterischen Besonderheiten 
der Sprache, meiner Meinung nach eher vermeiden wenn sie nicht 
ausdrücklich extrem nützlich sind, und lieber mit Grundfunktionalität 
jonglieren.

Mir ist aufgefallen, daß ältere traditionelle Sprachen großteils aus 
ihrem Syntax verständlich waren. Eine Fortran oder Pascal Quelle konnte 
man mit wenig Recherche verstehen, die heutigen Sprachen sind ohne 
Studium fast vollkommen unverständlich. Der Syntax gibt beim Lesen nicht 
länger Aufschluss auf die gemeinte Funktionalität. Nur ausführliches 
Wissen dieser Konstrukte und zugängliche Referenzdokumentation gibt noch 
Aufschluss. Das ist der Preis des Fortschritts, vermute uch.

Aus meiner Sicht machte ich die Erfahrung, daß ich C Code meist total 
verständlich finde, bei C++ andauernd nachschlagen muß was im Einzeln 
gemeint ist und die Terminologie nicht unbedingt immer ausreichend 
verständlich ist. Ja, Klassen und Objekte können ja recht nützlich und 
bequem sein. Aber für viele embedded Anwendungen mit Ressourcen 
begrenzten uC eigentlich doch nicht wirklich notwendig. Bei der 
Fehlersuche kann da schon viel zu viel versteckt sein. Speziell bei 
fremden Bibliotheken, wo man dann auch jene durchgehen muß.

Sind halt meine Ansichten, aber die, viele von Euch wahrscheinlich 
lächeln werden. Aber nicht jeder steckt in Euren Schuhen. Und da ich 
mich viel mit HW Design befasse, programmiere ich eben nicht den ganzen 
Tag, täglich. Es ist ja gut, daß sich jemand (ihr) über UB Gedanken und 
deren möglichen Auswirkungen macht.

Vg,
Gerhard

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Klaus H. schrieb:
> Warum C++03 damals die Schleife als UB angesehen hat, hat mich immer
> gewundert.

Kann eigentlich nicht sein, denn diese "Formulierung" wurde erst mit 
C++11 bzw. C11 eingeführt zusammen mit der Definition des Memory-Models. 
Das Memory-Model wiederum war notwendig, damit gerade die nebenläufigen 
Konstrukte eine definiterte Semantik bekommen.

Diese "Problem", was wir hier diskutieren, ist m.E. die Diskussion um 
die leere Menge. Denn in der Praxis leben wir mit sehr vielen, 
nicht-standardisierten Erweiterungen im C++-Compiler (auch im 
C-Compiler). Schließlich haben wir uns auch daran gewöhnt, nicht 
standard-konforme ISRs, asm()-Konstrukte, etc. in unseren MCU-Code 
einzubauen. Deswegen habe ich wiederholt versucht darzustellen, dass das 
Problem in der Praxis gar nicht existiert. Sei es, weil die leere, 
triviale Endlos-Schleife ein Spezialfall darstellt, oder Werkzeuge wie 
GCC ein konsistentes Verhalten im Sinne des least-astonishments zeigen.
Trotzdem muss man bei den Sprachen C und C++ das Konzept des UB einfach 
kennen, denn es ist ein Grundprinzip dieser beiden. Wer das ignoriert, 
hat schlicht die Sprache nicht verstanden.
Und ich möchte nicht wissen, in wie vielen Fällen auch heute noch 
type-punning via raw-unions in C++ gemacht wird. Das ist zwar ganz klar 
UB, jedoch wird es im g++ genauso wie im gcc, also im C-Mode behandelt, 
wo es eben kein UB ist. Das ist auch gut so, verhindert es doch 
allzugroße Unfälle. Gleichzeitig ist es aber auch schlecht, weil man auf 
den Fehler nicht hingewiesen wird, solang man keine Analyse-Tools 
verwendet, was die wenigsten hier wohl machen werden.
Daran zeigt sich eben, dass C keine Untermenge von C++ ist, sondern eine 
eigene Sprache.
Deswegen: diese Diskussion hier mag akademisch erscheinen bzw. auch 
sein, denn - wie gesagt - bin ich der Meinung, dass dies ein Spezialfall 
ist und wir in der Praxis damit in Reinform gar nicht in Berührung 
kommen, weil jedes MCU-Programm neben dem Standard liegt. Aber diese 
Diskussion soll ein Hinweis darauf sein, dass man mit dem Konzept UB 
umgehen muss: egal, ob Loops, raw-unions, integer-overflow, 
uninitialized variables, off-object pointer dereference, .....

von Klaus H. (klummel69)


Lesenswert?

Wilhelm M. schrieb:
> Kann eigentlich nicht sein, denn diese "Formulierung" wurde erst mit
> C++11 bzw. C11 eingeführt zusammen mit der Definition des Memory-Models.
> Das Memory-Model wiederum war notwendig, damit gerade die nebenläufigen
> Konstrukte eine definiterte Semantik bekommen.
Ups, sorry. Hast recht.
Aber es ändert nichts daran, dass das Konstrukt for(;;); schon Jahre 
lang im emedded Bereich eingesetzt wurde. Deine Beispiel zeigen ja 
schön, dass die meisten Compiler es trotzdem wie früher umgesetzt haben. 
Ich mag clang, aber dieses Verhalten ohne Warnung halte ich für 
gefährlich.
Wenn der Code beim Build jeweils vollständig getestet wird, vielleicht 
kein Problem. In der Praxis sehe ich aber immer wieder Firmware, die 
nach dem Motto „wir haben es zwar nicht dokumentiert, aber 
Entwicklertests wurden gemacht“ getestet wurden.

Wilhelm M. schrieb:
> Und ich möchte nicht wissen, in wie vielen Fällen auch heute noch
> type-punning via raw-unions in C++ gemacht wird
Jepp, viel zuviel.

Wilhelm M. schrieb:
> Aber diese Diskussion soll ein Hinweis darauf sein, dass man mit dem Konzept UB
> umgehen muss: egal, ob Loops, raw-unions, integer-overflow,
> uninitialized variables, off-object pointer dereference, .....

Volle Zustimmung. Und das Betrifft natürlich C++ und auch C.

von Gerhard O. (gerhard_)


Lesenswert?

Moin,

Es würde mich interessieren, welche Berücksichtigungen für den embedded 
Bereich gemacht werden.

Evaluiert der Compiler Quellen für Linux Anwendungen genauso wie für 
z.B. für den AVR? Inwieweit wird auf die Besonderheiten der uC Rücksicht 
genommen?

Das zu wissen, würde einen gewissen Anhaltspunkt geben.

Was mich an diesem Thema stört, ist dass viele Leute einschließlich 
mich, vor einigen Jahrzehnten mit C Anwendung angefangen haben und die 
damaligen (kommerziellen) Werkzeuge nur den Sprachengebrauch ohne 
hintergründige Theorie dokumentierten. Da wurden niemals 
Konformitätsreferenzen gegeben. Es gab Beispiele und das wars auch. Der 
Entwickler musste dann eben mit der Zeit seine eigenen Erfahrungen 
machen.

Und, mit Verlaub, jetzt seid ihr da und taucht mächtig in den Morass der 
UB ein, den nur ein Experte wirklich beurteilen kann. Hat sich 
Entwicklung nun in 2023 so geändert und hochgeschraubt, daß es ohne Euer 
(Fach) Wissen in der Praxis nicht mehr geht? Ich will einfach nicht 
glauben, daß die Ingenieure in den zahlreichen Betrieben in den 
Anfangsjahren auf Eurem Niveau sein konnten.

Abgesehen davon, wer versteht schon gleich die Erklärungen über jedes 
Detail. Zum Teil verstehe ich da auch nur Bahnhof. Diese Fachsprache zu 
verstehen ist keine Kleinigkeit.

Wo ist da der Mittelweg? Bitte seht es nicht nur von Eurem Standpunkt 
aus. Es gibt bestimmt noch viele embedded Entwickler die das eher mehr 
praxisbezogen angehen und auch nicht den Sprachenexpertenfachbezug 
haben, aber sonst für viele Anwendungen ordentlich programmieren können.

Und wo gibt es verständliche Dokus, die etwas breiter verständlich sind? 
Die Compiler Referenzunterlagen sind leider oft nur schwer verdaulich. 
Auch im Web, wie z.B. bei Stack Overflow, versteht man auch nicht immer 
gleich alles.

Man bekommt von dieser Diskussion wirklich den Eindruck, wer nicht auf 
Eurem Niveau ist, in der Industrie nicht mehr die geringste Chance haben 
zu können. Bitte seht das nicht als persönlichen Angriff bzw. Kritik, 
aber überm Zaun ist die Welt vielleicht doch etwas diverser.


Gerhard

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


Lesenswert?

Gerhard O. schrieb:
> Inwieweit wird auf die Besonderheiten der uC Rücksicht genommen?

Gar nicht, denn der größte Teil der Optimierungen passiert weit oberhalb 
des Niveaus der konkreten Maschine. Nur so profitieren aber eben auch 
"tier 3"-Plattformen trotzdem noch von vielen Optimierungen, die 
vorrangig für die "tier 1"-Plattformen entwickelt und getestet werden.

von Bauform B. (bauformb)


Lesenswert?

Gerhard O. schrieb:
> Evaluiert der Compiler Quellen für Linux Anwendungen genauso wie für
> z.B. für den AVR? Inwieweit wird auf die Besonderheiten der uC Rücksicht
> genommen?

Jörg W. schrieb:
> Gar nicht, denn der größte Teil der Optimierungen passiert weit oberhalb
> des Niveaus der konkreten Maschine.

Jein, für uC kann man (sollte man? wird man normalerweise?) 
-ffreestanding angeben. Das gibt es mindestens seit C99. Damit bekommt 
der Programmierer ein paar Freiheiten (z.B. main, newlib-nano) und muss 
dafür auf ein paar Optimierungen verzichten, weil es praktisch keine 
Standard-Bibliotheken gibt:

Jörg W. schrieb:
> Annahmen hinsichtlich der Funktionalität der Standardbibliothek sind
> halt einer der Punkte, der für ein hosted environment zutrifft.
>
> "Any library facilities available to a freestanding program, other
> than the minimal set required by Clause 4, are implementation-defined."
>
> Prinzipiell könnte es für Funktionen wie strlen() noch machbar sein,
> diese bei einem konstanten String zu optimieren (Clause 4 würde das
> meiner Meinung nach gestatten), aber in der Praxis passiert es nicht.

Bisher wurden in diesem minimal set nur Konstanten definiert und Dinge 
wie stdarg, die fest im Compiler eingebaut sein müssen. Mit string.h 
kommen bei C23 zum ersten Mal echte Bibliotheksfunktionen dazu. Bisher 
musste man für uC-Programme nur den gcc installieren, keine libc. Damit 
das so bleibt, müsste der gcc die string-Funktionen mitliefern. Es ist 
ja nicht sicher, ob z.B. die newlib-nano 100% konform ist.

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


Lesenswert?

Bauform B. schrieb:
>> Gar nicht, denn der größte Teil der Optimierungen passiert weit oberhalb
>> des Niveaus der konkreten Maschine.
>
> Jein, für uC kann man (sollte man? wird man normalerweise?)
> -ffreestanding angeben.

Wie schon dargelegt, verhindert das allerdings eher Optimierung, als 
dass es MCU-spezifische Optimierungen aktiviert. Aus ebendiesem Grunde 
benutze ich es persönlich dafür gar nicht. Die größere "Freiheit", dass 
ich main() dann auch als "void" deklarieren darf, nützt mir praktisch 
nichts. Der Rest der üblichen Bibliotheken ist in den Teilen, in denen 
die Funktionalität des Standards implementiert wird (also der gleiche 
Namensraum wie vom Standard benannt) recht gut standardkonform, 
zumindest bei AVR und ARM. (Von der newlib-nano halte ich persönlich 
nicht so viel, die meisten ARMs sind groß genug, dass sie die 
Vollversion der newlib problemlos nehmen können.) 
Bibliotheksfunktionalität, die nicht benutzt wird, kostet ja 
normalerweise auch nichts.

von Walter T. (nicolas)


Lesenswert?

Oha. Die for-Schleife läuft jetzt schon 15 Tage und wird irgendwie gar 
nicht mehr verlassen. Da hat wohl jemand die Abbruchbedingung vermurkst.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bauform B. schrieb:
> Jein, für uC kann man (sollte man? wird man normalerweise?)
> -ffreestanding angeben.

Würde ich nicht machen. Im avr-gcc gibt es zum Beispiel Optimierungen, 
die NICHT für freestanding gemacht werden.

Und das betrifft dann ziemlich jedes Programm, weil es um main geht (das 
bei freestanding keine Sonderrolle hat).

von Bauform B. (bauformb)


Lesenswert?

Johann L. schrieb:
> Bauform B. schrieb:
>> Jein, für uC kann man (sollte man? wird man normalerweise?)
>> -ffreestanding angeben.
>
> Würde ich nicht machen. Im avr-gcc gibt es zum Beispiel Optimierungen,
> die NICHT für freestanding gemacht werden.

Ja, tatsächlich
freestanding
1
  20424      24    1952   22400    5780 BUILD/syslib.elf
2
   6476       0     716    7192    1c18 BUILD/init.elf
3
   2884       0     832    3716     e84 BUILD/gps.elf
4
    528       0       0     528     210 BUILD/tzdata.elf
hosted
1
  20412      24    1952   22388    5774 BUILD/syslib.elf
2
   6460       0     716    7176    1c08 BUILD/init.elf
3
   2916       0     832    3748     ea4 BUILD/gps.elf
4
    528       0       0     528     210 BUILD/tzdata.elf
Aber per Definition sind uC-Programme eindeutig freestanding. 
Konsequenterweise sage ich dem Linker auch noch -nostdlib. Irgendwie war 
das ganz zu Anfang einfacher und übersichtlicher und jetzt ist es 
Tradition.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Hab nicht alles gelesen, aber
1
for(;;){}

ist einfach wie eine Vokabel - vlt eher noch eine Redewendung in C, die 
man auswendig lernt.

Sie bedarf keiner Verbesserung. Jeder C Programmierer mit etwas 
Erfahrung kennt das, wieso künstlich irgendwelche Konstrukte erfinden / 
bauen, die etwas triviales hübscher machen.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Es wurde schon alles zum Thema gesagt, nur noch nicht von jedem.

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.