Globale Bearbeitung: Tut mir leid, Leute, ich war total begeistert und habe viel Unsinn geschrieben. Nur ein alter Knacker, der schimpft.
Ich wollte glauben, dass C verschont wurde, aber leider wurde es seit C11 mit C ++ gleichgesetzt. Um zu wissen, was der Compiler mit Nebenwirkungen in Ausdrücken tun wird, muss offenbar ein kleines mathematisches Rätsel gelöst werden, das eine teilweise Anordnung von Codesequenzen basierend auf einem "befindet sich vor dem Synchronisationspunkt von" beinhaltet.
Ich habe in den Tagen von K & R zufällig einige wichtige eingebettete Echtzeitsysteme entworfen und implementiert (einschließlich der Steuerung eines Elektroautos, das Menschen gegen die nächste Wand krachen lassen könnte, wenn der Motor nicht in Schach gehalten würde, ein 10-Tonnen-Industriemotor Roboter, der Menschen zu Brei zerquetschen könnte, wenn er nicht richtig befohlen wird, und eine Systemschicht, die, obwohl harmlos, ein paar Dutzend Prozessoren dazu bringt, ihren Datenbus mit weniger als 1% Systemaufwand trocken zu saugen).
Ich bin vielleicht zu senil oder dumm, um den Unterschied zwischen undefiniert und nicht spezifiziert zu erkennen, aber ich denke, ich habe immer noch eine ziemlich gute Vorstellung davon, was gleichzeitige Ausführung und Datenzugriff bedeuten. Meiner wohl informierten Meinung nach ist diese Besessenheit der C ++ - und jetzt C-Leute mit ihren Lieblingssprachen, die Synchronisationsprobleme übernehmen, ein kostspieliger Wunschtraum. Entweder wissen Sie, was gleichzeitige Ausführung ist, und Sie brauchen keines dieser Dinge, oder Sie tun es nicht, und Sie würden der ganzen Welt einen Gefallen tun, ohne zu versuchen, sich damit anzulegen.
All diese vielen atemberaubenden Abstraktionen von Speicherbarrieren sind einfach auf eine vorübergehende Reihe von Einschränkungen der Multi-CPU-Cache-Systeme zurückzuführen, die alle sicher in gängigen Betriebssystemsynchronisationsobjekten wie beispielsweise den Mutexen und Bedingungsvariablen C ++ eingekapselt werden können bietet an.
Die Kosten für diese Kapselung sind nur ein winziger Leistungsabfall im Vergleich zu einigen Fällen, die durch die Verwendung feinkörniger spezifischer CPU-Anweisungen erzielt werden könnten.
Das volatile
Schlüsselwort (oder a#pragma dont-mess-with-that-variable
Nach allem, was ich als Systemprogrammierer getan habe, wäre es völlig ausreichend gewesen, dem Compiler zu sagen, er solle aufhören, Speicherzugriffe neu zu ordnen. Mit direkten asm-Anweisungen kann problemlos optimaler Code erstellt werden, um Treiber- und Betriebssystemcode auf niedriger Ebene mit Ad-hoc-CPU-spezifischen Anweisungen zu versehen. Ohne genaue Kenntnis der Funktionsweise der zugrunde liegenden Hardware (Cache-System oder Busschnittstelle) müssen Sie ohnehin nutzlosen, ineffizienten oder fehlerhaften Code schreiben.
Eine winzige Anpassung des volatile
Schlüsselworts und von Bob wäre jeder gewesen, außer dem Onkel des hartgesottensten Low-Level-Programmierers. Stattdessen hatte die übliche Gruppe von C ++ - Mathematikfreaks einen großen Tag damit verbracht, eine weitere unverständliche Abstraktion zu entwerfen, die ihrer typischen Tendenz entsprach, Lösungen zu entwerfen, die nach nicht existierenden Problemen suchen und die Definition einer Programmiersprache mit den Spezifikationen eines Compilers verwechseln.
Nur dieses Mal war die Änderung erforderlich, um auch einen grundlegenden Aspekt von C zu entstellen, da diese "Barrieren" selbst in C-Code auf niedriger Ebene generiert werden mussten, um ordnungsgemäß zu funktionieren. Dies hat unter anderem die Definition von Ausdrücken ohne jegliche Erklärung oder Rechtfertigung verwüstet.
Zusammenfassend ist die Tatsache, dass ein Compiler aus diesem absurden Teil von C einen konsistenten Maschinencode erzeugen könnte, nur eine entfernte Folge der Art und Weise, wie C ++ - Leute mit möglichen Inkonsistenzen der Cache-Systeme der späten 2000er Jahre fertig wurden.
Ein grundlegender Aspekt von C (Ausdrucksdefinition) wurde schrecklich durcheinander gebracht, so dass die überwiegende Mehrheit der C-Programmierer - die sich zu Recht nicht um Cache-Systeme kümmern - nun gezwungen ist, sich auf Gurus zu verlassen, um das zu erklären Unterschied zwischen a = b() + c()
und a = b + c
.
Der Versuch zu erraten, was aus diesem unglücklichen Array werden wird, ist ohnehin ein Nettoverlust an Zeit und Mühe. Unabhängig davon, was der Compiler daraus machen wird, ist dieser Code pathologisch falsch. Die einzige verantwortliche Sache, die damit zu tun hat, ist, es in den Papierkorb zu schicken.
Konzeptionell können Nebenwirkungen immer aus Ausdrücken entfernt werden, mit dem trivialen Aufwand, die Änderung explizit vor oder nach der Bewertung in einer separaten Anweisung erfolgen zu lassen.
Diese Art von beschissenem Code könnte in den 80er Jahren gerechtfertigt gewesen sein, als man nicht erwarten konnte, dass ein Compiler irgendetwas optimiert. Aber jetzt, da Compiler längst schlauer geworden sind als die meisten Programmierer, bleibt nur noch ein Stück beschissener Code übrig.
Ich verstehe auch nicht, wie wichtig diese undefinierte / nicht näher bezeichnete Debatte ist. Entweder können Sie sich darauf verlassen, dass der Compiler Code mit einem konsistenten Verhalten generiert, oder Sie können nicht. Ob Sie das als undefiniert oder nicht spezifiziert bezeichnen, scheint ein strittiger Punkt zu sein.
Meiner wohl informierten Meinung nach ist C in seinem K & R-Zustand bereits gefährlich genug. Eine nützliche Entwicklung wäre das Hinzufügen von Sicherheitsmaßnahmen mit gesundem Menschenverstand. Wenn Sie beispielsweise dieses erweiterte Code-Analyse-Tool verwenden, zwingen die Spezifikationen den Compiler zur Implementierung, um zumindest Warnungen über Bonkers-Code zu generieren, anstatt stillschweigend einen Code zu generieren, der möglicherweise extrem unzuverlässig ist.
Stattdessen beschlossen die Jungs zum Beispiel, eine feste Auswertungsreihenfolge in C ++ 17 zu definieren. Jetzt wird jeder Software-Idiot aktiv dazu angeregt, absichtlich Nebenwirkungen in seinen Code zu setzen, und sich darauf verlassen, dass die neuen Compiler die Verschleierung auf deterministische Weise eifrig handhaben werden.
K & R war eines der wahren Wunder der Computerwelt. Für zwanzig Dollar haben Sie eine umfassende Spezifikation der Sprache (ich habe gesehen, wie einzelne Personen komplette Compiler geschrieben haben, nur mit diesem Buch), ein ausgezeichnetes Nachschlagewerk (das Inhaltsverzeichnis würde Sie normalerweise auf ein paar Seiten der Antwort auf Ihre Frage verweisen Frage) und ein Lehrbuch, in dem Sie lernen, die Sprache auf vernünftige Weise zu verwenden. Vervollständigen Sie mit Begründungen, Beispielen und weisen Worten der Warnung vor den zahlreichen Möglichkeiten, wie Sie die Sprache missbrauchen können, um sehr, sehr dumme Dinge zu tun.
Dieses Erbe für so wenig Gewinn zu zerstören, scheint mir eine grausame Verschwendung zu sein. Aber auch hier könnte ich den Punkt sehr wohl nicht vollständig verstehen. Vielleicht könnte mich eine freundliche Seele in die Richtung eines Beispiels für neuen C-Code weisen, der diese Nebenwirkungen erheblich ausnutzt?