Makro verhält sich fehlerhaft bei GNU Compiler Linux u. Windows
-
Bashar schrieb:
Auf die Gefahr hin, mich zu wiederholen: Das Problem ist, dass ein und dasselbe Objekt mehrfach zwischen zwei Sequenzpunkten modifiziert wird. Dass das in einem Makro passiert ist vollkommen unerheblich. Der Code ist falsch. Er sollte vermutlich etwa so aussehen (falls erwartet wurde, dass die Seiteneffekte der ++-Ausdrücke von links nach rechts ausgeführt werden):
lpnadc->pu_buf = (U32)(lpnadc->R3P[0] << 24) \ | (U32)(lpnadc->R3P[1] << 16) \ | (U32)(lpnadc->R3P[2] << 8) \ | (U32)(lpnadc->R3P[3] ); \ lpnadc->R3P += 4;
Fast. Der Cast-Operator wirkt im Original auf den dereferenzierten Wert, nicht erst auf das shift-Ergebnis.
-
Vielen Dank für die tollen Hinweise. Aber muss ein guter Compiler nicht meckern, wenn der Code falsch ist?
-
Kompilierst du mit -Wall und -Wextra?
-
Testhalber hatte ich erst mit -Wall kompiliert. Es gab keine Warnings. Dann habe ich ohne -Wall kompiliert.
-
Sorry. Ich habe mich geirrt. Ihr habt ja so recht. Das Objekt DYN.c, das diesen Code enthält und ich als DYN.o erzeuge und zu meinem Testprogramm tst.c binde, hatte ich ohne -Wall übersetzt, nur tst.c mit -Wall gebunden und übersetzt. Ich habe jetzt DYN.c auch mit -Wall übersetzt und bekomme eine Warning gemeldet: "Operation auf lpnadc->R3P könnte undefiniert sein". Der GCC Compiler hat doch recht. Warum ein Borland-Compiler vom Kollegen das ignoriert, verstehe ich nicht. Borland kann ja auch nicht C99-Standard (Inline-Funktionen).
-
Jetzt ist ein Streit zwischen mir und meinem Kollegen entbrannt, ob der GCC einen Bug im Parallizer hat und das Original Statement hätte von links nach rechts abarbeiten müssen. Sein Borland Compiler bringt nicht einmal eine Warning bei dem Statement.
-
kjesse schrieb:
Jetzt ist ein Streit zwischen mir und meinem Kollegen entbrannt, ob der GCC einen Bug im Parallizer hat und das Original Statement hätte von links nach rechts abarbeiten müssen. Sein Borland Compiler bringt nicht einmal eine Warning bei dem Statement.
Welcher Parallizer?! Hier wird nichts parallelisiert.
Das Verhalten ist nach ISO/IEC 9899 undefiniert. Undefiniert heißt, der Compiler kann machen, was er will. Er kann das Programm übersetzen, oder nicht. Er kann eine Warnung ausgeben, oder nicht. Wenn er das Programm übersetzt, kann die Ausführung dieses Code dazu führen, dass die Erde explodiert. Das wäre laut Standard kein verbotenes verhalten.
Dein Kollege hat keine Ahnung.
EDIT:
Schon die Annahme, ein Compiler müsste irgendetwas von links nach rechts abarbeiten, ist falsch. Er muss lediglich sämtliche Sequenzpunkte von links nach rechts abarbeiten. Was zwischen zwei Sequenzpunkten geschieht, ist dem Compiler überlassen. Und in diesem Makro befinden sich keine Sequenzpunkte. Das sollte man als C-Programmierer aber wissen
-
LordJaxom schrieb:
EDIT:
Schon die Annahme, ein Compiler müsste irgendetwas von links nach rechts abarbeiten, ist falsch. Er muss lediglich sämtliche Sequenzpunkte von links nach rechts abarbeiten. Was zwischen zwei Sequenzpunkten geschieht, ist dem Compiler überlassen. Und in diesem Makro befinden sich keine Sequenzpunkte. Das sollte man als C-Programmierer aber wissenIch glaube der Kollege hat | mit || verwechselt.
-
Danke für die Unterstützung. Ich habe das genauso gesehen. Es ist der Code vom Kollegen, der auf Borland läuft und bei mir unter GCC eine Warning wirft. Der Kollege meinte, dass man den GCC in die Tonne treten sollte, wenn er nicht parallelisiert. Der Borland kann auch keine Inline-Funktionen. Der neue ANSI-konforme Code läuft übrigens perfekt.
-
kjesse schrieb:
Der Kollege meinte, dass man den GCC in die Tonne treten sollte, wenn er nicht parallelisiert.
Vielleicht sollte man Deinen Kollegen in die..... lassen wir das.
Wie kommt er darauf dass ein Compiler, ohne irgendwelche Erweiterungen, ohne dazu durch den Programmierer angewiesen worden zu sein, und ohne irgendwelche Threadfunktionen manuell benutzt zu haben, automatisch parallelisieren sollte?
Davon abgesehen, wieso sollte ausgerechnet eine parallele Ausführung sicherstellen, dass die vier Inkrementierungen in der richtigen Reihenfolge abgearbeitet werden? Der Witz an Parallelisierung wäre doch gerade, dass mehrere gleichzeitig abgearbeitet würden.
-
LordJaxom schrieb:
Wie kommt er darauf dass ein Compiler, ohne irgendwelche Erweiterungen, ohne dazu durch den Programmierer angewiesen worden zu sein, und ohne irgendwelche Threadfunktionen manuell benutzt zu haben, automatisch parallelisieren sollte?
Er meint wohl Instruction-Scheduling derart, dass parallele Pipelines bestmöglich ausgelastet werden. Nichts mit Threads.
Und er ist der Meinung, dass bei dieser Umordnung die Abhängigkeit der Inkrementierungen nicht richtig berücksichtigt wird. Da hätte er recht, wenn in C++ strenge Links-Nach-Rechts-Auswertung vorgeschrieben wäre, und wenn mehrfache Modifikationen zwischen Sequenzpunkten nicht explizit undefiniertes Verhalten hätten :p
-
Der Kollege hatte dann folgenden Code noch ausprobiert, der den gleichen Fehler beim Programmablauf bringt, aber keinerlei Warning mit GCC:
lpnadc->pu_buf = *lpnadc->R3P; lpnadc->R3P++; lpnadc->pu_buf=lpnadc->pu_buf<<8; \ lpnadc->pu_buf|= *lpnadc->R3P; lpnadc->R3P++; lpnadc->pu_buf=lpnadc->pu_buf<<8; \ lpnadc->pu_buf|= *lpnadc->R3P; lpnadc->R3P++; lpnadc->pu_buf=lpnadc->pu_buf<<8; \ lpnadc->pu_buf|= *lpnadc->R3P; lpnadc->R3P++; \
Hier wird zwar sequentiell gearbeitet, aber man hat das gleiche Problem mit dem Inkrementieren. Es kommt keine Warning, aber der gleiche Fehler beim Ausführen.
-
Ethon schrieb:
Wieso als Makro?Da ich schon weiß, welche Antwort kommen wird:
Der GCC kann so neuartige Standards wie C99. Da gibt es inline-Funktionen.Wie heißt es so schön in Effective C++: schon nur an Makro's zu denken ist schmerzhaft.
Und was spricht gegen eine Micki Maus Funktion ?
Makro's haben hübsche Seiteneffekte, da es bei Makro um ein dummen Ersetzungsvorgang handelt, und sind ebenfalls auch noch schwer zu debuggen. Sie sind daher nicht empfehlenswert.
#define min(a, b) (a < b) ? a : b //... c = min(++a, b); // ist äquivalent zu c = (++a < b) : ++a : b;
-
Ich benutze in meinen Programmen Inline-Funktionen, die auch noch schneller als Makros sind. Ich arbeite nur mit GCC auf Windows und Linux. Aber der Borland Compiler kann angeblich keine Inline-Funktionen im ANSI Standard.
-
Zum einen unterstützen weder Borland noch MSVC die neueren C-Standards (C99 aufwärts) und sollten demnach im C-Modus auch kein inline verstehen. Zweitens ist inline keine Garantie dass auch geinlined wird. Ergo: Kann viel gelabert werden, um Makros kommt man aber oft nicht herum.
-
Ich benutze in meinen Programmen Inline-Funktionen, die auch noch schneller als Makros sind. Ich arbeite nur mit GCC auf Windows und Linux. Aber der Borland Compiler kann angeblich keine Inline-Funktionen im ANSI Standard.
Hmm, ich glaube nicht das Makro's per se langsamer sind als Inline Funktionen. Bei Makros ersetzt der Präprozessor den Aufruf durch den Code während bei Inline Funktion der Linker den Aufruf ersetzt sofern das Kosten zu Nutzen Verhältniss stimmt (VC Compiler)
Aber, ist deine Anwendung so zeitkritisch das du auf ein paar Takte achten musst ?
Ich habe nämlich das Disassembly eines Testprojekts angeschaut und dort festgestellt das der Optimierer (alter VC Compiler) manchmal die Funktionen automatisch inlined ohne das ich explizite inline Funktionen hätte. Und seither stehe ich ein wenig kritisch gegenüber wenn es um Einsparungen von einigen wenigen Takten geht. Was nützt es dir wenn du hier und da ein paar Takte sparen willst wenn dein Optimierer das eh macht ? Alles unter der Vorraussetzung das du keine Sünden im Code hast.
Kann viel gelabert werden, um Makros kommt man aber oft nicht herum.
So zeitkritisch ???
Makro's funktionieren aber nur wenn man auf mögliche Seiteneffekte verzichtet. Funktionen sind da schöner, sie funktionieren auch mit Seiteneffekten. Sind also universeller. Und die schlechte Debuggingmöglichkeit ist meines Erachtens eh ein KO Kriterium.
-
Tim schrieb:
Zweitens ist inline keine Garantie dass auch geinlined wird.
Dann hat das aber auch einen guten Grund. Zum Beispiel, dass es ohne inline schneller ist. Der Compiler kann das beurteilen. Beim Makro ist er zum inline gezwungen und der Programmierer muss wissen, ob dies gerechtfertigt ist oder nicht.
-
kjesse schrieb:
Es kommt keine Warning, aber der gleiche Fehler beim Ausführen.
Was für ein Fehler? Du hast bisher nur geschrieben, dass das Verhalten anders als beim Sun oder Borland ist, und dass da irgendwas zweimal ausgeführt werden würde, ohne genauer zu beschreiben, was du eigentlich beobachtest.
Dazu möchte ich noch anmerken, dass in der Version die Casts fehlen. Sofern die im Ausgangscode notwendig (sprich: nicht nur Voodoo-Angstcode) waren, sind sie höchstwahrscheinlich auch jetzt noch notwendig. Im Zweifel poste die Definition des Strukturtyps von lpnadc.
-
Bitte ein Bit schrieb:
Wie heißt es so schön in Effective C++: schon nur an Makro's zu denken ist schmerzhaft.
(...)
Makro's haben hübsche Seiteneffekte, da es bei Makro um ein dummen Ersetzungsvorgang handelt, und sind ebenfalls auch noch schwer zu debuggen. Sie sind daher nicht empfehlenswert.
Makros sind subba!
Makros sind aber auch wie ein Hammer. Wenn man einen Nagel hat, ist ein Hammer subba! Und wenn man mit dem Hammer Schrauben in die Wand hämmert, dann ist deswegen nicht der Hammer schlecht.
-
Also Schrauben mit dem Hammer "schrauben" geht wunderbar und hält genau so gut wie geschraubt. :p
Anyway, Makros sind genial, nur als Funktionen missbraucht Mist.