Hallo Leute, ich schon wieder..
Habe noch eine Verständnis Frage zu Char..
Von der Quelle
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART
habe ich den Teil von ISR(USART_RXC_vect) kopier und angepast auf meine
µC
meine PC schintstelle sende immer mit 4s Pause
1
Hallo Welt
2
#aabcdfe
3
#aAcdfe
jetzt dachte ich mir, mit folgend Code könnte ich das Empfange auswerten
Bernd schrieb:> uart_puts(uart_string);> uart_puts("<\r\nPos 0: >");> uart_puts(uart_string[0]);
also das Erste, was mir da auffällt: Offenbar scheint uart_puts einen
C-String zu erwarten. Das ist ein Pointer auf ein char-array, welches
als letztes eine binäre Null enthält.
Andererseits ist uart_string[0] kein Pointer auf ein solches array,
sondern enthält vielmehr einen einzelnen char. Bei einem vernünftigen
Compiler sollte man hier eigentlich schon eine Warnung wegen
inkompatibler Typen bekommen.
Ohne das Framework zu kennen, würde ich erwarten, daß eine Routine zum
Ausgeben eines einzelnen Zeichens beispielsweise uart_putc heißt und
nicht uart_puts.
Servus,
probier mal:
char sendepuffer[20];
sprintf(sendepuffer,"\r\nEmpfang: >%c",uart_string);
uart_puts(sendepuffer);
Für den Fall eines C-Strings %s statt %c nehmen.
Gruß,
Jonas
Jonas schrieb:> sprintf(sendepuffer,"\r\nEmpfang: >%c",uart_string);
Das nun gerade nicht, denn von printf und Konsorten sollte man embedded
am besten die Finger lassen. Abgesehen davon ist uart_string ein Array,
also ein String, den kann man nicht mit %c ausgeben. Das geht nur mit
uart_string[0].
Wenn schon, dann eher sowas:
Ist aber auch saumäßig umständlich und nur zu empfehlen, wenn es kein
uart_putc() geben sollte und der Threadersteller auch keine solche
Funktion schreiben möchte.
Zugegeben wäre es nicht ganz so umständlich, wenn man die defensive
Programmierung mit Prüfung der Pufferlänge unterließe. ;-)
Nop schrieb:> Jonas schrieb:>> sprintf(sendepuffer,"\r\nEmpfang: >%c",uart_string);>> Das nun gerade nicht, denn von printf und Konsorten sollte man embedded> am besten die Finger lassen.
Weil?
Wenn genug Flash da ist, darf man sich auch ein bisschen Komfort gönnen,
zum Debuggen sowieso. Und unter "Embedded" fällt auch ein Raspberry Pi
3.
S. R. schrieb:> Weil?
Speicherbedarf im Wesentlichen. Wenn man die Vollversion von printf
nutzt, dann bindet man sich obendrein auch noch dynamisches
Speichermanagement ans Bein. Das will man embedded auch möglichst
vermeiden, u.a. wegen Speicherfragmentierung.
Auch zum Debuggen, weil das Timing dann u.U. völlig anders wird,
zumindest sporadisch.
Zudem ist ein sprintf, nur um einen char an einen String anzuhängen,
völliger Overkill.
> Und unter "Embedded" fällt auch ein Raspberry Pi 3.
Das Ausgangsposting verweist aber auf AVR, nicht auf Raspi 3.
Nop schrieb:> Wenn man die Vollversion von printf nutzt, dann bindet man sich> obendrein auch noch dynamisches Speichermanagement ans Bein.
Allerdings gibt ein printf()-Aufruf den Speicher nach Benutzung auch
vollständig wieder zurück...
> Das will man embedded auch möglichst> vermeiden, u.a. wegen Speicherfragmentierung.
...womit keine Speicherfragmentierung entstehen kann (sofern das
printf() nicht selbst unterbrochen wird und der unterbrechende Code
selbst dynamischen Speicher nutzt). Notfalls wird dynamischer Speicher
auf einem kleinen, statisch alloziierten Array implementiert, dann
entfällt der Grund komplett.
> Auch zum Debuggen, weil das Timing dann u.U. völlig anders wird,> zumindest sporadisch.
Wer aktiv Debugmeldungen ausgibt, verändert das Programm und damit das
Timing ohnehin, ob da nun printf drin ist, ist auch egal. Im schlimmsten
Fall wird der Programmablauf durchs Debuggen serialisiert und der
eigentliche Bug verschwindet.
> Zudem ist ein sprintf, nur um einen char an einen String anzuhängen,> völliger Overkill.
Das sollte ein moderner Compiler aber erkennen und optimieren können.
>> Und unter "Embedded" fällt auch ein Raspberry Pi 3.> Das Ausgangsposting verweist aber auf AVR, nicht auf Raspi 3.
Du schriebst aber nicht AVR, sondern Embedded.
Was ich damit sagen will: Ja, printf ist (einmalig!) teuer, schenkt
einem dafür aber ziemlich viel Komfort und macht den Code deutlich
lesbarer als manuelle Stringoperationen. Das ist ein starker Grund, es
trotzdem verfügbar zu halten, selbst wenn man es im Produktivcode eher
vermeidet. Zumindest ich finde Jonas Dreizeiler wesentlich einfacher zu
verstehen als deinen Krampf.
Muss man den (zu) kleinen AVR bis zum Rand vollstopfen, braucht die
Anwendung selbst zur Laufzeit dynamischen Speicher oder gibt es im
Timing keine Freiheiten mehr, sollte man printf besser vermeiden. Aber
das ist jedem, der auf der Ebene arbeitet, auch klar.
S. R. schrieb:> ...womit keine Speicherfragmentierung entstehen kann (sofern das> printf() nicht selbst unterbrochen wird und der unterbrechende Code> selbst dynamischen Speicher nutzt).
Dann muß man immer noch überhaupt erstmal einen Heap anlegen. Ich tue
das gar nicht erst, weil: keine dynamische Speicherallozierung.
> Notfalls wird dynamischer Speicher> auf einem kleinen, statisch alloziierten Array implementiert, dann> entfällt der Grund komplett.
Fände ich zuviel Aufwand. Insbesondere, um einen Char auszugeben, wofür
ich übrigens schlichtweg die putc-Routine nutzen würde, die ohnehin
implementiert sein wird, weil puts schließlich zeichenweise genau darauf
zurückgreift.
> Wer aktiv Debugmeldungen ausgibt, verändert das Programm und damit das> Timing ohnehin, ob da nun printf drin ist, ist auch egal.
Das ist natürlich auch wieder wahr.
>> Zudem ist ein sprintf, nur um einen char an einen String anzuhängen,>> völliger Overkill.>> Das sollte ein moderner Compiler aber erkennen und optimieren können.
Würde ich mich nicht drauf verlassen. Immerhin nehme ich C gerade
deswegen, weil ich kontrollieren kann und will, was eigentlich abläuft.
> Du schriebst aber nicht AVR, sondern Embedded.
Ja, und ich kenne es bei allen ernsthaften Projekten, daß überhaupt gar
nicht erst Code zur Dynamik dabei ist. Und deswegen wäre es einige
Arbeit, das nur für Debugausgaben einzufügen.
Gut, wenn man einen Raspi nimmt, ist sowieso alles egal. Man kann auch
einen kleinen NUC nehmen.
> Was ich damit sagen will: Ja, printf ist (einmalig!) teuer, schenkt> einem dafür aber ziemlich viel Komfort und macht den Code deutlich> lesbarer als manuelle Stringoperationen.
Dynamische Inhalte? Also hier wäre es ein Zeichen, dafür braucht man
überhaupt keine String-Ops. Ansonsten, um z.B. Integer zu konvertieren,
nutzt man üblicherweise entsprechend dedizierte, schlichte Funktionen.
> Zumindest ich finde Jonas Dreizeiler wesentlich einfacher zu> verstehen als deinen Krampf.
Der ist deswegen schwerer lesbar, weil er nebenbei auch noch gegen
Buffer-Overflows geschützt ist, was der reine sprintf-Aufruf auch nicht
gewesen ist. Das ist insofern ein unfairer Vergleich.
Nop schrieb:> Dann muß man immer noch überhaupt erstmal einen Heap anlegen. Ich tue> das gar nicht erst, weil: keine dynamische Speicherallozierung.
Muss man auf einem AVR nicht, macht die avr-libc für dich. Auf einem
kleinen ARM lege ich aber immer einen Heap an und verknote
stdin/stdout/stderr mit passenden Devices (Keypad/Display oder UART, je
nach System).
Hat den Vorteil, dass ich Teile der Programmlogik einfach unter Linux
entwickeln und auf dem Controller benutzen kann.
>> Das sollte ein moderner Compiler aber erkennen und optimieren können.>> Würde ich mich nicht drauf verlassen. Immerhin nehme ich C gerade> deswegen, weil ich kontrollieren kann und will, was eigentlich abläuft.
Also ich benutze C, weil mir der Compiler viel Arbeit abnimmt. Wenn ich
dem ständig hinterherräumen muss, bringt es nicht so viel. (Man sollte
trotzdem drauf achten, dass er keinen groben Unfug treibt.)
Und in den meisten Fällen ist es egal, ob deine Debugausgabe nun 200
oder 1000 Takte braucht, weil die CPU sowieso nichts zu tun hat.
> Ja, und ich kenne es bei allen ernsthaften Projekten, daß überhaupt gar> nicht erst Code zur Dynamik dabei ist. Und deswegen wäre es einige> Arbeit, das nur für Debugausgaben einzufügen.
Die Entscheidung, welcher Code nun in der ELF drin sein muss, trifft für
mich der Linker. Wenn niemand dynamischen Speicher braucht, dann ist in
der ELF kein malloc drin, fertig.
>> Was ich damit sagen will: Ja, printf ist (einmalig!) teuer, schenkt>> einem dafür aber ziemlich viel Komfort und macht den Code deutlich>> lesbarer als manuelle Stringoperationen.>> Dynamische Inhalte? Also hier wäre es ein Zeichen, dafür braucht man> überhaupt keine String-Ops. Ansonsten, um z.B. Integer zu konvertieren,> nutzt man üblicherweise entsprechend dedizierte, schlichte Funktionen.
Um eine Variable mit höherer Updaterate (> 2 Hz) zu verfolgen, ist es
sinnvoll, wenn sie eine feste Position auf dem Terminal/Display hat. Ein
löst das Problem ganz wunderbar (und ja, meine Displaytreiber verstehen
alle wenigstens \r, \n und \t).
>> Zumindest ich finde Jonas Dreizeiler wesentlich einfacher zu>> verstehen als deinen Krampf.>> Der ist deswegen schwerer lesbar, weil er nebenbei auch noch gegen> Buffer-Overflows geschützt ist, was der reine sprintf-Aufruf auch nicht> gewesen ist. Das ist insofern ein unfairer Vergleich.
Stimmt, Jonas hätte snprintf statt sprintf benutzen sollen.
Dann hätte er 5 Zeichen mehr tippen müssen. Und der Vergleich wäre fair.
S. R. schrieb:> Muss man auf einem AVR nicht, macht die avr-libc für dich.
Für Bastelprojekte kann man das sicherlich so machen. Bei ernsthafteren
Projekten käme man damit nichtmal durchs Code Review. Viele Kunden
fordern z.B. MISRA-Einhaltung. Aus gutem Grund, ich habe schon genug
Projekte mit dynamischer Speicherverwaltung gesehen, die genau daran
eingegangen sind. Deswegen verbannt man das kurzerhand komplett, und das
Problem ist gelöst.
> Hat den Vorteil, dass ich Teile der Programmlogik einfach unter Linux> entwickeln und auf dem Controller benutzen kann.
Dazu braucht man kein printf in den Teilen, die auf dem Controller
laufen sollen. BTDT.
> Also ich benutze C, weil mir der Compiler viel Arbeit abnimmt. Wenn ich> dem ständig hinterherräumen muss, bringt es nicht so viel.
Dann kannste auch gleich eine Sprache mit automatischem
Speichermanagement nehmen. Die sind dafür gedacht und nehmen noch mehr
Arbeit ab. Laufen u.U. auf einem µC dann halt nicht so toll.
> Die Entscheidung, welcher Code nun in der ELF drin sein muss, trifft für> mich der Linker.
Ist bei ernsthafteren Projekten nicht möglich, weil Du dann dead code
drin hast. Fliegt beim Code Review durch, spätestens beim coverage test.
Bei Bastelprojekten hingegen ist sowieso alles egal, muß nur irgendwie
halbwegs laufen.
> Um eine Variable mit höherer Updaterate (> 2 Hz) zu verfolgen, ist es> sinnvoll, wenn sie eine feste Position auf dem Terminal/Display hat.
Auch dazu braucht man kein printf. BTDT. Etwas wie itoa zu schreiben
(und zwar mit Buffercheck), das ist ja nun kein Hexenwerk.
> Stimmt, Jonas hätte snprintf statt sprintf benutzen sollen.> Dann hätte er 5 Zeichen mehr tippen müssen. Und der Vergleich wäre fair.
Wer embedded mit printf arbeitet, nutzt ohnehin auch strcpy und frißt
kleine Kinder. :-) Wie man übrigens gerade gesehen hat - wer mit printf
arbeitet, kommt von selber nicht auf snprintf. Du ja auch erst nach
meinem Hinweis.
Jonas schrieb:> Ich mach meine seriellen Ausgaben am AVR immer mit sprintf und hatte> damit noch nie Probleme.
Sehe ich auch so.
Immer dieses völlig unbegründete float/printf Bashing.
Die Compiler und MCs sind doch nicht auf dem Stand von vor 20 Jahren
stehen geblieben.
Ich benutze printf auf dem AVR, sogar für floats. Kostet einmalig ~4kB
Flash. Malloc wird aber nicht eingebunden, wozu auch.
Nop schrieb:>> Also ich benutze C, weil mir der Compiler viel Arbeit abnimmt. Wenn ich>> dem ständig hinterherräumen muss, bringt es nicht so viel.> Dann kannste auch gleich eine Sprache mit automatischem> Speichermanagement nehmen. Die sind dafür gedacht und nehmen noch mehr> Arbeit ab. Laufen u.U. auf einem µC dann halt nicht so toll.
Wenn ich der Meinung bin, meine Programmiersprache sei so schlecht, dass
ich ihr ständig hinterherräumen muss - sie also von Grund auf
unzuverlässig ist - dann mache ich was falsch und sollte mich selbst
hinterfragen.
Ich vertraue modernen Compilern zu einem relativ hohen Anteil und stehe
damit offensichtlich nicht alleine da.
Nop schrieb:
>> Die Entscheidung, welcher Code nun in der ELF drin sein muss, trifft für>> mich der Linker.> Ist bei ernsthafteren Projekten nicht möglich, weil Du dann dead code> drin hast.
Ich wusste garnicht, dass parametrierbarer Code verboten ist (Faktoren
von 0 oder 1 ergeben immer toten Code). Im Gegenteil, ich bin mir sogar
relativ sicher, dass man innerhalb einer Codebasis gewisse
Funktionalität halten darf, die nicht in jedem Produkt vorhanden ist.
S. R. schrieb:> Ich vertraue modernen Compilern zu einem relativ hohen Anteil und stehe> damit offensichtlich nicht alleine da.
Naja, vielleicht kannste ja mit nem Tip von nem Profi was anfangen:
http://embeddedgurus.com/stack-overflow/tag/printf/> Im Gegenteil, ich bin mir sogar> relativ sicher, dass man innerhalb einer Codebasis gewisse> Funktionalität halten darf, die nicht in jedem Produkt vorhanden ist
Dann sei Dir mal da ganz sicher. So sicher, wie Du sein kannst, ohne
Erfahrung mit entsprechend regulierten, ernsthaften Produkten zu haben.
So sicher, wie ein Bastler nur sein kann.
Nop schrieb:> Dann sei Dir mal da ganz sicher. So sicher, wie Du sein kannst, ohne> Erfahrung mit entsprechend regulierten, ernsthaften Produkten zu haben.
Willst du damit sagen, dass ein Hersteller von z.B. "Motorsteuergerät X"
für zwei Produkte "mit Funktion Y" und "mit Funktion Z" zwei völlig
getrennte Codebasen bereithalten muss, die dann zueinander aus 80%
Copy/Paste bestehen?
Bei uns laufen einige Projekte (im Bereich des automatisierten Testens),
die auf Produktlinienmanagement (also gemeinsame Codebasis mit
verschiedenen Funktionalitäten) abzielen, mit Blick auf Automotive.
Und nur um mal willkürlich ein Beispiel rauszunehmen: AVM könnte nie ein
Sicherheitsupdate für mehrere Generationen von Fritzboxen anbieten, wenn
sie nicht eine gemeinsame (zumindest pro Hardware-Generation) Codebasis
hätten.
Davon mal abgesehen bin ich der festen Überzeugung, dass die meisten
hier diskutierten Projekte nicht in der Welt solcher "entsprechend
regulierten, ernsthaften Produkte" liegt, und dann sind float, printf
und malloc - wenn vernünftig eingesetzt - auch auf einem AVR sinnvoll.
Mache mal Butter bei die Fische und gib mir Grund, meine Weltanschauung
zu ändern. Denn wie du richtig erfasst, lebe ich nicht in einer
MISRA-regulierten Welt.
Nop schrieb:> Naja, vielleicht kannste ja mit nem Tip von nem Profi was anfangen:> http://embeddedgurus.com/stack-overflow/tag/printf/
Dann lies den aber auch bis zum Ende durch.
> Now do the above mean you should always eschew formatted> output functions?> No! Indeed I recommend the use of vsprintf() here for certain> classes of problem.> What I do recommend is that you think long and hard before> using these functions to ensure that you really understand> what you are doing (and getting) when you use them.
Rufus Τ. F. zitierte im Beitrag #4746179:
>> What I do recommend is that you think long and hard before>> using these functions
Und nach gar nicht mal SO viel hartem Nachdenken kommt man unweigerlich
dazu, daß printf für die Ausgabe eines characters auf der Seriellen
einfach Quatsch ist.
S. R. schrieb:> Willst du damit sagen, dass ein Hersteller von z.B. "Motorsteuergerät X"> für zwei Produkte "mit Funktion Y" und "mit Funktion Z" zwei völlig> getrennte Codebasen bereithalten muss, die dann zueinander aus 80%> Copy/Paste bestehen?
Das ist der einfachste Weg. Man kann natürlich auch sein Repo aufspalten
in einen "common part", der bei beiden identisch ist, und in einen
"project part", wo die unterschiedlichen 20% drinstehen. Das macht man
üblicherweise, wenn man viele nur leicht unterschiedliche Produkte
anbietet.
Das ist dann kein leerer Code, weil diese 80% in beiden Varianten voll
genutzt werden.
Wobei das dann ziemlich viel Papierkrieg wird, wenn sich im common part
etwas ändert, weil aus Projekt Y etwas eingeflossen ist, was auch
Projekt Z betrifft - da muß dann der Dokuprozeß schon wirklich gut
aufgesetzt sein.
> Und nur um mal willkürlich ein Beispiel rauszunehmen: AVM könnte nie ein> Sicherheitsupdate für mehrere Generationen von Fritzboxen anbieten, wenn> sie nicht eine gemeinsame (zumindest pro Hardware-Generation) Codebasis> hätten.
AVM stellt auch Produkte her, bei denen keine besondere Abnahme
gefordert ist. Geht auch nicht, wenn man Linux verwendet, weil das
einfach viel zu komplex ist, als daß man da jemand die Hausmarke von 90%
code coverage beim testing knacken könnte.
Die übrigen 10% sind übrigens bei ernsthaften Projekten zu begründen,
und zwar jeder beim Test nicht durchlaufene Zweig einzeln. Mitsamt
Analyse, inwieweit der problematisch werden könnte. Und das ist noch die
harmlose Variante von coverage, weil das ja nur die Verzweigung erfaßt.
> Davon mal abgesehen bin ich der festen Überzeugung, dass die meisten> hier diskutierten Projekte nicht in der Welt solcher "entsprechend> regulierten, ernsthaften Produkte" liegt
Ich halte es aber für eine gute Praktik, bei Hobbyprojekten höhere
Standards anzusetzen, gerade weil man dabei ja auch was lernen will.
Zudem laufen sie dann auch einfach solider, und man spart sich Debugzeit
gerade bei den ekligen Fehlern, die nur manchmal auftreten.
Es hat ja seinen Grund, wieso gewisse Praktiken bei ernsthaften
Projekten nicht erlaubt sind. Natürlich ist es etwas anderes, ob die
Fahrzeugsteuerung hingepfuscht wird und dann Leute sterben wie im Falle
von Toyota, oder ob halt ein AVR-kontrollierter LED-Sternenhimmel an der
Zimmerdecke mal nicht geht.
>, und dann sind float, printf> und malloc - wenn vernünftig eingesetzt - auch auf einem AVR sinnvoll.
Für Bastelzwecke, oder wenn bloß Komfort bei Endverbraucherprodukten auf
dem Spiel steht, ist es ohnehin völlig gleichgültig, da kann man
abliefern, was immer man will. Endverbraucher kaufen ja auch den letzten
Schrott, Hauptsache 10 Euro billiger. Das kann man dann wirtschaftlich
letztlich nur noch schaffen, indem man vorgefertigte Bibliotheken
irgendwie zusammenbrät und ein bißchen mal durchprobiert, ob's läuft.
Jegliche Entwicklungszeit da reinzustecken würde das Projekt zum
Minusgeschäft machen.
Ich habe übrigens auch bereits Projekte erlebt, die nicht reguliert
waren, und wo ausgiebig mit malloc gearbeitet wurde. Mein Widerstand
wurde ignoriert, weil ich seinerzeit ja erst zwei Jahre Berufserfahrung
hatte, und was weiß ein Neuling schon.
Weil man damit das Gerät je nach Umständen dynamisch handhaben konnte,
was bei der BOM einen Speicherriegel gespart hat. Das Ende vom Lied war
dann, daß man dem Ding einen wöchentlichen Autoreset verpaßt hat, anders
war das nicht mehr in den Griff zu kriegen.
Auch das eine typische Folge dynamischer Speicherverwaltung - denn der
Zustand des Systems hängt dann davon ab, was es vorher getan hat. Der
Knackpunkt dabei ist - das System ist nicht mehr testbar.
Übrigens ist das Argument "der Compiler macht das schon" in solchen
Kreisen aus gutem Grund ebenfalls verpönt. Das hat mit -O0 zu laufen,
sonst muß stärkere Hardware genommen werden. Hintergrund ist z.B. die
völlig hirntote Algorithmik zum automatischen Inlining beispielsweise
beim GCC, die einen gerne mal einen stack overflow beschert, weil der
Compiler es eben nicht besser weiß.
Natürlich macht man das als Hobbyist nicht so, man will schließlich
maximalen Wums aus dem kleinen Ding zaubern, was man da vor sich hat.
Aber die Problematik sollte man schon verstehen, und das fängt damit an,
daß man seinem Compiler mißtraut. Nicht ganz so sehr wie sich selber
natürlich, aber doch schon.
Nachtrag: Wenn ich Dinge habe, die jeweils viel Speicher brauchen, die
ich aber nie zugleich mache, dann nehme ich nicht dynamische
Allozierung, sondern lege das auf den Stack, und zwar in Funktionen, die
nicht im selben call tree liegen. Weswegen ich auch so allergisch gegen
das auto-inlining beim GCC bin und ihn an solchen Stellen mit
haufenweise no-inline-Funktionsattributen im Zaum halte.
Erstens fragmentiert der Stack garantiert nicht, zweitens gibt es keine
Allokationsfehler, drittens muß ich das nicht manuell freigeben,
viertens habe ich keinen Allokator mit gelegentlich irrwitzigen Zeiten
drin, und zuguterletzt kann ich über den call tree den maximalen
Stackverbrauch leicht ausrechnen. Dadrauf kommt dann noch der
Stackbedarf aller Interrupts zusammengenommen sowie ein bißchen Reserve.
Größere Batzen, die aber permanent gebraucht werden, gibt's dann halt
als globale Variable, nach Möglichkeit mit reduziertem Scope.
Am Ende muß man sich nur das Mapfile und den Calltree anschauen, und man
hat keinen Speicherüberlauf mehr und muß sich nie mit Fehlerbehandlung
für malloc herumschlagen. Nicht nur, daß ich das aus ernsthafteren
Projekten so kenne, es funktioniert auch im Hobby angenehm robust. Spart
außerdem einiges an Debugzeit für schwer nachverfolgbare Fehler.
Was wohl der Grund ist, wieso sich Profis irgendwann mal hingesetzt
haben und das beschlossen haben.
Du verbietest also jegliche Form von dynamischem Speicher und möglichst
jeden Komfort, um zertifizierbaren und TÜV-prüfbaren Code zu haben. Mag
in deinem Job sinnvoll sein, für Hobbyprojekte ist es das in deiner
Absolutheit aus meiner Sicht nicht.
Ich werde auch weiterhin -Os (oder -O3, je nach Bedarf), printf und
float benutzen. Und das auch jedem raten, der davon profitieren kann und
nicht zufällig Autopiloten für Flugzeuge schreibt oder schlicht keinen
Platz dafür hat.
Nop schrieb:> Weil man damit das Gerät je nach Umständen dynamisch handhaben konnte,> was bei der BOM einen Speicherriegel gespart hat. Das Ende vom Lied war> dann, daß man dem Ding einen wöchentlichen Autoreset verpaßt hat, anders> war das nicht mehr in den Griff zu kriegen.
Ich kenne die Variante, dass in ein Produkt ein abgekündigter
RAM-Baustein eindesigned wurde - und als der nicht mehr lieferbar war,
gab es nur noch einen halb so großen Baustein (16 statt 32 MB). Dem
Linux wurde am Ende ein zram als swap gegeben, dann lief die Anwendung
wieder.
> Übrigens ist das Argument "der Compiler macht das schon" in solchen> Kreisen aus gutem Grund ebenfalls verpönt. Das hat mit -O0 zu laufen,> sonst muß stärkere Hardware genommen werden.
Was bin ich froh, dass ich in solchen Kreisen nicht unterwegs sein muss.
Da muss meine CPU nicht mit hirntotem Code sinnlos beschäftigt werden,
sondern die restlichen Takte im Leerlauf verbringen (und Strom sparen).
Wenn ihr den Compiler quasi abschaltet, warum nutzt ihr dann überhaupt
einen?
> Aber die Problematik sollte man schon verstehen, und das fängt damit an,> daß man seinem Compiler mißtraut. Nicht ganz so sehr wie sich selber> natürlich, aber doch schon.
Ich misstraue lieber den Bibliotheken, die ich einbinde. Die sind meist
von deutlich schlechterer Qualität als der Compiler selbst.
Aber ja, man sollte wissen, was man tut. Daher nutze ich auf den
kleinsten Systemen malloc auch nur für die Initialisierung (oder
printf).
Nop schrieb:> Für Bastelprojekte kann man das sicherlich so machen. Bei ernsthafteren> Projekten käme man damit nichtmal durchs Code Review. Viele Kunden> fordern z.B. MISRA-Einhaltung. Aus gutem Grund, ich habe schon genug> Projekte mit dynamischer Speicherverwaltung gesehen, die genau daran> eingegangen sind. Deswegen verbannt man das kurzerhand komplett, und das> Problem ist gelöst.
Das ist der Grund, warum ich so Dinge wie MISRA nicht mag. Dinge, die
von Anfängern oft falsch benutzt werden, werden einfach komplett
verbannt, was dann die Leute, die über das Anfänger-Stadium hinaus
gewachsen sind, in ihrer Arbeit behindert. Meiner Meinung nach muss und
kann man gar nicht mit ein paar Regeln dafür sorgen, dass ein Anfänger
fehlerfreien sicherheitskritschen Code schreibt. Wer's kann, braucht
kein MISRA, um es richtig zu machen, wer es nicht kann, wird's auch mit
MISRA nicht hinbekommen. Oder anders: MISRA macht auch schlechten
Programmieren keine guten Programmierer.
Rolf M. schrieb:> Das ist der Grund, warum ich so Dinge wie MISRA nicht mag. Dinge, die> von Anfängern oft falsch benutzt werden, werden einfach komplett> verbannt
Was so nicht stimmt. Hast Du Dir denn MISRA mal genauer durchgelesen?
Man kann durchaus gegen die Regeln verstoßen. Allerdings braucht man
dann einen definierten Prozeß, wo man dokumentiert, daß man eine
MISRA-Regel mißachtet hat, wieso das technisch nicht anders möglich war,
und eine Analyse der Konsequenzen - speziell, daß es keine negativen
gibt. Das muß dann vom Entwickler abgezeichnet werden, bei den
heftigeren Regeln auch von dessen Vorgesetzten.
Die Konsequenz ist, daß man nicht einfach "mal eben so" dagegen
verstößt, sondern gezwungen wird, gründlich nachzudenken. Die natürliche
Faulheit des Entwicklers führt von selber dazu, daß man sich diesen
Aufwand nur gibt, wenn es wirklich notwendig ist. Und nicht, weil es
bequem ist - der Zweck ist gerade, bequeme, aber in der Praxis
statistisch gesehen bedenkliche Konstrukte unbequem zu machen.
S. R. schrieb:> Du verbietest also jegliche Form von dynamischem Speicher und> möglichst jeden Komfort, um zertifizierbaren und TÜV-prüfbaren> Code zu haben.
Nein, sondern ich meide das, weil das aus gutem Grund in zertifizierten
Produkten nicht erlaubt ist. Zwar gebe ich Dir recht, daß das für
Hobbyprojekte auch nicht nötig ist. Dennoch möchte ich bei meinem
Hobbyprojekten, daß sie zuverlässig laufen.
Was übrigens auch der Grund ist, wieso ich auch privat Tools zur
statischen Code-Analyse einsetze.
> Ich werde auch weiterhin -Os (oder -O3, je nach Bedarf)
Privat nutze ich auch meistens -O2 und an Hotspots per Pragma -O3. Nur
muß man sich dann eben auch damit befassen, wie man dann z.B. den
Sanitiser benutzen kann, um diverse Arten von undefiniertem Verhalten
aufzudecken, von dem nach der Codierphase in jedem nicht-trivialen
C-Programm mit hoher Sicherheit was drinsteckt.
> float benutzen.
Float ist ja an sich auch unbedenklich. Wenn man eben mit Dingen
hantiert, wo Integer einfach nicht so geeignet sind, dann hat niemand
was gegen float. Im Gegenteil, das wird ja z.B. auf Cortex-M4 sogar von
der FPU unterstützt.
Wobei man die üblichen Fallen bei float natürlich schon kennen sollte.
Nie Vergleiche auf Identität, sondern nur >=, <=, >, <. Und keine
Schleifen mit Schrittweite von 0.1f, habe ich auch schon gesehen. Kein
Wunder, daß das bei MISRA ausdrücklich gelistet ist.
> Ich kenne die Variante, dass in ein Produkt ein abgekündigter> RAM-Baustein eindesigned wurde
Uhh, fiese Falle, ja.
> Wenn ihr den Compiler quasi abschaltet
Den Optimierer, nicht den Compiler. Der Compiler setzt ja nach wie vor
C-Code in Maschinencode um. Ich habe auch ganze Projekte schon in
Assembler gehabt, aber die Entwicklungszeiten sind deutlich länger, und
die Fehleranfälligkeit auch. In C hat man ein laxes Typensystem, in
Assembler gar keines mehr.
> Aber ja, man sollte wissen, was man tut. Daher nutze ich auf den> kleinsten Systemen malloc auch nur für die Initialisierung (oder> printf).
Für die Init-Phase, also quasi-statisch, geht das ja auch. Ist dann
Geschmackssache, ob man nicht gleich ein statisches Array dafür anlegt.
Nop schrieb:> Rolf M. schrieb:>> Das ist der Grund, warum ich so Dinge wie MISRA nicht mag. Dinge, die>> von Anfängern oft falsch benutzt werden, werden einfach komplett>> verbannt>> Was so nicht stimmt. Hast Du Dir denn MISRA mal genauer durchgelesen?>> Man kann durchaus gegen die Regeln verstoßen. Allerdings braucht man> dann einen definierten Prozeß, wo man dokumentiert, daß man eine> MISRA-Regel mißachtet hat, wieso das technisch nicht anders möglich war,
Und genau das ist der Punkt, bei dem es einen behindert. Man kann
bestimmte Konstrukte nur noch nutzen, wenn es technisch nicht anders
geht, und nicht mehr, wenn sie einfacher oder praktischer sind als die
Alternative.
Viele Regeln verbieten Konstrukte, die häufig missbraucht werden. Die
können aber bei bestimmungsgemäßem Gebrauch die Codequalität auch
verbessern. MISRA unterbindet das aber auch.
> Die natürliche Faulheit des Entwicklers führt von selber dazu, daß man> sich diesen Aufwand nur gibt, wenn es wirklich notwendig ist.
Eben. Man macht einfach nutzbare Dinge so umständlich, dass sie faktisch
nicht mehr nutzbar sind.
> Und nicht, weil es bequem ist - der Zweck ist gerade, bequeme, aber in> der Praxis statistisch gesehen bedenkliche Konstrukte unbequem zu machen.
"statistisch gesehen bedenkliche Konstruke"... interessant formuliert.
Ich halte es für sinnvoller, Leuten beizubringen, ihr Werkzeug wirklich
zu verstehen, statt dessen Funktionalität künstlich zu beschneiden,
damit auch welche, die es nicht verstehen, bestimmte Fehler nicht machen
können.
Nop schrieb:> Nein, sondern ich meide das, weil das aus gutem Grund in zertifizierten> Produkten nicht erlaubt ist. Zwar gebe ich Dir recht, daß das für> Hobbyprojekte auch nicht nötig ist. Dennoch möchte ich bei meinem> Hobbyprojekten, daß sie zuverlässig laufen.
Dynamischer Speicher macht nicht automatisch ein Programm unzuverlässig.
Wobei man in reinem C mangels jeglicher Automatismen tatsächlich sehr
aufpassen muss. Ich meide es da auch, wenn möglich.
Rolf M. schrieb:> Eben. Man macht einfach nutzbare Dinge so umständlich, dass sie faktisch> nicht mehr nutzbar sind.
Genau das ist die Absicht, weil die Praxis gezeigt hat, daß die Fallen
dabei eben immer auf SCHNAPP gestellt sind. Und jeder Produktfehler
letztlich sehr, sehr teuer werden kann. Besonders, wenn sich daran auch
noch vier Wochen Testfeld anhängen, und für eine Vollabnahme gibt sich
ein Kunde da nicht mit inkrementellen Delta-Tests zufrieden.
> Ich halte es für sinnvoller, Leuten beizubringen, ihr Werkzeug wirklich> zu verstehen, statt dessen Funktionalität künstlich zu beschneiden,> damit auch welche, die es nicht verstehen, bestimmte Fehler nicht machen> können.
Die Praxis zeigt, daß dem nunmal nicht so ist. Weil die Leute, die hier
Software schreiben, in erster Linie mal nicht Programmierer sind,
sondern eher E-Techniker. Weil man auch noch ein gehörig Maß an Wissen
um die Hardware mitbringen muß. Deren Wissen besteht nicht so sehr
darin, sich um die Fiesheiten von C Gedanken zu machen, als im
Systemverständnis.
Versuch denen mal begreiflich zu machen, daß ein Bitfield sich für
Registermapping oder Messagemapping sich so lecker anzubieten scheint,
man aber den Köder trotzdem nicht schlucken darf, weil man sich dann in
einem riesigen Wolleknäuel wiederfindet. Mit Krabben zwischendrin.
Ich habe ja meine ausgeprägte Haßliebe zu C, aber im Grunde finde ich es
den genialsten portablen Makro-Assembler überhaupt. Nur, ich beschäftige
mich aus Interesse auch damit. Die meisten Leute tun das aber nicht,
schon gar nicht in der Freizeit. Da C Dir als Programmierer das gibt,
was Du sagst (und nicht das, was Du willst), ist das keine besonders
gute Kombination mit Leuten, die im Grunde bei Pascal stehengeblieben
sind (btw. eine sehr gute Lehrsprache).
Und das wird alles immer schlimmer, seitdem man an den Unis auch noch
auf Java umstellt und die Leute mit Pointerarithmetik und mehrfach
dereferenzierten Pointern ebensowenig in Berührung gekommen sind wie mit
den Tücken realer Rekursion.
Spolsky schrieb dazu schon vor Jahren:
http://www.joelonsoftware.com/articles/theperilsofjavaschools.html