#Defines einrücken

99

Ich weiß, dass #defines usw. normalerweise nie eingerückt werden. Warum?

Ich arbeite gerade in einem Code, der eine schreckliche Mischung aus #defines, #ifdefs, #elses, #endifs usw. enthält. All dies wird oft mit normalem C-Code gemischt. Das Nichteinrücken des #defines macht sie schwer lesbar. Und die Mischung aus eingerücktem Code mit nicht eingerücktem #defines ist ein Albtraum.

Was ist der Vorteil, wenn Sie nicht einrücken #define? Macht es mich zu einem schlechten Menschen, wenn ich sie einrücke? Ist das nicht viel schöner?

#ifdef SDCC
    #if DEBUGGING == 1
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x3DC0
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x7DC0
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #else
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x4000
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x8000
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #endif
#else
    #if DEBUGGING == 1
        #define FLASH_MEMORY_END 0x7DC0
    #else
        #define FLASH_MEMORY_END 0x8000
    #endif
#endif
Raketenmagnet
quelle

Antworten:

103

Der Pre-ANSI C-Präprozessor ließ keinen Abstand zwischen dem Zeilenanfang und dem Zeichen "#" zu. Das führende "#" musste immer in der ersten Spalte stehen.

Pre-ANSI C-Compiler gibt es heutzutage nicht mehr. Verwenden Sie den von Ihnen bevorzugten Stil (Leerzeichen vor "#" oder Leerzeichen zwischen "#" und dem Bezeichner).

http://www.delorie.com/gnu/docs/gcc/cpp_48.html


quelle
26

Wie einige bereits gesagt haben, mussten einige Pre-ANSI-Compiler das # als erstes Zeichen in der Zeile verwenden, aber es war nicht erforderlich, dass die Präprozessor-Direktive daran angehängt wurde, sodass der Einzug auf diese Weise vorgenommen wurde.

#ifdef SDCC
#  if DEBUGGING == 1
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x3DC0
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x7DC0
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  else
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x4000
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x8000
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  endif
#else
#  if DEBUGGING == 1
#    define FLASH_MEMORY_END 0x7DC0
#  else
#    define FLASH_MEMORY_END 0x8000
#  endif
#endif

Ich habe diesen Stil oft in alten Unix-Headern gesehen, aber ich hasse ihn, da die Syntaxfarbe bei solchen Codes oft fehlschlägt. Ich verwende eine sehr sichtbare Farbe für die Vorprozessor-Direktive, damit sie hervorstechen (sie befinden sich auf Metaebene und sollten daher nicht Teil des normalen Codeflusses sein). Sie können sogar sehen, dass SO die Sequenz nicht auf nützliche Weise färbt.

Patrick Schlüter
quelle
16

In Bezug auf das Parsen von Präprozessor-Direktiven war dem C99-Standard (und dem C89-Standard davor) die Reihenfolge der vom Compiler logisch ausgeführten Operationen klar. Insbesondere glaube ich, dass dies bedeutet, dass dieser Code:

/* */ # /* */ include /* */ <stdio.h> /* */

ist äquivalent zu:

#include <stdio.h>

Ob gut oder schlecht, GCC 3.4.4 mit '-std = c89 -pedantic' akzeptiert jedenfalls die kommentarreiche Zeile. Ich befürworte das nicht als Stil - nicht für eine Sekunde (es ist schrecklich). Ich denke nur, dass es möglich ist.

ISO / IEC 9899: 1999 Abschnitt 5.1.1.2 Übersetzungsphasen besagen:

  1. [Zeichenzuordnung, einschließlich Trigraphen]

  2. [Linienspleißen - Entfernen von Backslash-Zeilenumbrüchen]

  3. Die Quelldatei wird in Vorverarbeitungstoken und Sequenzen von Leerzeichen (einschließlich Kommentaren) zerlegt. Eine Quelldatei darf nicht mit einem Teilvorverarbeitungstoken oder einem Teilkommentar enden. Jeder Kommentar wird durch ein Leerzeichen ersetzt. Zeilenumbrüche bleiben erhalten. Ob jede nicht leere Folge von anderen Leerzeichen als Zeilenumbrüchen beibehalten oder durch ein Leerzeichen ersetzt wird, ist implementierungsdefiniert.

  4. Vorverarbeitungsanweisungen werden ausgeführt, Makroaufrufe werden erweitert, [...]

In Abschnitt 6.10 Richtlinien zur Vorverarbeitung heißt es:

Eine Vorverarbeitungsanweisung besteht aus einer Folge von Vorverarbeitungstoken, die mit einem # Vorverarbeitungstoken beginnt, das (zu Beginn der Übersetzungsphase 4) entweder das erste Zeichen in der Quelldatei ist (optional nach Leerzeichen, die keine Zeilenumbrüche enthalten) oder das folgt einem Leerzeichen mit mindestens einem Zeilenumbruchzeichen und wird durch das nächste Zeilenumbruchzeichen beendet.

Der einzig mögliche Streit ist der Klammerausdruck "(zu Beginn der Übersetzungsphase 4)", was bedeuten könnte, dass die Kommentare vor dem Hash fehlen müssen, da sie bis zum Ende von Phase 4 nicht durch Leerzeichen ersetzt werden.

Wie andere angemerkt haben, verhielten sich die C-Präprozessoren vor dem Standard in vielerlei Hinsicht nicht einheitlich, und Leerzeichen vor und in Präprozessor-Direktiven waren einer der Bereiche, in denen verschiedene Compiler unterschiedliche Aufgaben ausführten, einschließlich der Nichterkennung von Präprozessor-Direktiven mit Leerzeichen vor ihnen .

Es ist bemerkenswert, dass das Entfernen von Backslash-Newline erfolgt, bevor Kommentare analysiert werden. Folglich sollten Sie //Kommentare nicht mit einem Backslash beenden .

Jonathan Leffler
quelle
7

Ich weiß nicht, warum es nicht häufiger ist. Es gibt sicherlich Zeiten, in denen ich Präprozessor-Direktiven einrücken möchte.

Eine Sache, die mir immer wieder im Weg steht (und mich manchmal davon überzeugt, nicht mehr zu versuchen), ist, dass viele oder die meisten Redakteure / IDEs die Direktive bei der geringsten Provokation in Spalte 1 werfen. Was höllisch nervt.

Michael Burr
quelle
5

Heutzutage glaube ich, dass dies hauptsächlich eine Wahl des Stils ist. Ich denke, an einem Punkt in der fernen Vergangenheit haben nicht alle Compiler den Gedanken des Einrückens von Präprozessordefinitionen unterstützt. Ich habe einige Nachforschungen angestellt und konnte diese Behauptung nicht stützen. Auf jeden Fall scheinen jedoch alle modernen Compiler die Idee zu unterstützen, das Präprozessor-Makro einzurücken. Ich habe jedoch keine Kopie des C- oder C ++ - Standards, daher weiß ich nicht, ob dies Standardverhalten ist oder nicht.

Ob es ein guter Stil ist oder nicht. Persönlich mag ich die Idee, sie alle links zu halten. Es gibt Ihnen einen konsistenten Ort, um nach ihnen zu suchen. Ja, es kann nervig werden, wenn es sehr verschachtelte Makros gibt. Wenn Sie sie jedoch einrücken, erhalten Sie schließlich einen noch seltsameren Code.

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif
JaredPar
quelle
13
Der Grund, warum dieser Code seltsam aussieht, ist, dass Sie zwei "Einrückungsströme" erstellt haben. Ich würde Zeile 4 um eine weitere Ebene einrücken, und ich würde die Zeilen 6 und 7 um zwei weitere Ebenen einrücken.
Kevin
2
Stimme voll und ganz zu. Ich setze manchmal sogar geschweifte Klammern, damit das # if genauso aussieht wie das if.
Baash05
3
Ich bemühe mich sehr, meinen Code so anzuordnen, dass er keine #ifdef Zeilen in den Teilen enthält, in denen ich den tatsächlichen Code habe. Wenn ich bedingte Dinge benötige, setze ich sie entweder in ausgerechnete Funktionen oder in ausgerechnete Makros ein. es ist viel klarer so, wie ich es finde (zumindest für mich). Im Idealfall befinden sich alle ausgerechneten Teile in anderen Dateien (Header oder bedingt kompilierte Quelldateien; die übliche "Bedingung" ist, für welche Plattform der Code erstellt wird).
Donal Fellows
2
Ich würde die Zeilen 4 um eine Ebene und die Zeilen 6 und 7 um zwei Ebenen einrücken.
Raketenmagnet
3

Für das Beispiel, das Sie gegeben haben, kann es angebracht sein, Einrückungen zu verwenden, um es klarer zu machen, da Sie eine so komplexe Struktur verschachtelter Direktiven haben.

Persönlich halte ich es für nützlich, sie die meiste Zeit nicht eingerückt zu lassen, da diese Anweisungen getrennt vom Rest Ihres Codes funktionieren. Anweisungen wie #ifdef werden vom Vorprozessor verarbeitet, bevor der Compiler Ihren Code jemals sieht, sodass ein Codeblock nach einer # ifdef-Anweisung möglicherweise nicht einmal kompiliert wird .

Es ist wichtiger, Anweisungen visuell vom Rest Ihres Codes getrennt zu halten, wenn sie mit Code durchsetzt sind (und nicht wie in dem von Ihnen angegebenen Beispiel ein dedizierter Block von Anweisungen).

Daniel Fortunov
quelle
3
Was ist aus Sicht der IP der Unterschied zwischen etwas, das nicht kompiliert wurde, und etwas, das aufgrund eines JMP nicht erreicht wurde?
Baash05
2

Ich arbeite gerade in einem Code, der eine schreckliche Mischung aus #defines, #ifdefs, #elses, #endifs, #etc enthält. All dies wird oft mit normalem C-Code gemischt. Das Nichteinrücken der #defines macht sie schwer lesbar. Und die Mischung aus eingerücktem Code mit nicht eingerückten #defines ist ein Albtraum.

Eine übliche Lösung besteht darin, die Anweisungen zu kommentieren, damit Sie leicht wissen, worauf sie sich beziehen:

#ifdef FOO
/* a lot of code */
#endif /* FOO */

#ifndef FOO
/* a lot of code */
#endif /* not FOO */
Bastien Léonard
quelle
6
Ich habe diesen Stil gesehen, mein Chef benutzt ihn. Und wie der Rest seines Codes macht es nur ein Chaos. Stellen Sie sich vor, Sie entfernen alle Einrückungen aus Ihren normalen if () - Anweisungen und verwenden stattdessen diese Kommentare. Sie werden sich beschweren, dass Sie nicht leicht sehen können, worauf sie sich beziehen.
Raketenmagnet
2

In fast allen derzeit verfügbaren C / CPP-Compilern ist dies nicht eingeschränkt. Es ist Sache des Benutzers, zu entscheiden, wie Sie den Code ausrichten möchten. Also viel Spaß beim Codieren.

Hemanth
quelle
1
Anständige Antwort. Könnten Sie es verbessern, indem Sie eine bestimmte Styleguide-Referenz hinzufügen?
EtherDragon