Kann man in C ohne Pointer überhaupt programmieren? Wenn es um Zuverlässigkeit und Sicherheit geht stehen Pascal und ADA ganz oben. Heutzutage wird aber viel in C geschrieben, nur "geht" C überhaupt ohne Pointer? Ist C nicht sehr Pointerlastig? Wird in der Kernelentwicklung auf Pointer gesetzt?
:
Verschoben durch Admin
weiß jemand, ZUBERLÄSSIG! welche Porgammiersprache in Autos für sicherheitskritische Komponenten verwendet wird?
Zwitter schrieb: > Wird in der Kernelentwicklung auf Pointer gesetzt? Natürlich. Guckste du hier Z. B. In der Funktion tty_ready https://elixir.bootlin.com/linux/latest/source/drivers/tty/tty_io.c Wer pointer versteht kann damit guten und schnellen, sicheren Code schreiben.
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Ein Leben ohne Pointer ist möglich, aber sinnlos.
Geben Frage wie willst du jemals Dma ohne pointer verwenden weil der Dma Controller doch eine Speicheradresse verlangt.
Um nun weitere Informationen zu geben. Ich weiß aus sicherer Quelle das eben beim LFZ, solche Steuergeräte eben NICHT mit Pointer programmiert werden. Diese sind strikt verboten von den die die Vorgaben machen. Leider konnte mir diese Person (die eben diese Vorgaben macht) nicht sagen, wie die Programmiersprache heißt, mit der KFZ ABS etc. Steuergeräte programmiert werden. Und das würde mich eben interessieren. C ist es dann offenbar nicht, wenn mit C nicht vernünftig ohne Pointer programmiert werden kann. Obwohl sie vermutete das es ein C Unterbau ist aber diese vom Hersteller modifiziert sein könnte Und nein, nur weil ein guter Programmierer ´mit Pointern umzugehen weiß, sind diese dennoch unsicher!! Wer verbreitet immer diesen Quatsch. Auch die besten machen Fehler! wer das nicht sieht, leidet unter gnadenloser Selbstüberschätzung
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Jein, nur wenn man keinen heap-Speicher verwendet und Übergabe in Funktionen als Array-Referenz a[] akzeptiert. In embedded Applikationen ist es aber oftmals sinnvoll, auf heap zu verzichten. Wer statt C auf C++ umschwenkt, kann pointer in Klassen verstecken und nur mit Referenzen arbeiten, dann entfällt die Pointerarithmetik und die Freigabeproblematik. Damit C Programme sicherer werden, gibt es Programmierstile (z.B. wie WIN32 API: Buffer nur mit Längenangabe übergeben) und Checkprogramme (lint, Fortify). So lässt sich nicht nur funktionierende Software schrieben, sondern auch viele Fehler von vorrneherein vermeiden. Unsere Erfahrungen mit C und C++ sagen aber, daß viele C++ Compiler aus korrektem C++ Quellcode leider falschen Maschinencode generieren (z.B. IBM Mainframe, uC) während bei C zumindest der Compiler meist funktioniert (die Zeiten, wo der fehlerhaft war, wie beispielsweise Borland C, sind inzwischen wohl weitgehend überwunden). Warum ein C Programm nicht läuft, sieht man also am Quelltext, bei C++ muss man teilweise auf den Maschinencode starren und sehr lange nach der Ursache forschen.
Michael B. schrieb: > Warum ein C Programm nicht läuft, sieht man also am Quelltext, > bei C++ muss man teilweise auf den Maschinencode starren > und sehr lange nach der Ursache forschen. Hast du die letzten 20 Jahre im Dornröschenschlaf verbracht? 10 davon setzte ich wenigstens schon C++ auf kleinen Controllern ein und falschen Maschinencode musste ich noch nie debuggen.
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Ja, kann man. > Wenn es um Zuverlässigkeit und Sicherheit geht stehen Pascal und ADA > ganz oben. Pascal hat viele andere Sprachen inspiriert, von daher hat es schon seinen Verdienst. In der Industrie eingesetzt Ada hat seinen Platz in der Luft- und Raumfahrtbranche, wird aber außerhalb dieser nur wenig genutzt. > Heutzutage wird aber viel in C geschrieben, nur "geht" C überhaupt ohne > Pointer? Ja. > Ist C nicht sehr Pointerlastig? Das kommt darauf an. > Wird in der Kernelentwicklung auf Pointer gesetzt? Ja sicher, zum Beispiel im Linux-Kernel. Freilich ist es nur eine Minderheit aller Programmierer, die überhaupt auf so einer niedrigen Abstraktionsebene arbeiten. Die meisten nutzen doch eher Betriebssysteme und Treiber, die von anderen bereitgestellt werden, anstatt diese selbst zu entwickeln.
Zwitter schrieb: > weiß jemand, ZUBERLÄSSIG! welche Porgammiersprache in Autos für > sicherheitskritische Komponenten verwendet wird? Misra C zB.
Zwitter schrieb: > weiß jemand, ZUBERLÄSSIG! welche Porgammiersprache in Autos für > sicherheitskritische Komponenten verwendet wird? In der Automobilbranche wird sehr oft C und C++ eingesetzt, und zwar nach dem MISRA-Standard: https://de.wikipedia.org/wiki/MISRA-C
Mark B. schrieb: > Pascal hat viele andere Sprachen inspiriert, von daher hat es schon > seinen Verdienst. In der Industrie eingesetzt Das sollte heißen: "In der Industrie eingesetzt wird es aber nur wenig."
> Kann man in C ohne Pointer überhaupt programmieren? Ja. > Wenn es um Zuverlässigkeit und Sicherheit geht stehen Pascal und > ADA ganz oben. Nicht wirklich. > Heutzutage wird aber viel in C geschrieben, nur "geht" C überhaupt > ohne Pointer? Selbstverständlich. > Ist C nicht sehr Pointerlastig? Das Eine hat mit dem Anderen genau garnix zu tun. > Wird in der Kernelentwicklung auf Pointer gesetzt? Selbstverständlich. Du stellst die falschen Fragen. Die korrekten Antworten auf die richtigen Fragen lauten in etwa "Ja, selbstverständlich kann man in C sicherheitskritische Applikationen schreiben. Wird tagtäglich millionenfach erfolgreich getan. Wie in allen(!! - sinnvollen -) Sprachen gibt es auch in C Feature, die man beherrschen muss um sie dann adäquat und sicher zu nutzen." HTH
ah ok, stimmt von MISRA hatte ich auch schon mal gehört. Ich werde wohl langsam alt:-)
Zwitter schrieb: > weiß jemand, ZUBERLÄSSIG! welche Porgammiersprache in Autos für > sicherheitskritische Komponenten verwendet wird? Ja: Es wird zwischen Handcode und Autocode unterschieden. Autocode zumeist mit Simulink, ist dann aber nach dem Code-Generieren auch C. Einiges wird auch händisch in C programmiert. Sicherheitskritisch = ISO26262, ASIL B, C, D Mit entsprechenden Coding-Guidelines kein Problem. Programmierung mit Pointern ist zumeist explizit untersagt, ebenso das Benutzen der Standardbibliotheken. Dynamische Speicherverwaltung (malloc, new) ist im ASIL-Kontext zwar nicht verboten, aber man hat das Nachweisproblem, dass es sicher funktioniert. Der einfachere Weg ist darauf zu verzichten.
"Ist C nicht sehr Pointerlastig? Das Eine hat mit dem Anderen genau garnix zu tun." Das liest sich in vielen Lehrbüchern aber ganz anders...da wird auch die Enge Bindung von Pointern und dem häufigen Einsatz gesprochen...daher meine Frage..
Natürlich wird auch sicherheitsrelevante Software in C und unter Verwendung von Pointern geschrieben. Allerdings kann es bei Pointer-Arithmetik gewisse Einschränkungen geben, da hier leicht Unsinn passieren kann.
Wie sehe denn bei diesem Beispiel die GUTE Version aus? Was könnte man denn deutlicheres als if (I == a) Schreiben? Also um dem häufigen Fehler mit = und == zu begegnene? Die folgende Variante ist schlecht, weil man nicht nachvollziehen kann, ob der Programmierer einen Fehler gemacht hat, oder absichtlich dieses Konstrukt gewählt hat:
1 | if ( i = a ) |
2 | { |
3 | /* Anweisung */ |
4 | } |
Der Compiler würde diese Variante erkennen und übersetzen zu:
1 | /* Zuweisung */ |
2 | i = a; |
3 | /* dann ein Vergleich */ |
4 | if ( i != 0 ) |
5 | { |
6 | /* Anweisung */ |
7 | } |
Möglicherweise meinte der Programmierer == und hat versehentlich = geschrieben, so dass die Anweisung nur bei Gleichheit von i und a ausgeführt werden soll:
1 | if ( i == a ) |
2 | { |
3 | /* Anweisung */ |
4 | } |
wäre es dann für einen Anfänger nicht viel Sinnvoller gleich Misra C konform zu lernen?
Einer dümmer als der andere schrieb: > Geht lieber Ostereier sammeln... Am Todestag des Herrn? Ein echter Katholik hat heute gar keinen Spaß, geht auch nicht ins Internet sondern in sich (geistig) und trauert. Ostereier ist erst Montag. Arbeitgeberfreundlicher wäre ein Ableben am Samstag und eine Auferstehung am nächsten Tag gewesen, aber so weit haben die Verfasser des neuen Testaments nicht gedacht. Und um noch eins drauf zu Setzen: Ich liebe Pointer. Warum was durch die Gegend kopieren, wenn ich nur die Adesse übergeben muss?
ODer ist damit gemeint das es wie in Pascal gemacht werden muss Also eine Zuweisung nur so erlaubt ist und eben nicht innerhalb einer IF Schleife erlaubt ist?
1 | /* Zuweisung */ |
2 | i = a; |
3 | /* dann ein Vergleich */ |
4 | if ( i != 0 ) |
5 | { |
6 | /* Anweisung */ |
7 | } |
"Ich liebe Pointer. Warum was durch die Gegend kopieren" Weil wie oben erwähnt, es im KFZ ABS Steuergeräten etc, schlicht nicht erlaubt ist, da kannst du sie noch so sehr lieben, du würdest dort nicht arbeiten..
Beitrag #6641958 wurde von einem Moderator gelöscht.
Zwitter schrieb: > Also um dem häufigen Fehler mit = und == zu begegnene? = an dieser Stelle in den eigenen Coding-Regeln verbieten. Das alleine hilft nicht aber wenn man die entsprechenden bzw. alle Compiler-Warnings anschaltet und vor allem dann auch alle Warnings abarbeitet. Ziel muss es sein warningsfreien Code zu haben. Pointer sind in sicherheitskritischer Software nicht grundsätzlich verboten aber die meisten Standards verbieten explizite Pointerarithmetrik. Wie oben schon gesagt ist implizite Pointerarithmetrik, d.h. Arrays erlaubt.
"Lachen, umdrehen, weggehen. So muss man mit diesen Subjekten verfahren." Und warum kommentierst du es dann? Irgendwie bekommst du nicht mal deine eigenen Regeln eingehalten du Clown. Hoffen wir mal das du nicht als Programmierer arbeitest
Pascal ist keine Sprache die in der Industrie eingesetzt wurde, moeglicherweise Turbo Pascal oder Delphi, und das auch nur selten IMO. Zwitter schrieb: > Die folgende Variante ist schlecht, weil man nicht nachvollziehen kann, > ob der Programmierer einen Fehler gemacht hat, oder absichtlich dieses > Konstrukt gewählt hat Wenn jemand sowas absichtlich gewaehlt hat, darf man ruhig ruhig eine Rede halten ueber den Programmmierdstil. Da C auch eine Zuweisung als Anweisung versteht, kann der Compiler nicht wirklich wissen ob "das so gemeint war", als Mensch darf man schon wissen dass "man das so nicht macht". Dafuer gibt es ja vorgaben, code rewiews, statische code analyse etc. pp.
Der genannte Text "Die folgende Variante ist schlecht, weil man nicht nachvollziehen kann, > ob der Programmierer einen Fehler gemacht hat, oder absichtlich dieses > Konstrukt gewählt hat" stammt nicht von mit und ich wollte auch nicht über den Text philosophieren, es geht darum wie es gemeint ist wie man es dann richtig macht. NAch MiSRA Standard ist es NICHT erlaubt, also gibt es da nichts zu diskutieren oder Reviewen. Wie gesagt, daher meine Frage, wie es denn nach MISRA Standard RICHTIG wäre
Bei Strings lassen sich Pointer in C nur schwer vermeiden.
:
Bearbeitet durch User
Zwitter schrieb: > Leider konnte mir diese Person (die eben diese Vorgaben macht) nicht > sagen, wie die Programmiersprache heißt, mit der KFZ ABS etc. > Steuergeräte programmiert werden. Ganz grosses Kino. Die Person macht Vorgaben, weiss aber nicht wofuer. wendelsberg
DoS schrieb: > Warum was durch die > Gegend kopieren, wenn ich nur die Adesse übergeben muss? Frage ich mich auch schon die ganze Zeit.
temp schrieb: > falschen Maschinencode musste ich noch nie debuggen Ja nun, wenn man nur Kinderprogramme schreibt, wird das vielleicht auch so bleiben. Ich bin auch kein Freund davon, die modernsten Sprachkonstrukte z.B. aus C++11, C++14, C++17, C++20 zu verwenden, aber nicht jeder hat die Erfahrung warum das Verzichten besser ist.
Michael B. schrieb: > temp schrieb: >> falschen Maschinencode musste ich noch nie debuggen > > Ja nun, wenn man nur Kinderprogramme schreibt, wird das vielleicht auch > so bleiben. > > Ich bin auch kein Freund davon, die modernsten Sprachkonstrukte z.B. aus > C++11, C++14, C++17, C++20 zu verwenden, aber nicht jeder hat die > Erfahrung warum das Verzichten besser ist. Braucht auch kein Schwein. Jedes Jahr kommt neuer syntactical sugar dazu, die in extrem seltenen Fällen irgendwelche Vorteile bieten. Blickt doch eh keiner mehr durch. Demnächst klatscht man da vermutlich noch ne garbage collection rein. dann ist die kloake voll xD
"Ganz grosses Kino. Die Person macht Vorgaben, weiss aber nicht wofür." Keine Ahnugn, Sie ist ING. und das ist ihre Aufgabe, aber sie programmiert halt nicht sondern macht Vorgaben bzw überprüft ob diese umgesetzt wurden, sie sitzt aber ÜBER den Programmierern, daher wird die eigentlich Überprüfung wohl ein untergebener durchführen und das Häkchen setzen. Ich hab auch nicht gefragt, weil welcher Firma sie arbeitet, aber sie ist dennoch bei TEstfahren gelegentlich dabei. Ob das nun speziell zum Aufgabenbereich gehört oder mehr dem Ausgleich und Erfahrungsammlung dient kann ich dir nicht sagen
Kann noch nicht mal in einem Forum zitieren, will aber bei den Großen über Code mitreden. Mit Dingen vom Hörensagen. Wie lächerlich wird es noch?
DoS schrieb: > Ein echter Katholik hat heute gar keinen Spaß, geht auch nicht ins > Internet sondern in sich (geistig) und trauert. Du meinst Protestant. Katholiken gehen morgens den Kreuzweg und nachmittags zur Karfreitagsliturgie. Dazwischen wird gearbeitet, ausmisten, schrubben und so, alles was keinen Krach macht (da gesetzlicher stiller Feiertag)
Macht halt keinen Sinn das ganze. Man würde jegliche dynamische Allokierung wegschmeißen (egal wo der mempool liegt, ob nu auf stack oder heap). Zusätzlich jegliche Call-by-Reference wegschmeißen. Function Pointer haste dann auch keine mehr, also keine Callbacks, ja, noch nicht mal ein "Hello world" ließe sich programmieren, weil String-Literale konstante Pointer sind - das wäre eine sehr seltsame Programmiersprache...
Michael B. schrieb: > Ich bin auch kein Freund davon, die modernsten Sprachkonstrukte z.B. aus > C++11, C++14, C++17, C++20 zu verwenden, aber nicht jeder hat die > Erfahrung warum das Verzichten besser ist. Hier bin ich nicht mit dir einverstanden! "Modern" sollte meiner Ansicht nach, kein Argument sein, um eine Spracheigenschaft abzulehnen. Es sei denn, man muss aufgrund irgendwelcher Vorgaben zu der pre C++11 Zeit kompatibel bleiben. Natürlich sind auch die "modernen" Spracheigenschaften, und mitgelieferten Tools/Libs nicht per se der heilige Gral und mit der gewohnten Vorsicht einzusetzen. Das gilt allerdings genauso für uralte Features, wie z.B. Goto, Pointer usw. Einfaches Beispiel: Die Iteration über ein Array wird durch den "range based for loop" um Größenordnungen einfacher und sicherer. Es gibt keine Chance, sich da mit Pointern oder Array Indices zu verheddern. Unter solchen Umständen kann "modern" doch kein schlaues Argument sein.
Da es MISRA aber nun mal gibt, ist halt die Frage wie solche Sachen dann umgesetzt werden. Daher steht es hier zur Diskussion
Mladen G. schrieb: > Pascal ist keine Sprache die in der Industrie eingesetzt wurde Doch. Man muss nur weit genug zurückgehen. 1960 gab es zwei Richtungen, die damals etwa gleich stark vertreten waren: Von HP gab es Rechner, die in Basic programmiert wurden, IBM / Siemens wurde meist in der ähnlichen Sprache Fortran IV programmiert, wie auch oftmals die PDP- (und später) VAX- Rechner von Digital Equipment. Philips Electrologica und Telefunken setzten mehr auf Algol, aus dem Pascal weiterentwickelt wurde. Turbo Pascal und Delphi kamen erst später, als die Firmen, die auf Pascal setzten, langsam an Marktanteil verloren. Ich habe im industriellen Umfeld mit allen diesen Rechnern gearbeitet. Algol / Pascal hat mehr Spaß gemacht, weil es eleganter war. Aber wegen der großen Verbreitung von IBM /360 und /370 bzw. Siemens 4004 musste ich hauptsächlich in Fortran programmieren.
Peter Panda2 schrieb: > Da es MISRA aber nun mal gibt, ist halt die Frage wie solche Sachen dann > umgesetzt werden. > Daher steht es hier zur Diskussion 1.) MISRA verbietet keine Pointer. 2.) Man darf Ausnahmen machen, diesen müssen begründet und dokumentiert sein. 3.) MISRA ist auf dem absteigenden Ast und heute oft nur noch ein Schimpfwort. 4.) Hör auf dir vom Not-Hauptschulabschluss Zwitter Dinge einreden zu lassen. Meine verwelkte Zimmerpflanze hat mehr Ahnung von C als der.
:
Bearbeitet durch User
Günni schrieb: > 1960 gab es zwei Richtungen, > die damals etwa gleich stark vertreten waren: > Von HP gab es Rechner, die in Basic programmiert wurden 1960 BASIC auf einem HP? Hmm. Beitrag für gestern, oder 1980? BASIC gibts seit 1964 und HP Computer seit 1966.
:
Bearbeitet durch User
warum werden Leute die Cylord eigentlich in solchen Foren nicht gesperrt? Zu seinem eigenen Schutz. Es ist ja offensichtlich das er Probleme hat. Ich die Bezeichnung Clown auch etwas unpassend, aber das bei ihm was nicht stimmt ist offensichtlich.
Peter Panda2 schrieb: > Es ist ja offensichtlich das er Probleme hat. Nö! Er spricht nur aus, was viele Andere denken, wenn man so einen Thread liest...
hat nur mit dem Thema nichts zu tun...in andern Foren wird sowas gelöscht
Arduino Fanboy D. schrieb: > Unter solchen Umständen kann "modern" doch kein schlaues Argument sein Fanboy halt, keinen Realtitätskontakt. Es geht darum, daß neue Compilererweiterungen leider nur mangelhaft getestet sind, und damit keineswegs portabel auch auf Plattformen die angebnliche die jeweilige Sprachversion schon unterstützen. Manches lernt man halt nur der Erfahrung und Schmerz, andere bleiben Fans.
Günni schrieb: > Siemens 4004 Die kenne ich auch noch, allerdings von der HW Seite. Zu der Zeit hatten wir bei einem Kunden noch eine 3003 am Leben gehalten. Das war noch richtig HW, Heavy Ware würde man heute sagen. Sorry für diesen Exkurs, es gibt leider nur wenige, die diese Zeit erlebt haben. Es freut mich immer, wenn jemand aus dieser Epoche hier schreibt.
Michael B. schrieb: > Fanboy halt, keinen Realtitätskontakt. Michael B. schrieb: > Manches lernt man halt nur der Erfahrung und Schmerz, andere bleiben > Fans. Was ist denn mit dir los? Der "range based for loop" hat sich seit über 10 Jahren bewährt. Wenn dein Compiler Ersteller es, in der Zeit, nicht geschafft hat, das stabil zu implementieren, dann gute Nacht. Mir scheint, deine Argumentation wurzelt eher in paranoiden Ängsten. Was auch die persönlichen Angriffe, wenigstens zum Teil, erklären würde.
Peter Panda2 schrieb: > hat nur mit dem Thema nichts zu tun...in andern Foren wird sowas > gelöscht Also ich bin bisher On-Topic, im Gegensatz zu dir.
Hallo Zwitter, was war eigentlich genau Deine Frage? Es klingt, als hätte Dich die halb verstandene Aussage dieser Person aufgeregt. Völlig ohne Pointer in C programmieren ist theoretisch möglich, aber nicht sehr praktisch. C kennt als Übergabemethode für Variablen nur call-by-value, und das heißt, dass eine Funktion ihre Parameter nicht verändern kann. Es können über Parameter also nur Informationen in die Funktion hinein fließen, aber nicht hinaus. Da in C zusätzlich die Rückgabewerte von Funktionen recht eingeschränkt sind, man also z.B. keine Tupel zurück geben kann, ist auch dieser Weg im allgemeinen nicht möglich. Dann könnte man noch die Ergebnisse über globale Variablen zurück geben, aber das wäre wirklich völlig abwegig und gerade der Sicherheit NICHT zuträglich. Der in C absolut übliche Weg aus dem Problem besteht darin, der Funktion einen Pointer auf dem Parameter statt des Parameters selbst zu übergeben. Das führt zwar zu Lästigkeiten bei der Syntax, funktioniert aber wunderbar und ist auch in sicherheitskritischen Umgebungen absolut zulässig. Was z.B. nach MISRA-C NICHT zulässig ist, ist Pointerarithmetik wie z.B int i = p2 - p1 oder p2 = p1 + 3. Also rechnen "per Hand" mit Pointern, alles was über das Weiterreichen der Adresse eines konkreten Objekts hinaus geht. Dynamischer Speicher, sprich malloc, ist im Regelfall auch nicht zulässig. Die Laufzeit ist schwierig abzuschätzen und es könnten komplexe Fehlerzustände entstehen. Das Problem mit dem if hast Du möglicherweise nicht verstanden.
1 | if (a = b) { ... |
ist absolut legales C. Es setzt a auf den Wert von b und führt den abhängigen Code aus, falls a != 0. Dummerweise ist das vergleichsweise selten das, was wirklich vom Entwickler beabsichtigt ist. Wenn man das verbietet und stattdessen
1 | a = b; if (a) { ... } |
fordert, kann das Problem nicht durch einen Tippfehler entstehen.
Zwitter schrieb: > Und das würde mich eben interessieren. > C ist es dann offenbar nicht, wenn mit C nicht vernünftig ohne Pointer > programmiert werden kann. > Obwohl sie vermutete das es ein C Unterbau ist aber diese vom Hersteller > modifiziert sein könnte > Und nein, nur weil ein guter Programmierer ´mit Pointern umzugehen > weiß, sind diese dennoch unsicher!! Sorry, Du hast keine Ahnung. Mit 90% ist das C, wenn es erfolgreich und sicher ist. Für das, wo man Pointer unbedingt braucht, haben andere Sprachen ähnliche Probleme. Du kannst mit statischer Codeprüfung praktisch jede programmiervoschrift relativ umfassend prüfen. Einem ABS hilft es nur bedingt, wenn ADA einen falschen Arrayzugriff erkennt. Wenn ich ein falsches ptr++ mache, ist das schlimmer als ein > statt ein <? Wenn Anfänger = statt == schreiben und nicht gewarnt werden, dann ist das Hybris. Das wird aber nicht dadurch besser, dass man stattdessen spezialsprachen entwickelt statt Warnungen einzuschalten
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Natürlich. Du kannst alle Pointer auf Arrayzugriffe mit Index umschreiben. Auf den erzeugten Code wird das nur wenig Einfluß haben.
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Viele glauben das geht nur in C++ aber es geht auch in C - und ist in der Embedded Programmierung sogar teilweise vorgeschrieben - kein malloc() Hier ein Beispiel: C struct wie C++ Klasse struct DATA { int x; }; struct DATA Data(int x) { struct DATA data; data.x = x; return data; } int main(void) { struct DATA test = Data(1); printf("%i", test.x); return 0; }
Thilo R. schrieb: > if (a = b) { ... Die mir bekannte übliche Vorgehensweise/Konvention ist.... Alle Warnungen aktivieren! Dann zeigt der Kompiler mit seinem Finger genau auf die Stelle. Sollte der Programmierer exakt das meinen, was er da geschrieben hat, dann Klammern drum. Die kosten nix. if (a = b) { ... // evtl unbeabsichtigt, wirft Warnung if ((a = b)) { ... // beabsichtigt, wirft keine Warnung
ah, ok danke. Das bringt etwas mehr an Erkenntnis:-) Danke auch an Thilo R. Und ja,A. S Ich habe keine Ahnung, deshalb frage ich hier so unwissend;-) Und Cyloard, nein ich bin kein Hauptschüler, selbst wenn wäre ginge dich das so gar nichts an. Ich bin Realschüler aber Legastheniker, kannst ja mal googeln was das ist, etwas Bildung würde dir offensichtlich auch nicht schaden
Zwitter schrieb: > Ich bin Realschüler aber Legastheniker, kannst ja mal googeln was das > ist, etwas Bildung würde dir offensichtlich auch nicht schaden Es ist interessant dass du ständig insistierst, anderen hätten zu wenig Bildung. Damit kannst du dein de-facto Unwissen auch nicht ausgleichen.
Zwitter schrieb: > Und ja,A. S > Ich habe keine Ahnung, deshalb frage ich hier so unwissend;-) Dafür haust Du aber einige Behauptungen raus, die einfach falsch sind: Zwitter schrieb: > NAch MiSRA Standard ist es NICHT erlaubt, also gibt es da nichts zu > diskutieren oder Reviewen. Zwitter schrieb: > Und nein, nur weil ein guter Programmierer ´mit Pointern umzugehen > weiß, sind diese dennoch unsicher!!
A. S. schrieb: > Dafür haust Du aber einige Behauptungen raus, die einfach falsch sind: Er ist halt ein Schulbub. Und mit dem diskutiert ihr hier ob C gut oder schlecht ist? Hättet ihr mal auf mich gehört, hättet ihr euch nicht so zum Affen machen müssen.
:
Bearbeitet durch User
Zwitter schrieb: > wäre es dann für einen Anfänger nicht viel Sinnvoller gleich Misra C > konform zu lernen? Eher nicht: * Erstmal muss man die Grundlagen lernen. Direkt mit MISRA-C einzusteien wäre wohl vergleichbar mit jemand der eine natürliche Sprache nur mittelsGedichten lernt. * Ohne C zu verstehen, kann man MISRA-C auch nicht verstehen. MISRA-C besteht aus Regeln der Art "mach das in C nicht" (wenige) und vielen der Art "wenn du das in C machst, musst du dokumentieren, warum" (viele). * MISRA-C hinkt meist dem aktuellen C Standard etwas hinterher, sodass MISRA-C für neue C-Standards erst einige Jahre später verfügbar wird. * Für Standard C gibt es reichlich gute Lehrbücher. Für einen Einstieg direkt (also ohne C-Vorkenntnisse) mit MISRA-C gibt es meines Wissens nach keine.
Zwitter schrieb: > NAch MiSRA Standard ist es NICHT erlaubt, also gibt es da nichts zu > diskutieren oder Reviewen. Blödfug, Pointerarithmetik ist nicht erlaubt, Pointer natürlich schon. Wär ja schön blöd, alles immer durch die Gegend zu kopieren, und Objektorientierung gar nicht möglich. Zwitter schrieb: > Und Cyloard, nein ich bin kein Hauptschüler, selbst wenn wäre ginge dich > das so gar nichts an. > Ich bin Realschüler aber Legastheniker, kannst ja mal googeln was das > ist, etwas Bildung würde dir offensichtlich auch nicht schaden Ein bisschen Manieren würden dir nicht schaden, und weniger oft uninformiert durch die Gegend tönen. Zwitter schrieb: > IF > Schleife Und du willst anderen erklären was in MISRA steht. Alles klar.
Ich programmiere auch auf controllern, allerdings Pascal. Und lasse Pointer weg. Falls ein Algorithmus mal Pointer wollte, wird er umgeschrieben auf ohne Pointer. Also zB einen sortierterten Binaerbaum habe ich ohne Pointer. Die Anzahl Elemente ist auch beschraenkt.
Darum Assembler, das ist aber nur was für echte Hartholzprogrammierer.
Spassbremse schrieb: > Darum Assembler, das ist aber nur was für echte > Hartholzprogrammierer. eher was für hinterwäldler
Sicherheit bzw. Zuverlässigkeit erreicht man nicht durch einen einzelnen Baustein. Von daher sind die Erwartungen die einige hier an z.B. MISRA stellen völlig überzogen. Erst mit einer Kette von Bausteinen bekommt man ein sicheres Gesamtsystem, wobei natürlich die Zahl der 9en (99%, 99.9%, ...) diktiert wie viele und welche von diesen Bausteinen man einsetzen muss. Als da wären z.B. Coding Guidelines, Code Reviews, Design Reviews, Code Tests, Hardware Tests, Redundanz, parallele Implementierung von verschiedenen Firmen, Ausfallanalysen, ... Und egal wie man programmiert - am Ende hat man immer Zeiger. Vielleicht versteckt sie die Programmiersprache vor einem, vielleicht bedient man sich eines Kniffs wie z.B. [] in C, aber der Zeiger ist da, 99% aller System funktionieren nur mit Zeigern in irgendeiner Form. Wer meint komplexe Systeme ohne Zeiger programmieren zu können macht Murks.
Nun, ja. Wenn man mit arrays arbeitet ist der Index etwas wie ein pointer, aber er stimmt schon in der Granularitaet. Dann muss nur der Index noch im erlaubten Bereich der Elemente sein. Ein Index++ ist eben nicht dasselbe wie ein Pointer++ Was dahinter ist, kann dir ja eigenlich egal sein.
Viel entscheidender ist, dass Arrayzugriffe in C keine "Arrayzugriffe" sind, sondern simple Pointerarithmetik. Es gibt keine Prüfung auf Indexverletzungen und damit hast du einfach ein Einfallstor für Fehler. Aber schon mit C++ ließe sich das sauber umgehen, zumindest dahingehend, dass ein ordentlicher Laufzeitfehler generiert wird. (So wie in allen anderen Programmiersprachen auch.) Indexüberprüfungen zur Compilezeit kann AFAIK keine Programmiersprache.
Btw. "index++" ist schon das gleiche wie "pointer++" - sofern der Pointer vom gleichen Typ ist wie das Array, auf das sich der Index bezieht. Es ist sogar exakt das gleiche - und da liegt das Problem.
Pandur S. schrieb: > Nun, ja. Wenn man mit arrays arbeitet ist der Index etwas wie ein > pointer, In c und C++ gibt es die garantierte Äquivalenz zwischen Indexiertem und Pointer Zugriff. Damit ist es völlig gleichwertig, welche der drei Zugriffsvarianten man nutzt:
1 | int test[3] = {1,2,3}; |
2 | int t1 = *test+2; |
3 | int t2 = test[2]; |
4 | int t3 = 2[test]; |
Wobei die 2te sich wohl intuitiv am leichtesten erfassen lässt
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Ja, aber nur ganz primitive Dinge. Schon in einem Hello-World-Programm wird der Textstring als Pointer an die printf-Funktion übergeben. > Wenn es um Zuverlässigkeit und Sicherheit geht stehen Pascal und ADA > ganz oben. Auch in Pascal und Ada gibt es Pointer Pointer gibt es in praktisch jeder Programmiersprache, nur heißen sie oft anders. An sich stellen sie auch kein Problem dar, sondern höchstens die Art und Weise wie damit umgegangen wird. In C hat man dafür von Hause aus viele Freiheiten, bspw. die Konvertierung von Integers in Pointer und Pointer-Arithmetik. Diese Dinge werden in der hardwarenahen Programmierung benötigt und können bei Unachtsamkeit gefährlich werden. Ur-Pascal war in solchen Dingen recht restriktiv, aber seit Turbo-Pascal bieten alle verbreiteten Pascal-Dialekte diesbezüglich praktisch dieselben Möglichkeiten (und damit auch Gefahren) wie C. In Ada muß für die hardwarenahe Programmierung ein Modul namens Interfaces.C.Pointers eingebunden werden, dann hat man auch dort alle Freiheiten wie in C. Immerhin wird durch die Auslagerung in ein externes Modul versehentlicher Unfug mit Pointern in normalem Anwendungscode weitgehend vermieden. Was sicherheitskritische Anwendungen betrifft: - Auch diese benötigen i.Allg. direkten Hardwarezugriff. Man sollte diesen aber Kapseln und auf ganz wenige Programmteile der Anwendung beschränken oder gleich einem Betriebssystem überlassen. Diese Teile müssen logischerweise ganz besonders sorgfältig entwickelt und geprüft werden. Die Trennung von hardwarenahem und Anwendungscode lässt sich in Ada leichter kontrollieren als in C oder Pascal. - Ein Fehler in der Verwendung von Pointern kann fatal sein, jeder andere Programmierfehler aber ebenso. Insofern bringen sprachbasierte Restriktionen bzgl. der Verwendung von Pointern zwar gewisse Vorteile, sie sollten aber nicht überbewertet werden. Entsprechendes gilt für künstliche auf C aufgesetzte Regelwerke wie bspw. MISRA. Diese lassen vielleicht die Softwareverantwortlichen besser schlafen und decken auch den einen oder anderen groben Schnitzer bei der Programmierung auf, sie sollten aber keineswegs dazu verleiten zu glauben, dass die damit entwickelte Software wesentlich sicherer oder gar garantiert perfekt sicher wird. Arduino Fanboy D. schrieb: > int t1 = *test+2; Da fehlen noch zwei Klammern:
1 | int t1 = *(test+2); |
Diese holprige Schreibweise mit 4 Sonderzeichen dürfte einer der Hauptgründe dafür sein, das es die gezuckerte Variante
1 | int t1 = test[2]; |
gibt. In ähnlicher Weise werden auch bei a->b statt (*a).b zwei Sonderzeichen eingespart.
:
Bearbeitet durch Moderator
Yalu X. schrieb: > Arduino Fanboy D. schrieb: >> int t1 = *test+2; > > Da fehlen noch zwei Klammern: > > int t1 = *(test+2); Da hast du allerdings Wahr! Die Klammerung ist mir durch die Lappen gegangen.
Michael B. schrieb: > Unsere Erfahrungen mit C und C++ sagen aber, daß viele C++ > Compiler aus korrektem C++ Quellcode leider falschen > Maschinencode generieren (z.B. IBM Mainframe, uC) > während bei C zumindest der Compiler meist funktioniert > (die Zeiten, wo der fehlerhaft war, wie beispielsweise > Borland C, sind inzwischen wohl weitgehend überwunden). Sehr interessant! So einen Blödsinn habe ich noch nie gelesen
Löthans schrieb: > Sehr interessant! So einen Blödsinn habe ich noch nie gelesen Oha... Kritik mag der laberkopp gar nicht! Dann wird er ganz fix, ganz fies.
Michael B. schrieb: > Unsere Erfahrungen mit C und C++ sagen aber, daß viele C++ > Compiler aus korrektem C++ Quellcode leider falschen > Maschinencode generieren (z.B. IBM Mainframe, uC) Das ist mir jetzt etwas zu sehr verallgemeinert. Das deckt sich zumindest nicht mit meiner Erfahrung. Hast Du konkrete Beispiele? Compilerfehler hatte ich bisher eine Hand voll im Laufe der Jahre. Und die waren meist halb C halb C++. Vielmehr waren die meisten Fehler eher Fehler der Programmierer. C++ hat halt komplexere Strukturen als C und man kann sich deutlich mehr ins Knie schießen. Schönes aktuelles Beispiel von oben: Arduino Fanboy D. schrieb: > int t1 = *test+2; Yalu X. schrieb: > Da fehlen noch zwei Klammern: > int t1 = *(test+2); Un das ist genau der Grund, weshalb Pointer mit Vorsicht zu geniessen sind. (Ich spreche bewusst nicht von Verbot). Man sollte einfach wissen, welche Stolpersteine eine Sprache hat. Und hier wäre es in beiden Varianten (C / C++) passiert.
A. S. schrieb: > DoS schrieb: > >> Ein echter Katholik hat heute gar keinen Spaß, geht auch nicht ins >> Internet sondern in sich (geistig) und trauert. > > Du meinst Protestant. Katholiken gehen morgens den Kreuzweg und > nachmittags zur Karfreitagsliturgie. > Dazwischen wird gearbeitet, ausmisten, schrubben und so, alles was > keinen Krach macht (da gesetzlicher stiller Feiertag) Fuck! Dann haben meine Eltern mich gehörig Verars*ht. Und wurden wohl von ihren Eltern... Boah!
Zwitter schrieb: > Weil wie oben erwähnt, es im KFZ ABS Steuergeräten etc, schlicht nicht > erlaubt ist, Soso; du kennst also die Interna aller KFZ-Steuergeräte, um diese Aussage so in dieser Allgemeinverbindlichkeit treffen zu können? Interessant - ich wusste offenbar noch gar nicht, was in meinen eigenen Entwicklungsprojekten bis dato so vor sich ging. Muss ich mal sehen, dass ich meine ganzen alten Entwicklungsteams nochmal an einen Tisch bekomme - wir müssen nachträglich die Pointer herauspatchen, weil der Zwitter vom Mikrocontrollerforum gesagt hat, dass das verboten ist. Mal sehen, was die OEMs dazu sagen - hätten sie ja auch damals schon während ihrer Reviews merken können, dass wir hier Verbrechen begehen...
Mladen G. schrieb: > Pascal ist keine Sprache die in der Industrie eingesetzt wurde, > moeglicherweise Turbo Pascal oder Delphi Man erkläre mal den Unterschied zwischen Pascal und Turbopascal/Delphi. Pascal gab es auch von MS und da da kenne ich mindestens 1 Programm, das industriell eingesetzt wurde. Ach ja, ich habe selbst ein Programm mit Delphi geschrieben, welches weltweit industriell eingesetzt wird.
(prx) A. K. schrieb: > 1960 BASIC auf einem HP? Basic gab es auf alle Fälle auf dem HP. Ob das 1960 schon so war kann ich nicht sagen. Ich habe aber noch 1990 HP-Basic gelernt/lernen müssen. Habe selbst noch einen 216'er von HP mit etwas 300'er Peripherie im Keller stehen. Das Ding funktioniert sogar noch und ich hatte den vor nicht allzu langer Zeit sogar noch mal angeworfen, um eine uralt Diskette zu kopieren.
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? > Wenn es um Zuverlässigkeit und Sicherheit geht stehen Pascal und ADA > ganz oben. > Heutzutage wird aber viel in C geschrieben, nur "geht" C überhaupt ohne > Pointer? > Ist C nicht sehr Pointerlastig? > Wird in der Kernelentwicklung auf Pointer gesetzt? Von welchem Kernel redest Du? Dem Linux-Kernel? Da gibts Pointer. Na klar kann man C auch ohne Pointer programmieren, aber warum sollte man. Ist ungefähr so: Kann man ein Auto (keine Automatikschaltung), auch ohne die Gangschaltung zu benutzen, fahren? Klar geht, aber warum um alles in der Welt? Pointer in C sind effizient und schnell, und auch kein Hexenwerk. Solange man versteht was man und Pointer so tun, gibt es keine Probleme. Um die Wahrscheinlichkeit von Programmierfehlern zu verringern, gibt es mehrere Ansätze. Einer davon sind die MISRA-C regeln. Diese mehr-oder-weniger Sinnvollen Regeln sollen helfen (nicht abschließend): - Code-Verhalten nicht von zufälligen Implementierungen des Compilers abhängig zu machen (der C-Standard ist leider nicht immer eindeutig) - typische potentielle Programmierfehler (z.b. if (a=b)) auszuschließen, entweder MISA-C Regel für diese Zeile ausschalten oder auf 2 Zeilen aufteilen. - Sich Gedanken zu machen (z.B. if - else if ==> dann muß es auch ein abschließendes else geben) und zu dokumentieren .... Andere Möglichkeiten sind z.B. unit-tests. Aber mit oder ohne Pointer: Man darf bei Software per Definition nie davon ausgehen, daß diese Fehlerfrei ist. Man muß immer in 2 Richtungen arbeiten: Fehlervermeidung (z.B. Programmieren nach MISRA-C) und Fehlerbeherrschung (ein einzelner Programmierfehler darf nicht zum Flugzeugabsturz führen). Gruß Robert
Zwitter schrieb: > Leider konnte mir diese Person (die eben diese Vorgaben macht) nicht > sagen, wie die Programmiersprache heißt, mit der KFZ ABS etc. > Steuergeräte programmiert werden. Schaust du in einschägige Normen. Dort steht, welche Sprachen 'empfohlen' werden und welche nicht. Zwitter schrieb: > "geht" C überhaupt ohne > Pointer? Beim Funktionsaufruf mit Parameter 'call by reference' wird s eng. Sonst kein Problem solange man bei arrays eine Feldüberschreitung abfängt. Ach ja, für die Schreihälse: ein C array ist kein pointer. ;-)
Der Robs schrieb: > (ein einzelner Programmierfehler darf nicht zum > Flugzeugabsturz führen) Du kennst die Geschichte zum MAX Flieger?
Syntaktisch mag ein C Array kein Pointer sein, aber in allen weiteren belangen ist es das gleiche. Der Compiler erzeugt auch den gleichen Code daraus. Gegenbeweis: Formuliere ein Beispiel, das als Array funktioniert, aber nicht als Pointer. Beispiele die aufzeigen, dass es identisch ist, gibt's oben schon. Nur mal so zum Ausholen ein Beispiel wo ein Array kein Pointer ist: In Java darf die JVM Arrays so anlegen wie sie möchte. Sie müssen weder am Stück im Speicher liegen, noch überhaupt vollständig allokiert sein.
In Java würde ein ungültiger Arrayindex aber auch sofort zu einer OutOfBoundsException führen und nicht irgendeinen Mist liefern, den man fälschlicherweise für valide halten könnte.
Klaus H. schrieb: > Das ist mir jetzt etwas zu sehr verallgemeinert. Das deckt sich > zumindest nicht mit meiner Erfahrung. > Hast Du konkrete Beispiele? Sicher, aber die Darlegung was der Compiler wann falsch macht, ist schon zusammengerafft meistens ein ganzes Heft, mit Source code, disassemblierten Maschinencode, Beispielzahlen und Beispielergebnissen. Da glauben Schleifen daß Register erhalten blieben, oder floating point units daß sie Werte schon in andere Register übertragen hatten, früher gab es z.B. bugs in malloc/free von Borland, heute eher Bugs bei der expansion von templates. Ich rede eher nicht über Pointer-aliasing-Probleme, die sind ja oft mit Compileroptionen herbeigeredet. > Compilerfehler hatte ich bisher eine Hand voll im Laufe der Jahre. Und > die waren meist halb C halb C++. So ist es auch bei mir. Früher C (als es noch kein C++ gab), inzwischen C++. Da wir auf 10 Plattformen compilieren, fallen solche Fehler auf. Dann wird analysiert, der Sprachstandard genau durchgeguckt, und manchmal ist halt einfach ein Fehler im Compiler. IBM z.B. 'xc' freut sich über eine gute Fehleranalyse und liefert dann auch in einiger Zeit einen gefixten Compiler. Microsoft (Template-Problem) kümmerte sich eher weniger. Löthans schrieb: > Sehr interessant! So einen Blödsinn habe ich noch nie gelesen Ja nun, wenn man keine Erfahrung hat...
Johannes schrieb: > Syntaktisch mag ein C Array kein Pointer sein, aber in allen weiteren > belangen ist es das gleiche. Der Compiler erzeugt auch den gleichen Code > daraus. Nein, tut er nicht. Sehr Compilerabhängig. pointercode ist effizienter in der regel, kürzer und schneller.
Beispiele? Normalerweise sind Pointer eher problematisch in Sachen Optimierung bzgl. Aliasing.
Bluebeer schrieb: > Johannes schrieb: >> Syntaktisch mag ein C Array kein Pointer sein, aber in allen weiteren >> belangen ist es das gleiche. Der Compiler erzeugt auch den gleichen Code >> daraus. > > Nein, tut er nicht. Sehr Compilerabhängig. pointercode ist effizienter > in der regel, kürzer und schneller. So viele Möglichkeiten hat der Compiler hier nicht. Da ein Array in C jederzeit zu einem Pointer degenerieren kann, und dann via diesem Pointer jederzeit angesprochen werden kann, muss der Compiler das Array entsprechend vorhersehbar im Speicher anlegen. Genau das ist ja der Unterschied zu anderen Hochsprachen. Und wenn das Array immer gleich im Speicher liegen muss, kann auch der Zugriff darauf nicht sehr verschieden sein, von Compiler zu Compiler.
:
Bearbeitet durch User
Johannes schrieb: > aber in allen weiteren > belangen ist es das gleiche. Was irgendein Compiler dahin optimiert, sollte keine allgemeingültige Referenz sein. :-( Wirf einen Blick darauf, wie sich die Dinge im Speicher darstellen. ;-)
IEC61508 schrieb: > Ach > ja, für die Schreihälse: ein C array ist kein pointer. ;-) Aber die Adresse des Arrays ist ein Pointer. Und der Zugriff auf ein Arrayelement enthält eine Pointerarithmetik. Also dasselbe, nur versteckt für Leute, die Angst vor Pointern haben.
Jobst Q. schrieb: > die Adresse des Arrays Ist eine Zahl, wo du das Feld im Speicher findest. Jobst Q. schrieb: > Pointer Ist ein Speicher, in dem eine Zahl steht. Diese Zahl kann z. B. eine Adresse eines arry sein. ;-)
IEC61508 schrieb: > Der Robs schrieb: >> (ein einzelner Programmierfehler darf nicht zum >> Flugzeugabsturz führen) > > Du kennst die Geschichte zum MAX Flieger? Und wo war da der Programmierfehler? Der Fisch stinkt ja wirklich vom Kopf her. Die 737MAX hatte Probleme im Systemdesign und irgendwelchen Specs. Die reine Programmierung war nach allem was ich gelesen habe Spezifikationskonform. Matthias
DoS schrieb: > Fuck! Dann haben meine Eltern mich gehörig Verars*ht. Und wurden wohl > von ihren Eltern... > Boah! Frag mal nach. Meist stellt sich dann raus, dass der Hausherr doch Protestant war oder so. Fasten (also kein Internet) gerne, aber arbeiten war völlig normal. Zumindest wenn man Eigentum hat. In einer Mietswohnung hat man vielleicht weniger bock, sich 4 Mal umzuziehen (Kreuzweg->Sonntagstaat, Arbeit->Alltagsklamotten, Liturgie->Sonntagstatt, Arbeit->Alltagsklamotten. .
Peter Panda2 schrieb: > Da es MISRA aber nun mal gibt, ist halt die Frage wie solche Sachen dann > umgesetzt werden. > Daher steht es hier zur Diskussion Jaein. Tatsache ist, daß MISRA und andere Standards existieren, und daß sie in verschiedenen Bereichen zwingend vorgeschrieben sind. Die Frage ist aber, ob diese Regularien tatsächlich das vorschreiben, was hier so gerne behauptet wird, nämlich, daß Pointer in C verboten seien. Nun, die Juristen haben da einen Standardspruch: "Ein Blick ins Gesetz vereinfacht die Rechtsfindung". Analog dazu gilt hier: "Ein Blick in die Vorgaben..." Und ach, siehe da: Pointer sind in C keineswegs verboten, sogar Pointerarithmetik ist dabei unter bestimmten Umständen erlaubt. Alle mir bekannten Standards empfehlen nämlich nur, nach Möglichkeit (!) aus Pointer zu verzichten, und Pointerarithmetik ist nur bei Arrayzugriffen und mit gleichzeitiger Überprüfung der Feldgrenzen sowie einer Prüfung auf Nullpointer erlaubt. Huch.
Sheeva P. schrieb: > Und ach, siehe da: Pointer sind in C keineswegs verboten, > sogar Pointerarithmetik ist dabei unter bestimmten Umständen erlaubt. > Alle mir bekannten Standards empfehlen nämlich nur, nach Möglichkeit (!) > aus Pointer zu verzichten, und Pointerarithmetik ist nur bei > Arrayzugriffen und mit gleichzeitiger Überprüfung der Feldgrenzen sowie > einer Prüfung auf Nullpointer erlaubt. Huch. Das wurde alles schon mehrfach hier geschrieben, nur noch nicht von Sheeva. Huch.
Arduino Fanboy D. schrieb: > Löthans schrieb: >> Sehr interessant! So einen Blödsinn habe ich noch nie gelesen > > Oha... > Kritik mag der laberkopp gar nicht! > Dann wird er ganz fix, ganz fies. Möglicherweise -- also, es ist jetzt natürlich nur so eine Vermutung -- ist sein Nickname ja nicht zufällig, sondern durchaus passend gewählt. Wobei ich mich bei seinen "Beiträgen" regelmäßig frage, was ein so fortschrittsfeindlicher Mensch in der Softwareentwicklung macht; als Peitschenmacher, Faßbinder, Wagner oder Köhler wäre er vermutlich glücklicher und nicht ständig mit modernen Zeugs kontrontiert. Zumal das seine Argumentation ja auch schon beweist, daß er mit abstrakten Dingen heillos überfordert ist: wenn Compilerbauer einen fehlerhaften Compiler liefert, dann ist natürlich nicht sein Compilerbauer daran schuld, sondern die modernen features in der Sprachspezifikation... ;-)
Cyblord -. schrieb: > Sheeva P. schrieb: >> Und ach, siehe da: Pointer sind in C keineswegs verboten, >> sogar Pointerarithmetik ist dabei unter bestimmten Umständen erlaubt. >> Alle mir bekannten Standards empfehlen nämlich nur, nach Möglichkeit (!) >> aus Pointer zu verzichten, und Pointerarithmetik ist nur bei >> Arrayzugriffen und mit gleichzeitiger Überprüfung der Feldgrenzen sowie >> einer Prüfung auf Nullpointer erlaubt. Huch. > > Das wurde alles schon mehrfach hier geschrieben, nur noch nicht von > Sheeva. Huch. Tatsächlich hat meines Wissens bisher noch niemand genauer geschrieben, wie die tatsächlichen Einschränkungen von MISRA-C in diesem Bereich aussehen -- und der Unsinn, daß Pointer in entsprechenden Regularien verboten seien, wurde ja auch bereits mehrfach behauptet. Wo ist Dein Problemchen, Junge?
Beitrag #6643014 wurde von einem Moderator gelöscht.
Sheeva P. schrieb: > Tatsächlich hat meines Wissens bisher noch niemand genauer geschrieben, > wie die tatsächlichen Einschränkungen von MISRA-C in diesem Bereich > aussehen Doch, wurde hier schon gemacht. Lesen hilft.
Beitrag #6643043 wurde von einem Moderator gelöscht.
Beitrag #6643051 wurde von einem Moderator gelöscht.
Beitrag #6643060 wurde von einem Moderator gelöscht.
Beitrag #6643061 wurde von einem Moderator gelöscht.
Beitrag #6643065 wurde von einem Moderator gelöscht.
Beitrag #6643072 wurde von einem Moderator gelöscht.
Beitrag #6643073 wurde von einem Moderator gelöscht.
Michael B. schrieb: >> Hast Du konkrete Beispiele? > > Sicher, aber die Darlegung was der Compiler wann falsch macht, ist schon > zusammengerafft meistens ein ganzes Heft, mit Source code, > disassemblierten Maschinencode, Beispielzahlen und Beispielergebnissen. > Da glauben Schleifen daß Register erhalten blieben, oder floating point > units daß sie Werte schon in andere Register übertragen hatten, früher > gab es z.B. bugs in malloc/free von Borland, heute eher Bugs bei der > expansion von templates. Ich rede eher nicht über > Pointer-aliasing-Probleme, die sind ja oft mit Compileroptionen > herbeigeredet. Eine andere als eine dermaßen unspezifische habe ich nicht erwartet. Und sie kommt dann ja auch. Die Frage war aber nicht nach Deinem "ganzen Heft", sondern nach "einem konkreten Beispiel". Tja.
Beitrag #6643354 wurde von einem Moderator gelöscht.
Zwitter schrieb: > Kann man in C ohne Pointer überhaupt programmieren? Kann man aber das macht die ganze Sache erheblich aufwendiger und unübersichtlicher, warum also sollte man das tun? Häufig verwendet man schon Pointer, ohne dass man es überhaupt merkt, Beispiel Arrays.
Neue Java Kollegen bitten mich oft Referenzen und Garbage Collector zu erklären. Da lange man zwangsläufig ganz schnell bei den Pointern, die unter der Haube dahinter stecken. Dass Pointer von gewissen Leuten verteufelt werden, kann ich nicht nachvollziehen. Ja sie sind fehlerträchtig. Aber andere Ersatz-Konstrukte sind ebenso Fehlerträchtig, wenn man sie nicht voll verstanden hat. Das Thema "Pufferüberlauf" hat nur indirekt mit Zeigern zu tun. Pufferüberläufe kann man mit Array-Indizies und Referenzen ebenso auslösen, wenn es keine speziellen Schutzmechanismen gibt. Pointer geben einem die Freiheit, auf Schutzmechanismen zu verzichten und sie zu hintergehen. Das bedeutet aber umgekehrt nicht, dass man das tun sollte. Also sind nicht die Pointer "böse", sondern das, was man daraus macht.
Beitrag #6643998 wurde von einem Moderator gelöscht.
Beitrag #6644017 wurde von einem Moderator gelöscht.
Stefan ⛄ F. schrieb: > Dass Pointer von gewissen Leuten verteufelt werden, kann ich nicht > nachvollziehen. > > Ja sie sind fehlerträchtig. Aber andere Ersatz-Konstrukte sind ebenso > Fehlerträchtig, wenn man sie nicht voll verstanden hat. Alles, was über Parameterübergabe hinausgeht ist relativ fehleranfällig. Und gerade C hat nun einmal die Eigenschaft, dass man dort andauernd wieder bei irgendwelcher Pointer-Arithmetik landet. Niemand scheint sich die Mühe zu machen, zB irgendwelche Container-Klassen abzukapseln. Da wird grundsätzlich der Pointer mit einem "size" Parameter übergeben und dann raw darauf zugegriffen. Ist natürlich "völlig ungefährlich". Stimmt auch meistens. uC Programmierung sieht naturgemäß ein wenig anders aus als der meiste Application-Code, aber bei open-source Projekten in C weiß ich immer schon: Wenn irgendwo etwas geparsed wird oder interne Strukturen gebraucht werden, ist da ein endloser Block an Pointer-gefrickel zu erwarten. Da wirft man nur einen Blick drauf und weiß Bescheid: Der Code ist derartig schlecht strukturiert, dass an tausenden Stellen verwendete Operationen wie "Interpretiere jetzt einen Pointer als einen Header mit 3 shorts zb" da auch einfach immer wieder so im Code stehen ``` char *p .... ... x = *(header*)p; p += 6; ``` Die zwei Zeilen sind zu kurz, als das jemand auf die Idee käme das in eine Funktion zu packen. Die Konsequenz von Schlampigkeit 2. Ordnung.
Al Fine schrieb: > Niemand scheint sich die Mühe zu machen, > zB irgendwelche Container-Klassen abzukapseln. Logisch, weil das in C nicht geht. In einigen anderen Sprachen hat die Möglichkeit geschaffen. Insofern ist dein "Niemand" nicht wirklich zutreffend. Bei dem von dir genannten Parser-Code geht es um Effizienz. In Assembler sähre der noch schlimmer aus. Da muss man halt durch - oder eben die Prioritäten anders legen, dann kann man seinen Code auch schöner strukturieren.
Stefan ⛄ F. schrieb: > Logisch, weil das in C nicht geht. So einfach würde ich die C-Verfechter jetzt nicht vom heißen Stuhl lassen. Naturlich geht das mit C-typischen Mitteln wie Macros.
Al Fine schrieb: > Naturlich geht das mit C-typischen Mitteln wie Macros. Du erzeugst Klassen in C mit Makros? Das will ich sehen!
Stefan ⛄ F. schrieb: > Du erzeugst Klassen in C mit Makros? Das will ich sehen! Sorry, aber "Nächster!" Lern UML an der UNI und sag dem Prof in der Abschlussprüfung "Das geht nicht in C"
Al Fine schrieb: > Sorry, aber "Nächster!" > Lern UML an der UNI und sag dem Prof in der Abschlussprüfung "Das geht > nicht in C" Ich erkenne darin keine Antwort auf meine Frage, wie man mit Makros in C Container-Klassen implementiert. Mir ist durchaus bewusst, dass man in C objektorientiert programmieren kann. Aber nicht so, dass es automatisch Zeiger-, Typ- und Überlaufsicher wird und nicht so, wie man den Begriff "Klasse" heutzutage versteht.
Stefan ⛄ F. schrieb: > Mir ist durchaus bewusst, dass man in C objektorientiert programmieren > kann. Dann verstehe ich das Problem nicht. Alleine die Zugriffe auf Elemente in eine Funktion zu kapseln, macht es unglaublich viel einfacher, dort nach Belieben Gültigkeitsprüfungen durchzuführen. Ranges lassen sich auch darstellen und ableiten. Google sagt, gibts alles: https://github.com/LeoVen/C-Macro-Collections https://github.com/P-p-H-d/mlib ....
Ach so, ja... Dass das in C eine Qual ist, ist selbstverständlich richtig. Sollte man wissen, wenn man auf plain C beharrt.
Al Fine schrieb: > macht es unglaublich viel einfacher Mag sein (sehe ich anders). Aber manchmal liegt die Priorität eben in der Effizienz des Codes. Und genau da benutzt man C. Wo Effizienz weniger wichtig ist benutze ich Java. Da hast du den ganzen Schutz-Schnickschnack automatisch und unumgänglich.
Stefan ⛄ F. schrieb: > Mag sein (sehe ich anders). Aber manchmal liegt die Priorität eben in > der Effizienz des Codes. Und genau das ist der Punkt, wo mir die Entwicklungen dieses Jahrtausends verschlafen scheinen. Wie kommst du auf jetzt auf Effizenz? Ist gekapselter Code ineffizent?
Al Fine schrieb: > Ist gekapselter Code ineffizent? Laufzeitprüfungen, heißen Laufzeitprüfungen, weil sie zur Laufzeit stattfinden müssen. Das hat natürlich, wen soll es auch wundern, Auswirkungen auf die Laufzeit.
Arduino Fanboy D. schrieb: > Laufzeitprüfungen, heißen Laufzeitprüfungen, weil sie zur Laufzeit > stattfinden müssen. Da sehe ich die Frage aber nicht beantwortet. Der Compiler kann bounds-Prüfungen statisch wegoptimieren, wenn man da p und p+1024 hat. Wenn nicht, hat man die halt nicht. Nein - wo ist der Nachteil, den pointer und den end-pointer gekapselt in einer Funktion zu haben? Warum keine Funktion, um den header da aus so einer Range zu ziehen? Ich verstehe das nicht...
Mark B. schrieb: > Zwitter schrieb: >> weiß jemand, ZUBERLÄSSIG! welche Porgammiersprache in Autos für >> sicherheitskritische Komponenten verwendet wird? > > In der Automobilbranche wird sehr oft C und C++ eingesetzt, und zwar > nach dem MISRA-Standard: > > https://de.wikipedia.org/wiki/MISRA-C Misra verbietet keine Pointer.
Beitrag #6644243 wurde von einem Moderator gelöscht.
Stefan ⛄ F. schrieb: > Al Fine schrieb: >> Niemand scheint sich die Mühe zu machen, >> zB irgendwelche Container-Klassen abzukapseln. > > Logisch, weil das in C nicht geht. In einigen anderen Sprachen hat die > Möglichkeit geschaffen. Insofern ist dein "Niemand" nicht wirklich > zutreffend. > > Bei dem von dir genannten Parser-Code geht es um Effizienz. In Assembler > sähre der noch schlimmer aus. Da muss man halt durch - oder eben die > Prioritäten anders legen, dann kann man seinen Code auch schöner > strukturieren. Erstens gibt es nicht den Einen Assembler, zweitens ist das keine Hochsprache und drittens ist das als Beispiel an dieser Stelle total unpassend. Aber Hauptsache mitgeredet...
Beitrag #6644312 wurde von einem Moderator gelöscht.
Stefan ⛄ F. schrieb: > Mag sein (sehe ich anders). Aber manchmal liegt die Priorität eben in > der Effizienz des Codes. Und genau da benutzt man C. > > Wo Effizienz weniger wichtig ist benutze ich Java. Da hast du den ganzen > Schutz-Schnickschnack automatisch und unumgänglich. Das ist jetzt nicht Dein Ernst, oder? Die hackst in C wegen der Effizienz? Falls Du mit Effizienz hier Code-Größe oder -Geschwindigkeit meinst, dann sei Dir versichert, dass Du eine Ausnahme bist. Die allermeisten verwenden C wegen einfach, verfügbar, erforscht, bekannt, eindeutig, verlässlich, breite Toolchain. Und für diese allermeisten ist es wichtiger, zuverlässig als performant zu programmieren.
Beitrag #6644332 wurde von einem Moderator gelöscht.
A. S. schrieb: > Das ist jetzt nicht Dein Ernst, oder? Die hackst in C wegen der > Effizienz? Ja sicher. In C programmiere ich kleine Mikrocontroller, z.B den ATtiny13. Was anderes läuft darauf auch nicht (außer Assembler, aber das Wort wurde mir hier ja verboten). Auf dem Arbeitsplatz programmiere ich überwiegend in Java und gar nicht in C. Eben weil da Zuverlässigkeit wichtiger ist. Das sind aber auch völlig andere Maschinen, um die es dort geht. C ist dort regelrecht verboten, weil man sich damit zu schnell ins eigene Knie schießen kann. Kann man mit Java zwar auch, aber nicht ganz so schnell.
A. S. schrieb: > Die allermeisten verwenden C wegen einfach, verfügbar, erforscht, > bekannt, eindeutig, verlässlich, breite Toolchain. Und für diese > allermeisten ist es wichtiger, zuverlässig als performant zu > programmieren. Also ohne jetzt zuviel Öl ins Feuer zu giessen: C hat knapp 200 cases of undefined behavior. Zuverlässig ist anders, vor allem wenn sich bei einem Compilerupdate das undefined behavior ändert. Ich nutze C deshalb, weil es oft nichts anderes gibt. Mir wäre eine Sprache viel lieber, die "zuverlässig" hilft, dass Programmierer weniger Fehler einbauen. Im embedded Bereich ist einfach weniger Auswahl...
Stefan ⛄ F. schrieb: > Das sind aber auch > völlig andere Maschinen, um die es dort geht. Was sind das denn für Maschinen?
IEC61508 schrieb: > Was sind das denn für Maschinen? Server für Enterprise Anwendungen. Online Shops, Payment Gateways, Geschäftsanwendungen, etc. Java macht auch auf kleineren Geräten Freude. Ich habe es zum Spaß lange Zeit mit Lego Mindstorms NXT eingesetzt. Aber auf ATtinies und geht es halt nicht.
Ok, nix gefährliches. Dann ist es auch egal, ob C, Java oder sonst was. ;-)
IEC61508 schrieb: > Ok, nix gefährliches Ja. Wenn bei meinen Programmen etwas schief läuft, kann es "nur" teuer werden.
Klaus H. schrieb: > vor allem wenn sich bei einem Compilerupdate das > undefined behavior ändert. Wer sich auf ein UB verlässt, das verhalten sich ändert, dann das Programm nicht mehr tut, was es vorher tat, der ist selber schuld. Es heißt ja gerade UB, eben weil man sich nicht darauf verlassen kann, dass es immer und überall immer das gleiche tut. Nein, das ist eine Schlampigkeit/Dummheit des Programmierers und kein Nachteil der Sprache.
Arduino Fanboy D. schrieb: > Nein, das ist eine Schlampigkeit/Dummheit des Programmierers und kein > Nachteil der Sprache. Allerdings wurden Hochsprachen erfunden, damit der Programmierer sich im Idealfall um solche Details keine Gedanken machen muss. Keine mir bekannte erfüllt diesen Anspruch vollständig. Doch C ist diesbezüglich eine der schlechtesten. C ist quasi die Ente/Käfer/Trabbi unter den Automobilen. Nicht schlecht, aber auch nicht deppensicher.
Beitrag #6644495 wurde von einem Moderator gelöscht.
Arduino Fanboy D. schrieb: > Nein, das ist eine Schlampigkeit/Dummheit des Programmierers und kein > Nachteil der Sprache. Stefan ⛄ F. schrieb: > Allerdings wurden Hochsprachen erfunden, damit der Programmierer sich im > Idealfall um solche Details keine Gedanken machen muss. Es ist sogar anders herum: Es ist ein Vorteil der Sprache, weil nur dadurch bestimmte Optimierungen möglich sind. Anders herum wurden viele Spachen danach eher nicht erfunden, um sich um undefined behaviour keine Gedanken machen zu müssen.
Arduino Fanboy D. schrieb: > Nein, das ist eine Schlampigkeit/Dummheit des Programmierers Das ist zwar richtig, aber Bugs gibt es immer und überall. Speziell diese Bugs sind besonders eklig, weil sie schwer zu entdecken sind, solange sie keine Laufzeitauswirkungen haben. Den Compiler, mit dem das in zwei Jahren auffallen wird, hast Du jetzt ja noch nicht. > und kein Nachteil der Sprache. Es wäre nur dann kein Nachteil, wenn zuverlässig zumindest eine Compiler-Warnung käme. Das geschieht zwar oft, aber beileibe nicht immer. Bei Software, wo es darauf ankommt, verwendet man daher inzwischen recht aufwendige Tools zur statischen Codeanalyse.
Stefan ⛄ F. schrieb: > Allerdings wurden Hochsprachen erfunden, damit der Programmierer sich im > Idealfall um solche Details keine Gedanken machen muss. Wenn ich das richtig verstanden habe, wurde C nicht dafür entwickelt um jedem Deppen jede Schlampigkeit durchgehen zu lassen, oder sie ihm um die Ohren zu hauen. Das war nicht das Ziel. Beides nicht. Sondern, in erste Linie, um die Portierung der Unix Kerne zu erleichtern. 1. den Assembler weitgehend zu ersetzen. 2. einfach zu portierender Kompiler 3. Performance und nochmal Performance Stefan ⛄ F. schrieb: > Allerdings wurden Hochsprachen erfunden, damit der Programmierer sich im > Idealfall um solche Details keine Gedanken machen muss. Deine Erwartungshaltung entspricht nicht den Zielen der ursprünglichen Entwickler. Deine Erwartungshaltung beruht also auf einem Irrtum, deinerseits. Natürlich kann man in C heute noch 2 Buchstaben addieren, die Wurzel daraus ziehen und das Ergebnis dann als Pointer auffassen/nutzen. OK, mittlerweile hauts dir einige Warnungen und Errors um die Ohren. Mehr an "Benutzerfreundlichkeit" kann eine solche Sprache nicht liefern, welche ohne Laufzeitprüfungen auskommen muss.
Nop schrieb: > Es wäre nur dann kein Nachteil, wenn zuverlässig zumindest eine > Compiler-Warnung käme. Das geschieht zwar oft, aber beileibe nicht > immer. Bei Software, wo es darauf ankommt, verwendet man daher > inzwischen recht aufwendige Tools zur statischen Codeanalyse. Die Warnungen kommen doch, wenn man sie einschaltet. Und diese aufwendigen Tools sind zu Spottpreisen da und zuverlässig. Im Zweifel kann man die Sprachdefinition durchsuchen, so klein und präzise ist die. Natürlich gibt es keinen Grund, Webserver oder Desktop-Anwendungen in C zu schreiben. Dafür gibt es Java, C++ etc.
A. S. schrieb: > Die Warnungen kommen doch, wenn man sie einschaltet. Oft, aber nicht 100% zuverlässig. Das ist auch gar nicht möglich. > Und diese > aufwendigen Tools sind zu Spottpreisen da und zuverlässig. Die, die zu Spottpreisen verfügbar sind, sind schon gar nicht zuverlässig. Ich rede hier nicht über Müll wie PC-Lint, das vor allem durch falsch-positive Warnungen "glänzt", und auch nicht CppCheck, das zwar empfehlenswert ist, aber bei der interprozeduralen Analyse einfach nichts bietet. > Im Zweifel > kann man die Sprachdefinition durchsuchen, so klein und präzise ist die. Die Sprachfefinition schon, aber die Codebasis nicht unbedingt.
Stefan B. schrieb: > Mark B. schrieb: >> Zwitter schrieb: >>> weiß jemand, ZUBERLÄSSIG! welche Porgammiersprache in Autos für >>> sicherheitskritische Komponenten verwendet wird? >> >> In der Automobilbranche wird sehr oft C und C++ eingesetzt, und zwar >> nach dem MISRA-Standard: >> >> https://de.wikipedia.org/wiki/MISRA-C > > Misra verbietet keine Pointer. Das hat zum Glück auch keiner behauptet. ;-) MISRA schränkt aber die Verwendung von Zeigern ein. Nach den MISRA-Regeln kann man also nicht alles mit Zeigern tun, was vom Sprachstandard her möglich wäre.
Klaus H. schrieb: > A. S. schrieb: >> Die allermeisten verwenden C wegen einfach, verfügbar, erforscht, >> bekannt, eindeutig, verlässlich, breite Toolchain. Und für diese >> allermeisten ist es wichtiger, zuverlässig als performant zu >> programmieren. > > Also ohne jetzt zuviel Öl ins Feuer zu giessen: C hat knapp 200 cases of > undefined behavior. > Zuverlässig ist anders, vor allem wenn sich bei einem Compilerupdate das > undefined behavior ändert. Das klingt, als ob du undefined behavior als etwas betrachtet, das man routinemäßig überall einsetzt. Aber es ist im Standard deshalb erwähnt, weil man es eben nicht nutzen darf.
Rolf M. schrieb: > Das klingt, als ob du undefined behavior als etwas betrachtet, das man > routinemäßig überall einsetzt. Aber es ist im Standard deshalb erwähnt, > weil man es eben nicht nutzen darf. Um Gotteswillen nein, sorry das war ein Eigentor, die Intention war eine andere. A. S. hatte geschrieben, > Die allermeisten verwenden C wegen einfach, verfügbar, erforscht, > bekannt, eindeutig, verlässlich, breite Toolchain. Und für diese > allermeisten ist es wichtiger, zuverlässig als performant zu > programmieren. Das Argument "Zuverlässig" halte ich für falsch. C nimmt man, weil es sich am embedded Markt durchgesetzt hat. Die Zuverlässigkeit kommt in C nicht aus der Sprache, sondern aus dem Zusammenspiel Programmiererfahrung und einem guten Prozess (Codeanalyse, Reviews, guten Tests). Zuverlässig im Sinne es hilft mir zuverlässig Fehler zu vermeiden (nicht zuverlässig undefiniertes Verhalten zu besitzen). Genau deshalb sind ja in vielen Branchen bestimmte C Spracheigenschaften stark eingegrenzt worden: damit das Risiko sinkt, dass dadurch Fehler eingebaut werden.
Rolf M. schrieb: > Das klingt, als ob du undefined behavior als etwas betrachtet, das man > routinemäßig überall einsetzt. Aber es ist im Standard deshalb erwähnt, > weil man es eben nicht nutzen darf. Also anders gesagt: Die behelfsmäßigen Korrekturen des Sprachstandards sind sehr umfangreich. 200 Fälle undefinierten Verhaltens finde ich nicht gerade wenig, allzumal C eine relativ übersichtliche Sprache ist. Und dann sind ja noch die ganzen Fälle, die zwar definiert sind, die man aber trotzdem unterlassen sollte. Man muß C zugute halten daß es noch aus einer Zeit stammt in der viele Dinge, die heute möglich und selbstverständlich sind, damals einfach noch nicht gab oder einfach nur von nachrangiger Wichtigkeit waren. An einigen Punkten fehlte damals auch einfach die Erfahrung, die man heute hat. Das ist aber kein Grund, die Unzulänglichkeiten von C nicht als solche zu benennen. Oder diese gar als Vorteil umzudeuten, das ist einfach zu lächerlich.
Wühlhase schrieb: > Man muß C zugute halten daß es noch aus einer Zeit stammt in der viele > Dinge, die heute möglich und selbstverständlich sind, damals einfach > noch nicht gab oder einfach nur von nachrangiger Wichtigkeit waren. An > einigen Punkten fehlte damals auch einfach die Erfahrung, die man heute > hat. Ja, das sehe ich auch so. Auf den PC bezogen musste man bis zum 386er noch um jeden Takt feilschen, wenn man flüssige Grafik oder gar Sound erzeugen wollte. Heute merkst du den Unterschied kaum noch, wenn ein Programm zugunsten der Sicherheit doppelt so viele Takte braucht. Weil die Gesamt-Performance eines PC eher durch die Leistung der Peripherie um die CPU herum dominiert wird. Heite können wir uns eine Menge Sicherheit und Komfort leisten - weswegen hardwarenahe Programmiersprachen wie C in diesem Bereich allmählich obsolet werden. Auf Mikrocontrollern sieht die Welt noch anders aus. Aber auch dort machen sich sogar schon Scriptsprachen breit.
Wühlhase schrieb: > > Also anders gesagt: Die behelfsmäßigen Korrekturen des Sprachstandards > sind sehr umfangreich. 200 Fälle undefinierten Verhaltens finde ich > nicht gerade wenig, allzumal C eine relativ übersichtliche Sprache ist. > Und dann sind ja noch die ganzen Fälle, die zwar definiert sind, die man > aber trotzdem unterlassen sollte. Die meisten dieser ca 200 Fälle beziehen sich auf die Standardbibliothek. Wer die gesamte Liste sehen will: Anhang J.2 im C-Standard. > > Das ist aber kein Grund, die Unzulänglichkeiten von C nicht als solche > zu benennen. Oder diese gar als Vorteil umzudeuten, das ist einfach zu > lächerlich. Wo es auf Effizienz ankommt, kann es durchaus ein Vorteil sein, dass es undefiniertes Verhalten ist, einen Nullzeiger zu dereferenzieren. Das zur Laufzeit abzufangen, wäre teuer. Und wenn es abgefangen würde, was wäre dann ein sinnvolles zu definierendes Verhalten? Den Wert 0 zurückzuliefern? Dann hätte man an dieser Stelle zwar kein undefiniertes Verhalten mehr, aber meist immer noch einen Bug in dem Programm, das das macht. dann hätte man zwar weniger undefiniertes Verhalten, aber wohl sogar mehr Bugs in Programmen, da ein Bug der heute, dank undefiniertem Verhalten auf vielen Systemem als SIGSEGV gut sichtbar ist plötzlich schwieriger zu debuggen wäre, da er erst an anderer Stelle im Programm Auswirkungen durch einen falschen Wert hätte.
Philipp Klaus K. schrieb: >> Das ist aber kein Grund, die Unzulänglichkeiten von C nicht als solche >> zu benennen. Oder diese gar als Vorteil umzudeuten, das ist einfach zu >> lächerlich. > > Wo es auf Effizienz ankommt, kann es durchaus ein Vorteil sein, dass es > undefiniertes Verhalten ist, einen Nullzeiger zu dereferenzieren. Das > zur Laufzeit abzufangen, wäre teuer. Und wenn es abgefangen würde, was > wäre dann ein sinnvolles zu definierendes Verhalten? Den Wert 0 > zurückzuliefern? Dann hätte man an dieser Stelle zwar kein undefiniertes > Verhalten mehr, aber meist immer noch einen Bug in dem Programm, das das > macht. Das ist der Punkt. In Java würde z.B. bei einem Zugriff "out of bounds" eine Exception ausgelöst. Die muss man auch erst mal abfangen und dann etwas sinnvolles damit machen. Und wie kann man sinnvoll reagieren, wenn man weiß, dass jetzt Daten durch einen gerade aufgetretenen Programmfehler kaputt sind? Was bleibt, ist nur, dass das Programm definiert abstürzt, statt ggf. irgendwas zu tun. Übrigens verbietet C solche Prüfungen nicht. Ein Compiler darf die durchaus auch einbauen, und es gibt Compiler, die so etwas auch können. Es wird nur von C nicht gefordert.
:
Bearbeitet durch User
Ehrlich gesagt sehe ich keinen Zusammenhang zwischen wohldefiniertem Verhalten und Effizienz (was immer Effizienz auch bedeuten mag). Und wie gesagt: auf den meisten Prozessoren, die heute so eingesetzt werden, kann man sich zumindest gelegentliche teure Operationen leisten. Sicher nicht auf einem ATtiny, aber die meisten STM32 langweilen sich doch die meiste Zeit, wenn sie nicht gerade mit der HAL programmiert wurden. Aber vor noch nicht allzulanger Zeit war das eben anders. Und einen Nullpointer als Rückgabewert für eine fehlgeschlagene Operation wie z.B. abgelehnte Speicherallokation (das meintest du doch?) würde ich nicht als undefiniertes Verhalten bezeichnen. Denn was man damit tun soll, ist klar: nämlich nichts.
Klaus H. schrieb: > Das Argument "Zuverlässig" halte ich für falsch. Mit Zuverlässig meinte ich hier: es ist abzusehen, was passiert. Kaum ein C-compiler hat nennenswerte unbekannte Fehler. Oder fehlende Sprachfeatures. Bei UB habe ich ggf. Müll, aber es ist spezifiziert, was UB ist. Kein GB-Collector, der macht, wann er will. Es ging im Beispiel ja darum, beim Parsen von Daten den Code zu strukturieren. Und Stefan erweckte den Eindruck, dass C vor allem wegen der Performance verwendet wir. Stefan ⛄ F. schrieb: > Aber manchmal liegt die Priorität eben in der Effizienz des Codes. Und > genau da benutzt man C. > Wo Effizienz weniger wichtig ist benutze ich Java. Und das ist m. E. Falsch. C benutzt man auch dort, wo Zuverlässigkeit (Safety, Verfügbarkeit, Echtzeit) wichtig ist.
Wühlhase schrieb: > Und einen Nullpointer als Rückgabewert für eine fehlgeschlagene > Operation wie z.B. abgelehnte Speicherallokation (das meintest du doch?) > würde ich nicht als undefiniertes Verhalten bezeichnen. Denn was man > damit tun soll, ist klar: nämlich nichts. Das es den Nullzeiger als Rückgabewert gibt ist noch nicht das undefinierte Verhalten. Das undefinierte Verhalten tritt auf, sobald ich diesen Zeiger dereferenziere, also so etwas:
1 | char *p = malloc(50000); |
2 | if(!p) |
3 | *p = 'a'; // Undefiniertes Verhalten, wenn diese Zeile erreicht wird |
Undefiniertes Verhalten heißt dann, dass die C-Implementierung machen darf, was sie will. Und Implementierungen nutzen diese Freiheit: Auf einem kleinen µC, wo sich so etwas nicht effizient abfangen lässt, wird halt versucht etwas an die physische Adresse 0 zuschreiben, was oft nichts bewirkt, oder in eine dort befindliches I/O-Register schreibt. Auf einem PC wird das Programm per Segfault beendet. Natürlich ist klar, dass du diesen Nullzeiger nicht dereferenzieren sollst. Aber wenn du es doch tust ist es eben einer diese ca 200 Fälle von undefiniertem Verhalten.
Rolf M. schrieb: > Übrigens verbietet C solche Prüfungen nicht. Ein Compiler darf die > durchaus auch einbauen, und es gibt Compiler, die so etwas auch können. > Es wird nur von C nicht gefordert. Tatsächlich ist die Möglichkeit, Fehler zu erkennen auch eine Motivation für undefiniertes Verhalten: In C ist der Zugriff auf uninitalisierte lokale Variablen undefiniertes Verhalten. Eine naheliegende Alternative wäre, diese implizit mit 0 zu initialisieren. Das würde Performanz kosten, aber es gibt noch ein weiteres Argument dagegen: In vielen Fällen wäre eine Initalisierung mit 0 nicht das, was der Programmierer wollte, man hätte also auch bei definiertem Verhalten einen Bug. Aber mit undefiniertem Verhalten kann die Implementierung einen Fehler melden (zur Compilezeit, oder z.B. mit valgrind auch zur Laufzeit), was sie nicht dürfte, wenn das Verhalten in diesem Fall definiert wäre.
Rolf M. schrieb: > Was bleibt, ist nur, dass das Programm definiert abstürzt, statt ggf. > irgendwas zu tun. Web Server sollen dabei nicht abstürzen, sondern einen Error 500 zurück liefern. Die anderen User, die von dem Fehler nicht betroffen sind, wollen schließlich weiter arbeiten. Und der eine betroffene User will vielleicht auch mit anderen Datensätzen weiter arbeiten. Deswegen macht dort eine Exception, die man fangen muss durchaus Sinn. Rolf M. schrieb: > Übrigens verbietet C solche Prüfungen nicht. Ein Compiler darf die > durchaus auch einbauen, und es gibt Compiler, die so etwas auch können. > Es wird nur von C nicht gefordert. Ja, guter Hinweis.
Da der Thread vom ursprünglichen Thema nun zu undefiniertem Verhalten abschweifte, könnte etwas Hintergrund nützlich sein. Es gibt mehrere Motivationen für undefiniertes Verhalten in C. Die beiden wichtigsten sind: 1) Sachen, die eigentlich Fehler sind, aber sich im Allgemeinen zur Compilezeit nicht erkennen lassen (meist weil die Frage, ob sie auftreten, äquivalent zum Halteproblem ist). Hier soll eine für manche Implementierungen (of µC) teure Fehlererkennung zur Laufzeit nicht vorgeschrieben werden, um die Effizienz nicht zu beinträchtigen. Das undefinierte Verhalten erlaubt Implementierungen natürlich zu versuchen, manchmal Fehler zur Compilezeit oder zur Laufzeit zu melden. 2) Sachen, bei denen es unübersichtlich viele sinnvolle Möglichkeiten gibt, sie umzusetzen. Hier soll die Freiheit der Implementierungen nicht eingeschänkt werden. Meist geht es hier um die Interaktion des C-Programms mit seiner Umgebung, z.B. Bibliotheksfunktionen, deren Funktionssignatur standardisiert ist, die aber etwas systemspezifisches machen. P.S.: Zu 1) Wenn diese leicht zu erkennen wären, würde man sie statt dessen zu Constraint Violations (Fehler zur Compilezeit) machen. Zu 2) Wenn es nur eine überschaubare Anzahl sinnvoller Möglichkeiten gäbe würde man hier stattdessen implementierungsabhängiges Verhalten verwenden (und in den Standard eine Liste dieser Möglichkeiten aufnehmen, während die Implementierung dokumentieren müsste, welche davon sie umsetzt).
:
Bearbeitet durch User
Philipp Klaus K. schrieb: > Tatsächlich ist die Möglichkeit, Fehler zu erkennen auch eine Motivation > für undefiniertes Verhalten: Nein. Die Motivation ist, daß lokale Variablen nicht automatisch initialisiert werden, weil das Laufzeit-Performance kosten würde, sobald der automatische Initialisierungswert (z.B. 0) nicht das ist, was gebraucht wird. > In C ist der Zugriff auf uninitalisierte > lokale Variablen undefiniertes Verhalten. Ob man dann einen Fehler erkennt oder nicht, hängt davon ab, was im Speicher gerade steht. Zuverlässig ist das nicht. Wäre die Erkennung eines Fehlers die Motivation, dann würde wie in Rust der Compiler gleich das Programm abweisen, und das wäre im Sprachstandard auch so definiert.
Nop schrieb: > Die Motivation ist, daß lokale Variablen nicht automatisch > initialisiert werden, weil das Laufzeit-Performance kosten würde, Ein Relikt aus grauer Vorzeit. Dass es so gut wie keinen Laufzeitzuwachs gibt, ist heute klar. Das beweisen entsprechende Compileroptionen, die automatics immer initialisieren und somit diese UB entfernen. Und das beweisen auch moderne Sprachen wie Rust. Heute sollte man eher den Ansatz verfolgen in Sprachen per default alles safe zu designen und dann für rare Ausnahmefälle Ausnahmen schaffen (vgl. unsafe-Rust).
Philipp Klaus K. schrieb: > 1) Sachen, die eigentlich Fehler sind, aber sich im Allgemeinen zur > Compilezeit nicht erkennen lassen (meist weil die Frage, ob sie > auftreten, äquivalent zum Halteproblem ist). Hier soll eine für manche > Implementierungen (of µC) teure Fehlererkennung zur Laufzeit nicht > vorgeschrieben werden, um die Effizienz nicht zu beinträchtigen. Das > undefinierte Verhalten erlaubt Implementierungen natürlich zu versuchen, > manchmal Fehler zur Compilezeit oder zur Laufzeit zu melden. > > 2) Sachen, bei denen es unübersichtlich viele sinnvolle Möglichkeiten > gibt, sie umzusetzen. Hier soll die Freiheit der Implementierungen nicht > eingeschänkt werden. Das ist dann aber eher unspecified oder implementation-defined. Undefined heißt, dass irgendwas passieren darf und ab dieser Stelle im Programm keinerlei Regeln mehr gelten. In diesen Fällen will man aber eher schon ein zwar nicht vom Standard festgezurrtes, aber doch deterministisches und in irgendeiner Form sinnvolles Verhalten. > Meist geht es hier um die Interaktion des C-Programms mit seiner Umgebung, > z.B. Bibliotheksfunktionen, deren Funktionssignatur standardisiert ist, die > aber etwas systemspezifisches machen. Sie sollen sich systemspezifisch verhalten, aber nicht undefiniert. > Zu 2) Wenn es nur eine überschaubare Anzahl sinnvoller Möglichkeiten > gäbe würde man hier stattdessen implementierungsabhängiges Verhalten > verwenden (und in den Standard eine Liste dieser Möglichkeiten > aufnehmen, während die Implementierung dokumentieren müsste, welche > davon sie umsetzt). implementation-defined heißt nicht zwangsläufig, dass eine endliche Liste aller Möglichkeiten aufgezählt wurde. Es heißt nur, dass der Compiler ein Verhalten wählen und dokumentieren muss.
MaWin schrieb: > Heute sollte man eher den Ansatz verfolgen in Sprachen per default alles > safe zu designen und dann für rare Ausnahmefälle Ausnahmen schaffen So ist es ja auch. C stammt aus einem anderen Jahrhundert.
MaWin schrieb: > Nop schrieb: >> Die Motivation ist, daß lokale Variablen nicht automatisch >> initialisiert werden, weil das Laufzeit-Performance kosten würde, > > Ein Relikt aus grauer Vorzeit. > Dass es so gut wie keinen Laufzeitzuwachs gibt, ist heute klar. > Das beweisen entsprechende Compileroptionen, die automatics immer > initialisieren und somit diese UB entfernen. Du meinst … durch definiertes, aber immer noch ggf. fehlerhaftes Verhalten zu ersetzen. Mir ist es da lieber, wenn ich durch eine Meldung darauf aufmerksam gemacht werde, wenn eine Variable genutzt wird, die noch gar keinen Wert bekommen hat, als wenn sie einfach mit einem zwar definierten, aber ggf. nicht gewünschten Wert vorbelegt wird. gcc warnt mich in den meisten Fällen bei so etwas. > Heute sollte man eher den Ansatz verfolgen in Sprachen per default alles > safe zu designen und dann für rare Ausnahmefälle Ausnahmen schaffen > (vgl. unsafe-Rust). safe ist für mich, wenn der Compiler keine Annahmen darüber trifft, was ich vielleicht wollen könnte.
MaWin schrieb: > Ein Relikt aus grauer Vorzeit. > Dass es so gut wie keinen Laufzeitzuwachs gibt, ist heute klar. Das kommt auf das Programm an. > Und das beweisen auch moderne Sprachen wie Rust. Nein. Rust initialisiert lokale Variablen nicht automatisch, sondern weist den uninitialisierten Zugriff stattdessen mit einem Compile-Fehler ab. Das kostet ebenfalls keine Laufzeit, erspart einem die längliche Fehlersuche, und man findet solche Fehler obendrein zuverlässig und nicht nur, wenn im Speicher zufällig Werte stehen, die zu erkennbar fehlerhaftem Programmablauf führen.
Rolf M. schrieb: > als wenn sie einfach mit einem zwar > definierten, aber ggf. nicht gewünschten Wert vorbelegt wird. Richtig. Deshalb bricht der Compiler von Rust ja auch ab, wenn kein expliziter default-Wert gefordert wird. > safe ist für mich, wenn der Compiler keine Annahmen darüber trifft, was > ich vielleicht wollen könnte. Meine volle Zustimmung. Deshalb ist C mit UB Mist. UB ist die Annahme, dass man ebenjenes UB-Konstrukt nicht verwendet.
Rolf M. schrieb: > Undefined heißt, dass irgendwas passieren darf und ab dieser Stelle im > Programm keinerlei Regeln mehr gelten. Nicht erst ab dieser Stelle, sondern das komplette Programm als Ganzes. Das macht die Fehlersuche ja mitunter so "lustig".
Nop schrieb: > Das kommt auf das Programm an. Genau. Und deshalb sollte man solche Programme, wo es darauf ankommt, zum Sonderfall machen. >> Und das beweisen auch moderne Sprachen wie Rust. > > Nein. Rust initialisiert lokale Variablen nicht automatisch Ja. Aber Rust zwingt den Programmierer zur Initialisierung, auch wenn das mal nicht nötig sein sollte. z. B. bei Arrays, die im weiteren Programmverlauf dann wieder überschrieben werden, bevor sie gelesen werden.
MaWin schrieb: > z. B. bei Arrays, die im weiteren > Programmverlauf dann wieder überschrieben werden, bevor sie gelesen > werden. Da braucht es keine Initialisierung, weil das "zweite" Beschreiben eine Initialisierung ist. Das gilt aber natürlich nur dann, wenn nicht nur teilweise beschrieben wird.
Nop schrieb: > Das gilt aber natürlich nur dann, wenn nicht nur > teilweise beschrieben wird. Ja eben. Das war mein Beispiel.
MaWin schrieb: > Ja eben. Das war mein Beispiel. Wenn die Überschreiblänge sich erst zur Laufzeit ergibt, aber das Lesen auch auf dem Rest passieren könnte, würde man auch in C ein memset reinsetzen. Der Vorteil von Rust ist dabei nicht automatische Initialisierung, sondern daß der Compiler statt einer je nach Compiler optional zuschaltbaren Warnung gleich einen Fehler wirft, was in der Tat das sinnvollere Verhalten ist.
MaWin schrieb: > Nop schrieb: >> aber das Lesen auch auf dem Rest passieren könnte > > Das habe ich nicht geschrieben. Sonst ist es aber auch kein undefiniertes Verhalten - und auch in Rust darfst Du uninitialisierte Variablen gleich im Dutzend haben. Du darfst nur nicht lesend darauf zugreifen.
Hier mal eine Anekdote zu implementation defined und Pointern. Zudem zeigt es, dass ein Zeiger auf char eben nicht das gleiche ist wie ein char Array. Die jeweiligen Zufgriffsmöglichkeiten sind äquivalent, aber nicht die Definitionen. Gegeben AVR-Gcc gnu++17
1 | char *test1 = "ein x"; // bedenklich |
2 | char test2[] = "ein x"; // alles gut |
Später dann Manipulationen:
1 | *(test1+4) = 'u'; // bedenklich |
2 | *(test2+4) = 'u'; // alles gut |
3 | |
4 | test1[3] = '-'; // bedenklich |
5 | test2[3] = '-'; // alles gut |
Alles funktioniert, wie es soll!
Schaut man aber in die Spezifikation, dann ist
> char *test1 = "ein x";
Implementation Defined.
Das heißt, dass spätere Manipulationen des Strings nicht so
funktionieren müssen, wie man das erwartet.
Klarer test1[3] = '-' kann auch auf anderen Systemen funktionieren, muss
aber nicht, könnte z.B. auch eine Speicherschutzverletzung werfen....
Aktiviert man alle Warnungen, sagt einem der Gcc
1 | E:\Programme\arduino\portable\sketchbook\sketch_apr05a\sketch_apr05a.ino:11:15: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings] |
2 | 11 | char *test1 = "ein x"; |
3 | |
|
Das sollte einen schon stutzig machen! Richtig ist es so:
1 | const char *test1 = "ein x"; |
Ein Zeiger auf einen konstanten String Oder gar so:
1 | const char *const test1 = "ein x"; |
Ein konstanter Zeiger auf einen konstanten String Es ist also durchaus möglich auf das "implemtation defined" angemessen zu reagieren. Nötig ist dafür: Etwas Wissen um die Sprache. Etwas Disziplin und Sorgfalt.
Stefan ⛄ F. schrieb: > Du hast gerade C mit C++ verwechselt. Nein, das habe ich nicht. Sogar knallhart extra dabei geschrieben. Arduino Fanboy D. schrieb: > Gegeben AVR-Gcc gnu++17 Zudem verhält sich der Gcc C und der C++ Kompiler an der Stelle identisch. (mal abgesehen von der Warnung) Aber das passt ja nicht in deine Vorstellungswelt. --------- Eigentlich ist es sogar noch schlimmer:
1 | #include <Streaming.h> // die Lib findest du selber ;-) |
2 | |
3 | void setup() |
4 | {
|
5 | Serial.begin(9600); |
6 | |
7 | char *t1 = "Beispiel"; |
8 | char *t2 = "Beispiel"; |
9 | |
10 | t2[0] = '*'; |
11 | |
12 | Serial << F("t1: ") << t1 << endl; |
13 | Serial << F("t2: ") << t2 << endl; |
14 | }
|
15 | |
16 | void loop() |
17 | {
|
18 | |
19 | }
|
Zeigt folgende Ausgabe:
1 | t1: *eispiel |
2 | t2: *eispiel |
t1 und t2 zeigen auf den gleichen Speicherbereich. Auch hier, C und C++ identisches Verhalten.
Ich wusste nicht dass wir hier Pointer von C++ diskutieren. Dachte du seist versehentlich dorthin abgedriftet.
Beitrag #6645150 wurde von einem Moderator gelöscht.
Stefan ⛄ F. schrieb: > Ich wusste nicht dass wir hier Pointer von C++ diskutieren. Ja, mit dem Denken, daran haperts manchmal....
Rolf M. schrieb: >> >> 2) Sachen, bei denen es unübersichtlich viele sinnvolle Möglichkeiten >> gibt, sie umzusetzen. Hier soll die Freiheit der Implementierungen nicht >> eingeschänkt werden. > > Das ist dann aber eher unspecified oder implementation-defined. > Undefined heißt, dass irgendwas passieren darf und ab dieser Stelle im > Programm keinerlei Regeln mehr gelten. In diesen Fällen will man aber > eher schon ein zwar nicht vom Standard festgezurrtes, aber doch > deterministisches und in irgendeiner Form sinnvolles Verhalten. > Nicht notwendigerweise. Z.B. hat N2464 undefiniertes Verhalten für realloc mit size 0 eingeführt, weil man sah dass Implementierungen hier so sehr divergieren, dass implementierungsabhängiges Verhalten hier nicht mehr sinnvoll wäre. N2464 wurde von WG14 einstimmig angenommen (16 Ja 0 Nein 0 Enthaltung). >> Meist geht es hier um die Interaktion des C-Programms mit seiner Umgebung, >> z.B. Bibliotheksfunktionen, deren Funktionssignatur standardisiert ist, die >> aber etwas systemspezifisches machen. > > Sie sollen sich systemspezifisch verhalten, aber nicht undefiniert. Undefiniert im C-Standard, um die Definition anderen Standards, z.b. POSIX zu überlassen. Auf dem konkreten System, im Programm dann also kein undefiniertes Verhalten, aber eben einer der ca 200 Fälle undefinierten Verhaltens im C-Standard.
Arduino Fanboy D. schrieb: > Schaut man aber in die Spezifikation, dann ist >> char *test1 = "ein x"; > Implementation Defined. Ich vermute, du meinst den Standard mit "Spezifikation"? Wo genau sagt er, dass hier etwas implementation defined ist? > Das heißt, dass spätere Manipulationen des Strings nicht so > funktionieren müssen, wie man das erwartet. > Klarer test1[3] = '-' kann auch auf anderen Systemen funktionieren, muss > aber nicht, könnte z.B. auch eine Speicherschutzverletzung werfen.... Du kommst zu richtigen Ergebnis ... der Weg ist aber falsch.
Nop schrieb: > Rust initialisiert lokale Variablen nicht automatisch, sondern weist > den uninitialisierten Zugriff stattdessen mit einem Compile-Fehler ab. Ein Compiler kann das nicht (sicher) erkennen. Sonst würden Warnungen ja auch reichen.
A. S. schrieb: > Ein Compiler kann das nicht (sicher) erkennen. Ja doch, natürlich. Man muss die Sprache nur vernünftig definieren.
mh schrieb: > Ich vermute, du meinst den Standard mit "Spezifikation"? Ok, falsche Wort verwendet.... Ich meinte: Referenz! Meine Referenz sagt: > whether string literals are distinct is implementation-defined Oder auch in neuerer Fassung: > distinctness is unspecified, > and same string literal can yield different object Zudem auch noch: > Attempting to modify a string literal results in > undefined behavior: they may be stored in read-only storage > (such as .rodata) or combined with other string literals
Arduino Fanboy D. schrieb:
[...]
Keins deine Zitate aus dieser Referenz sagt, dass die Zeile ID ist.
Nop schrieb: > Philipp Klaus K. schrieb: > >> Tatsächlich ist die Möglichkeit, Fehler zu erkennen auch eine Motivation >> für undefiniertes Verhalten: > > Nein. Die Motivation ist, daß lokale Variablen nicht automatisch > initialisiert werden, weil das Laufzeit-Performance kosten würde, sobald > der automatische Initialisierungswert (z.B. 0) nicht das ist, was > gebraucht wird. Doch. Erst auf der letzten WG14-Sitzung wurde wieder einmal die Frage diskutiert, was der nächste C-Standard (hier C23) zum Lesen nicht initalisierter Variablen sagen soll. Die Diskussion findet sich im vorläufigen Protokoll N2690 in Abschnitt 5.10.
mh schrieb: > Arduino Fanboy D. schrieb: > [...] > > Keins deine Zitate aus dieser Referenz sagt, dass die Zeile ID ist. Die Zitate zeigen, dass es dem Compiler überlassen bleibt wie er mit dem String literal umgeht, wie und wo er es im Speicher anlegt.
Bei der ganzen Diskussion um das undefined Behavior sollte man sich auch vor Augen halten, dass die Liste der UBs im Standarddokument eine Besonderheit von C und C++ ist. In den Sprachdefinitionen der meisten anderen Programmiersprachen ist diese Liste entweder gar nicht vorhanden oder sehr unvollständig. Dort ist einfach alles undefined, was nicht explizit defined ist. Das ist auch völlig in Ordnung, hat halt nur den Nachteil, dass sich die UBs nicht so schön nachzählen lassen wie in C ;-) Des Weiteren gibt es für die meisten Programmiersprachen gar keine ISO-Norm. Stattdessen gibt es eine mehr oder weniger vollständige Sprachdefinition der Entwickler der Sprache und zusätzlich oft noch einen Referenzcompiler, der zu Rate gezogen werden kann, wenn etwas in der Sprachdefinition unklar ist. So macht der Referenzcompiler aus manchem undefined Behavior ein "de facto defined Behavior". C ist erst standardisiert worden, nachdem es schon eine Vielzahl von Compilern gab, die die nicht allzu präzise Sprachdefinition von K&R mit viel Ermessensspielraum umgesetzt haben. Vor diesem Hintergrund erscheint es es durchaus sinnvoll, dass einige ohnehin fragwürdige Konstrukte, die in den existierenden Compilern auf untereinander inkompatible Art und Weise implementiert waren, nicht zugunsten eines bestimmten Compilers oder einer bestimmten Prosessorarchitektur festgelegt, sondern stattdessen explizit als undefined Behavior verboten wurden.
MaWin schrieb: > Ja doch, natürlich. > Man muss die Sprache nur vernünftig definieren. Du meinst, man muss notfalls doppelt halt initialisieren, einmal fürs Programm und vorher für den Compiler? Oder wie machst Du das?
A. S. schrieb: > Du meinst, man muss notfalls doppelt halt initialisieren Richtig. In der Praxis kommt das selten genug vor, dass es keine Rolle für die Performance spielt, oder man Ausnahmen regeln kann, für die wenigen Stellen, an denen es dann doch stören würde.
MaWin schrieb: > Man muss die Sprache nur vernünftig definieren. Wenn man an C ohne Rücksicht auf die Abwärtskompatibilität beliebig herumdefinieren könnte, wäre es IMHO am sinnvollsten, Variablen nicht automatisch, sondern zwingend direkt bei ihrer Definition durch den Programmierer zu initialisieren. Dann ist es auch für den dümmsten Compiler ganz leicht zu erkennen, wenn eine Initialisierung fehlt. Doppelte Initialisierungen können dadurch vermeiden werden, dass Variablen am Ort ihrer ersten Verwendung definiert werden, was sowieso guter Stil ist. Die Fälle, wo eine doppelte Initialisierung unvermeidbar ist, dürften so selten sein, dass sie performanzmäßig kaum ins Gewicht fallen. Durch die Einführung weiterer Sprachkonstrukte könnte auch diese Fälle erschlagen.
Yalu X. schrieb: > > Die Fälle, wo eine doppelte Initialisierung unvermeidbar ist, dürften so > selten sein, dass sie performanzmäßig kaum ins Gewicht fallen. Durch die > Einführung weiterer Sprachkonstrukte könnte auch diese Fälle erschlagen. Bei einem einfachen Compiler wie SDCC sieht das anders aus:
1 | void f(void) |
2 | {
|
3 | char buffer[2048] = {0}; // Von dir vorgeschlagene erforderliche Initalisierung |
4 | init_buffer(buffer); |
5 | …
|
6 | }
|
Da bräuchte es schon einiges an Link-Time-Optimization, damit der Compiler hier die Initialisierung des Buffers herausoptimieren kann, und bei der Größe des Buffers kann es sich durchaus in der Performanz deutlich bemerkbar machen.
Arduino Fanboy D. schrieb: > mh schrieb: >> Arduino Fanboy D. schrieb: >> [...] >> >> Keins deine Zitate aus dieser Referenz sagt, dass die Zeile ID ist. > Die Zitate zeigen, dass es dem Compiler überlassen bleibt wie er mit dem > String literal umgeht, wie und wo er es im Speicher anlegt. Das ist aber nicht das, was du ursprünglich geschrieben hast. Yalu X. schrieb: > Die Fälle, wo eine doppelte Initialisierung unvermeidbar ist, dürften so > selten sein, dass sie performanzmäßig kaum ins Gewicht fallen. Das hört sich super an ... aber gibt es dazu irgendwelche handfesten Daten, um das so pauschal sagen zu können? Es ist ja nicht nur das "wie oft" sondern es ist auch das "wann" und "wieviel" relevant.
Philipp Klaus K. schrieb: > Da bräuchte es schon einiges an Link-Time-Optimization, damit der > Compiler hier die Initialisierung des Buffers herausoptimieren kann, und > bei der Größe des Buffers kann es sich durchaus in der Performanz > deutlich bemerkbar machen. Und jetzt werden die Verfechter "richtiger" Programmiersprachen wieder Workarounds für ihre Anwendungsfälle zeigen und warum man "das alles sowieso ganz anders macht". Das wirklich gute (und zuverlässige) an C ist, dass es eben nicht besser wird. Jeder weiß, was besser wäre, doch viele Köche (hier Anforderungen) verderben den Brei. Für andere Aufgaben (Web, OOP, ...) gibt es ja Alternativen.
Arduino Fanboy D. schrieb: > Schaut man aber in die Spezifikation, dann ist >> char *test1 = "ein x"; > Implementation Defined. Das string literal "ein x" ist vom Typ Array[6] aus const char, daher ruft das Schreiben dorthin undefiniertes Verhalten hervor. C++ hat hier nur aus Gründen der Kompatibilität zu alten Programmen diesen Spezialfall zugelassen - normalerweise kann man ein const nicht auf diese Weise ohne explizite Konvertierung entfernen. Bei deinem Array passiert nichts, weil es nicht const ist und nur mit dem Inhalt des Literals initialisiert wird. Das Array darf man daher überschreiben, so lange man nicht über das Ende hinaus schreibt. Philipp Klaus K. schrieb: >>> Meist geht es hier um die Interaktion des C-Programms mit seiner Umgebung, >>> z.B. Bibliotheksfunktionen, deren Funktionssignatur standardisiert ist, die >>> aber etwas systemspezifisches machen. >> >> Sie sollen sich systemspezifisch verhalten, aber nicht undefiniert. > > Undefiniert im C-Standard, um die Definition anderen Standards, z.b. > POSIX zu überlassen. Hast du mal ein konkretes Beispiel dafür, wo das so ist? Philipp Klaus K. schrieb: > Bei einem einfachen Compiler wie SDCC sieht das anders aus: > void f(void) > { > char buffer[2048] = {0}; // Von dir vorgeschlagene erforderliche > Initalisierung Hier wird aber auch nur das erste Element explizit initialisiert. Da greift die Regel, dass, wenn mindestens ein Element explizit initialisiert ist, alle anderen implizit mit 0 initialisiert werden. Damit das alles explizit ist, müsste man schon 2048 mal die 0 hinschreiben. Und wenn sich die Array-Größe ändert, jedes mal anpassen, was vor allem nervig wird, wenn die Größe sinnvollerweise irgendwo in einem Makro definiert ist.
:
Bearbeitet durch User
Rolf M. schrieb: > Philipp Klaus K. schrieb: >> Bei einem einfachen Compiler wie SDCC sieht das anders aus: >> void f(void) >> { >> char buffer[2048] = {0}; // Von dir vorgeschlagene erforderliche >> Initalisierung > > Hier wird aber auch nur das erste Element explizit initialisiert. Da > greift die Regel, dass, wenn mindestens ein Element explizit > initialisiert ist, alle anderen implizit mit 0 initialisiert werden. > Damit das alles explizit ist, müsste man schon 2048 mal die 0 > hinschreiben. Und wenn sich die Array-Größe ändert, jedes mal anpassen, > was vor allem nervig wird, wenn die Größe sinnvollerweise irgendwo in > einem Makro definiert ist. Das hat wenig mit dem zu tun, was Philipp in seinem Beitrag geschrieben hat.
Philipp Klaus K. schrieb: > Bei einem einfachen Compiler wie SDCC sieht das anders aus: > > void f(void) > { > char buffer[2048] = {0}; // Von dir vorgeschlagene erforderliche Initalisierung > init_buffer(buffer); > … > } Das wäre einer der von mir erwähnten Ausnahmefälle. Das Ungeschickte bei C ist, dass man als Initialisierungswerte nur Ergebnisse von Ausdrücken verwenden kann und Arrays keine Rückgabewerte von Funktionen sein können. Um ein Array mittels eines Funktionsaufrufs mit Werten zu belegen, wird (wie in deinem Beispiel) das Array üblicherweise als Argument an die Funktion übergeben. Man bräuchte also (wie ich oben bereits andeutete) ein weiteres Sprachkonstrukt, um Variablen auch per Funktionsaufruf mit Call-by_Reference-Übergabe des Arrays initialisieren zu können. Eine griffige Syntax fällt mir dazu auf die Schnelle nicht ein. Das folgende Beispiel mit dem neuen Schlüsselwort init_by ist ein erster, holpriger Versuch, der ausschließlich dazu dient, das Prinzip zu verdeutlichen:
1 | char buffer[1024] init_by init_buffer(buffer); |
PS: Um kein neues Schlüsselwort einführen zu müssen, könnte man init_by auch durch etwas bereits Existierendes wie bspw. "do" oder "->" ersetzen, das innerhalb einer Deklaration noch keine Bedeutung hat:
1 | char buffer[1024] do init_buffer(buffer); |
:
Bearbeitet durch Moderator
mh schrieb: > Das hat wenig mit dem zu tun, was Philipp in seinem Beitrag geschrieben > hat. Gut erkannt.
Rolf M. schrieb: >>> >>> Sie sollen sich systemspezifisch verhalten, aber nicht undefiniert. >> >> Undefiniert im C-Standard, um die Definition anderen Standards, z.b. >> POSIX zu überlassen. Das undefinierte Verhalten beim Ändern des von getenv zurückgegebenen Strings wird von POSIX genutzt, um das Verhalten zu definieren, wenn es ich um einen vorher per putenv hinzugefügten handelt. Aber in den meisten Fällen, in denen man anderen Standards wie POSIX Freiheit geben will, wird das unspezifiziertes statt über undefiniertes Verhalten gemacht.
Johannes schrieb: > Viel entscheidender ist, dass Arrayzugriffe in C keine "Arrayzugriffe" > sind, sondern simple Pointerarithmetik. Es gibt keine Prüfung auf > Indexverletzungen und damit hast du einfach ein Einfallstor für Fehler. Beim Zugriff auf das Arrayelement außerhalb der Grenzen gibt es undefiniertes Verhalten. Somit hat die C-Implementierung die Freiheit, dieses undefinierte Verhalten für eine Überprüfung zu nutzen. Wer diese Überprüfung will, kann das bei GCC und LLVM per -fsanitize=array-bounds machen. Auch für andere Fälle undefinierten Verhaltens, die sich erst zur Laufzeit erkennen lassen, bieten Implementierungen oft entsprechende Möglichkeiten.
Philipp Klaus K. schrieb: > Beim Zugriff auf das Arrayelement außerhalb der Grenzen gibt es > undefiniertes Verhalten. Somit hat die C-Implementierung die Freiheit, > dieses undefinierte Verhalten für eine Überprüfung zu nutzen. > Wer diese Überprüfung will, kann das bei GCC und LLVM per > -fsanitize=array-bounds machen. > Auch für andere Fälle undefinierten Verhaltens, die sich erst zur > Laufzeit erkennen lassen, bieten Implementierungen oft entsprechende > Möglichkeiten. Allerdings kostet die Überprüfung zur Laufzeit eben CPU Zeit und Speicher. Irgendwo müssen die Grenzen eingetragen sein, es muss ein Code laufen der diese Grenzen bei jedem Zugriff prüft. Sicherheitsgurte gibts hier nicht umsonst. Und grade im Embedded Umfeld will niemand "versteckten" Code der schnell mal 30% der Laufzeit in einer Schleife mit Arrayzugriffen ausmachen kann.
Cyblord -. schrieb: > > Allerdings kostet die Überprüfung zur Laufzeit eben CPU Zeit und > Speicher. Irgendwo müssen die Grenzen eingetragen sein, es muss ein Code > laufen der diese Grenzen bei jedem Zugriff prüft. Sicherheitsgurte gibts > hier nicht umsonst. Und grade im Embedded Umfeld will niemand > "versteckten" Code der schnell mal 30% der Laufzeit in einer Schleife > mit Arrayzugriffen ausmachen kann. Klar. Drum ist es ja praktisch, dass es hier undefiniertes Verhalten gibt, dass den Implementierungen die Freiheit gibt, die unterschiedlichen Anforderungen verschiedener Programmierer umzusetzen.
Stefan ⛄ F. schrieb: > C ist quasi die Ente/Käfer/Trabbi unter den Automobilen. Nicht schlecht, > aber auch nicht deppensicher. Wären da ein Donkervoort oder Caterham nicht passendere Autovergleiche? Ich mein, für besondere Fahrleistungen sind Enten, Trabbis und Käfer ja nun nicht gerade bekannt.
Jemand schrieb: > Wären da ein Donkervoort oder Caterham nicht passendere Autovergleiche? Die kenne ich nicht. Wenn ich mir die Fotos bei Google anschaue: nein, so cool hat C noch nie ausgesehen. Obwohl - die offene Karosserie passt. > Ich mein, für besondere Fahrleistungen sind Enten, > Trabbis und Käfer ja nun nicht gerade bekannt. C läuft auf prima mit kleinem "Motor". Ich musste mal Java auf einem 386er nutzen, das war super ätzend.
Stefan ⛄ F. schrieb: > Jemand schrieb: >> Wären da ein Donkervoort oder Caterham nicht passendere Autovergleiche? > > Die kenne ich nicht. Wenn ich mir die Fotos bei Google anschaue: nein, > so cool hat C noch nie ausgesehen. Obwohl - die offene Karosserie passt. Paßt auch sonst vieles: keine Servolenkung, kein Bremskraftverstärker, kein ABS oder ASR, wie Donkervoort auf der alten Seite schrieb: "the driver is in charge". >> Ich mein, für besondere Fahrleistungen sind Enten, >> Trabbis und Käfer ja nun nicht gerade bekannt. > > C läuft auf prima mit kleinem "Motor". > > Ich musste mal Java auf einem 386er nutzen, das war super ätzend. Wärgs, mein herzliches Beileid!
Stefan ⛄ F. schrieb: > Ich musste mal Java auf einem 386er nutzen, das war super ätzend. Was Pointer in C sind, sind Threadpools in Java. Man muss schon wissen was man tut ... LG, Sebastian
Philipp Klaus K. schrieb: > Wer diese Überprüfung will, kann das bei GCC und LLVM per > -fsanitize=array-bounds machen. Leider funktioniert die nur, wenn direkt auf dem Array gearbeitet wird und nicht, wenn es an eine Funktion übergeben wird, wo es zum Pointer verflacht. Letzteres ist aber der häufigere Fall.
Stefan ⛄ F. schrieb: > Jemand schrieb: >> Wären da ein Donkervoort oder Caterham nicht passendere Autovergleiche? > > Die kenne ich nicht. Wenn ich mir die Fotos bei Google anschaue: nein, > so cool hat C noch nie ausgesehen. Obwohl - die offene Karosserie passt. > >> Ich mein, für besondere Fahrleistungen sind Enten, >> Trabbis und Käfer ja nun nicht gerade bekannt. > > C läuft auf prima mit kleinem "Motor". Caterham und Donkervoort sind Sohn und Stiefsohn des legendären Lotus Seven, und schon der war bekannt dafür, trotz des im Vergleich zur Konkurrenz geringeren Hubraums abzugehen wie Schmidts Katze. Wie hat er das gemacht? Durch Light-Weight. Auch das passt ganz gut zu C :)
Yalu X. schrieb: > Wie hat er das gemacht? Durch Light-Weight. > Auch das passt ganz gut zu C Ja. Man lässt alle Schnörkel und Anbauten weg, die man weglassen kann. Sheeva P. schrieb: > wie Donkervoort auf der alten Seite schrieb: "the > driver is in charge". Wer das nicht gut findet: völlig OK, es gibt andere Programmiersprachen mit anderen Zielen.
Nop schrieb: > Philipp Klaus K. schrieb: > >> Wer diese Überprüfung will, kann das bei GCC und LLVM per >> -fsanitize=array-bounds machen. > > Leider funktioniert die nur, wenn direkt auf dem Array gearbeitet wird > und nicht, wenn es an eine Funktion übergeben wird, wo es zum Pointer > verflacht. Letzteres ist aber der häufigere Fall. Auch ein Grund, warum C oder "das C in C++" nicht verwenden sollte. Auch wer keine einzige Klasse schreibt, kann doch Vorteile aus C++ ziehen. Und mit einem Funktions-template vermeidet man das "decay".
Wilhelm M. schrieb: > Auch wer keine einzige Klasse schreibt, kann doch Vorteile aus C++ > ziehen. Und mit einem Funktions-template vermeidet man das "decay". Das ist nun wirklich unsinn. In C++ nutzt man std::vector und std::array und hat das Problem mit dem decay gar nicht erst.
mh schrieb: > Wilhelm M. schrieb: >> Auch wer keine einzige Klasse schreibt, kann doch Vorteile aus C++ >> ziehen. Und mit einem Funktions-template vermeidet man das "decay". > > Das ist nun wirklich unsinn. Sehe ganz und gar nicht so. Dann hast Du mich falsch verstanden. Viele wollten kein C++ benutzen, oder können es nicht wegen unterschiedlichster Gründe. Etwa weil sie die stdlib nicht auf ihrer Plattform haben oder nutzen wollen/können. Mir ging es nur darum. > In C++ nutzt man std::vector und std::array > und hat das Problem mit dem decay gar nicht erst. s.o., wenn man das nutzen will oder kann. Natürlich ist es klar, dass man das nutzt, wenn man C++ umfänglich verwendet. Hier geht es aber um "das C im C++".
Wilhelm M. schrieb: > Und mit einem Funktions-template vermeidet man das "decay". Allerdings hat man dann für jede Array-Größe, mit der man die Funktion nutzt, eine eigene Instanz dieses Templates. Und die Größe muss zur Compilezeit feststehen.
:
Bearbeitet durch User
Rolf M. schrieb: > Allerdings hat man dann für jede Array-Größe, die im Programm vorkommt, > eine eigene Instanz dieses Templates Und das ist selbst in µC Umgebungen kein Problem, der befürchtete "bloat" ist wesentlich geringer, als man gemeinhin annimmt. Rolf M. schrieb: > Und die Größe muss zur Compilezeit > feststehen. Das stimmt: VLA sind C, aber:
1 | const size_t n{10}; |
2 | int a[n]{}; |
ist auch in C++ ok, wobei GCC "echte" VLAs als extension möglich macht.
Wilhelm M. schrieb: > Rolf M. schrieb: >> Und die Größe muss zur Compilezeit >> feststehen. > > Das stimmt: VLA sind C, aber: Seit C11 aber nur noch optional. > const size_t n{10}; > int a[n]{}; > > ist auch in C++ ok, Aber das ist trotzdem eine zur Compilezeit feststehende Größe. Einer Funktion, die einen Zeiger übergeben bekommt, ist es dagegen vollkommen egal, was das für ein Array ist. Man muss ihr natürlich die Größe mitteilen.
Rolf M. schrieb: > Aber das ist trotzdem eine zur Compilezeit feststehende Größe. Das habe ich ja auch oben unterschieden. Rolf M. schrieb: > Man muss ihr natürlich die Größe > mitteilen. Und das macht man am besten in einem 'struct'. Ja, und dann ist der Weg zu std::array<> oder etwas ähnlichem (falls man stdlib nicht benutzen will/kann) ja nicht mehr weit.
Wilhelm M. schrieb: > Auch ein Grund, warum C oder "das C in C++" nicht verwenden sollte. Es geht hier auch um C. Es ist echt anstrengend, deine C++ lobhudelei andauernd und an jeder unpassenden Ecke. Bekommst Geld dafür?
900ss schrieb: > Wilhelm M. schrieb: >> Auch ein Grund, warum C oder "das C in C++" nicht verwenden sollte. > > Es geht hier auch um C. Sehr passend: ... auch ... > Es ist echt anstrengend, deine C++ lobhudelei > andauernd und an jeder unpassenden Ecke. Bekommst Geld dafür? Das ist keine Lobhudelei, sondern einfach der Hinweis darauf, das es besser geht (in diesem Fall). Da ja oben sogar über andere Programmiersprachen und auch über hypothetische Erweiterungen von C diskutiert wird, sollte es doch möglich sein, auch den Hinweis auf cherry-picking aus C++ möglich sein, oder?
Yalu X. schrieb: > Caterham und Donkervoort sind Sohn und Stiefsohn des legendären Lotus > Seven, und schon der war bekannt dafür, trotz des im Vergleich zur > Konkurrenz geringeren Hubraums abzugehen wie Schmidts Katze. Wie hat er > das gemacht? Durch Light-Weight. Auch das passt ganz gut zu C :) Sehr richtig, Colin Chapman -- der Gründer von Lotus -- hat sehr konsequent auf Leichtbau gesetzt und wird mit Aussagen wie "Any car which holds a whole race is too heavy" und "Adding Power makes you faster on the straights. Subtracting weight makes you faster everywhere" zitiert. Leider hat das dazu geführt, daß die Formel1 einige sehr heftige Unfälle erleben mußte, die bekanntesten davon vermutlich die Crashes von Martin Donelly 1990, Jochen Rindt 1969 in Barcelona, sowie im darauf folgenden Jahr der tödliche Unfall von Jochen Rindt -- allesamt, weil wichtige Teile an ihren Fahrzeugen gebrochen waren.
Lothar schrieb: > Viele glauben das geht nur in C++ aber es geht auch in C - und ist in > der Embedded Programmierung sogar teilweise vorgeschrieben - kein > malloc() > > Hier ein Beispiel: C struct wie C++ Klasse Schönes Beispiel von dir, super! Ich geb dir recht, grundsätzlich kann man mit Stack-Kopien schön hantieren und kann möglicherweise auch eine Aufblähung in zu große Structs vermeiden (was die Sache u.U. testbarer machen kann). In Verbindung mit Designated Initializers (C99) wirds fast lesbar: struct DATA test = { .x = 1 }; Dennoch kosten Funktionsrahmen beim Aufruf potentiell mehr und die Rücksprunge ebenso durch entweder Daten-Kopiererei oder -Verschiebung. Einen richtigen Vorteil sehe ich eigentlich nicht gegenüber einem "struct DATA restrict *const" ^^
db8fs schrieb: > Dennoch kosten Funktionsrahmen beim Aufruf potentiell mehr und die > Rücksprunge ebenso durch entweder Daten-Kopiererei oder -Verschiebung. Hat jemand Erfahrung mit RVO in C? Ist das von Standard ausgeschlossen? Ich habe eben etwas getestet und konnte gcc nicht dazu bringen eine Kopie mit RVO zu vermeiden.
mh schrieb: > db8fs schrieb: >> Dennoch kosten Funktionsrahmen beim Aufruf potentiell mehr und die >> Rücksprunge ebenso durch entweder Daten-Kopiererei oder -Verschiebung. > > Hat jemand Erfahrung mit RVO in C? Ist das von Standard ausgeschlossen? > Ich habe eben etwas getestet und konnte gcc nicht dazu bringen eine > Kopie mit RVO zu vermeiden. Ok, clang hat keine Probleme RVO zu nutzen.
mh schrieb: > db8fs schrieb: >> Dennoch kosten Funktionsrahmen beim Aufruf potentiell mehr und die >> Rücksprunge ebenso durch entweder Daten-Kopiererei oder -Verschiebung. > > Hat jemand Erfahrung mit RVO in C? Ist das von Standard ausgeschlossen? Warum sollte es? In C++ muss es ja nur deshalb explizit erlaubt sein, weil das Kopieren eines Objekts "side effects" haben kann - über Konstruktor und Destruktor, und wenn die Kopie nicht gemacht wird, finden die nicht statt. Das gibt es bei C aber nicht, und daher ist es von der "as if"-Rule abgedeckt. > Ich habe eben etwas getestet und konnte gcc nicht dazu bringen eine > Kopie mit RVO zu vermeiden. Also ich hab's grad probiert, und es funktioniert bei mir mit gcc, wenn man mindestens mit -O1 optimiert.
Rolf M. schrieb: > mh schrieb: >> db8fs schrieb: >>> Dennoch kosten Funktionsrahmen beim Aufruf potentiell mehr und die >>> Rücksprunge ebenso durch entweder Daten-Kopiererei oder -Verschiebung. >> >> Hat jemand Erfahrung mit RVO in C? Ist das von Standard ausgeschlossen? > > Warum sollte es? In C++ muss es ja nur deshalb explizit erlaubt sein, > weil das Kopieren eines Objekts "side effects" haben kann - über > Konstruktor und Destruktor, und wenn die Kopie nicht gemacht wird, > finden die nicht statt. Das gibt es bei C aber nicht, und daher ist es > von der "as if"-Rule abgedeckt. Das ist mir klar. Deswegen hab ich gefragt, ob es ausgeschlossen wird. Rolf M. schrieb: >> Ich habe eben etwas getestet und konnte gcc nicht dazu bringen eine >> Kopie mit RVO zu vermeiden. > > Also ich hab's grad probiert, und es funktioniert bei mir mit gcc, wenn > man mindestens mit -O1 optimiert. Ok RVO mit nem rvalue macht gcc bei mir auch. Hast du zufällig mal NRVO also mit nem RVO mit nem lvalue getestet?
1 | struct Data { |
2 | int b[64]; |
3 | };
|
4 | struct Data make_Data(int a) { |
5 | struct Data tmp; |
6 | for(int i=0; i<64; ++i) { |
7 | tmp.b[i] = a + i; |
8 | }
|
9 | return tmp; |
10 | }
|
mh schrieb: > Ok RVO mit nem rvalue macht gcc bei mir auch. Hast du zufällig mal NRVO > also mit nem RVO mit nem lvalue getestet? Ja. Aber ich glaub, ich bin einer Optimierung aufgesessen.
Arduino Fanboy D. schrieb: > Eigentlich ist es sogar noch schlimmer:
1 | char *t1 = "Beispiel"; |
2 | char *t2 = "Beispiel"; |
3 | t2[0] = '*'; |
Das erzeugt unter Betriebssystemen wie Linux in der Regel einen Programmabbruch aka Core Dump. Dass Du es unterlässt, t2 als non-const-Pointer anzugeben, hindert den Compiler noch lange nicht daran, den konstanten String "Beispiel" in das Text-Segment zu legen. Kommt dieser String mehrfach im Code vor, optimiert der Compiler das und erzeugt nur einen String im Text-Segment. Das darf er, da Zeichenketten nicht beschreibbar sind. Und damit zeigen t1 und t2 auf denselben String. Die Zeile
1 | t2[0] = '*'; |
versucht dann, ins Text-Segment zu schreiben. Das gibt unter Linux (und auch anderen Betriebssystemen) einen harten Programm-Abbruch. Schreib einfach:
1 | char t1[] = "Beispiel"; |
2 | char t2[] = "Beispiel"; |
und Dein "Problem" löst sich in Luft auf.
:
Bearbeitet durch Moderator
Frank M. schrieb: > und Dein "Problem" löst sich in Luft auf. Das ist nicht mein Problem, und zudem erwähne ich wohl schon, dass es eine Ausnahmen werfen kann, oder sonst ein Drama generieren. Da eben die Ablage des Strings "Implementatition Defined" ist und der schreibende Zugriff dann ein "Undefined Behavior" nach sich zieht. Eigentlich diente es eher der Vorführung, dass Pointer und Arrays eben NICHT das gleiche sind. Es gibt Äquivalenzen, aber mehr auch nicht. Da schienen mir einige der Vorposter falsch abgebogen zu sein.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.