Variable Deklarationsplatzierung in C

Ich dachte lange, dass in C alle Variablen am Anfang der function deklariert werden müssten. Ich weiß, dass in C99 die Regeln die gleichen wie in C ++ sind, aber was sind die Variablendeklarations-Platzierungsregeln für C89 / ANSI C?

Der folgende Code wird erfolgreich mit gcc -std=c89 und gcc -ansi :

 #include  int main() { int i; for (i = 0; i < 10; i++) { char c = (i % 95) + 32; printf("%i: %c\n", i, c); char *s; s = "some string"; puts(s); } return 0; } 

Sollten die Deklarationen von c und s im C89 / ANSI-Modus nicht zu einem Fehler führen?

Solutions Collecting From Web of "Variable Deklarationsplatzierung in C"

Es kompiliert erfolgreich, weil GCC es als GNU-Erweiterung erlaubt, obwohl es nicht Teil des C89- oder ANSI-Standards ist. Wenn Sie sich streng an diese Standards halten wollen, müssen Sie die -pedantic Flagge übergeben.

Für C89 müssen Sie alle Ihre Variablen am Anfang eines Bereichsblocks deklarieren.

Ihre char c Deklaration ist also gültig, da sie am Anfang des for-Schleife-Bereichsblocks steht. Aber die char *s Deklaration sollte ein Fehler sein.

Von einem Wartbarkeits- statt syntaktischen Standpunkt aus gibt es mindestens drei Gedankengänge:

  1. Deklarieren Sie alle Variablen am Anfang der function, sodass sie sich an einem Ort befinden und Sie die umfassende Liste auf einen Blick sehen können.

  2. Deklarieren Sie alle Variablen so nah wie möglich an dem Ort, an dem sie zuerst verwendet werden, damit Sie wissen, warum sie benötigt werden.

  3. Deklarieren Sie alle Variablen am Anfang des innersten Bereichsblocks, damit sie so schnell wie möglich den Gültigkeitsbereich verlassen und dem Compiler ermöglichen, den Speicher zu optimieren und Ihnen mitzuteilen, ob Sie sie versehentlich an der Stelle verwenden, die Sie nicht beabsichtigt haben.

Im Allgemeinen bevorzuge ich die erste Option, da ich finde, dass die anderen mich oft zwingen, Code für die Deklarationen durchzusuchen. Wenn Sie alle Variablen im Voraus definieren, können Sie sie auch leichter initialisieren und von einem Debugger aus überwachen.

Ich werde manchmal Variablen innerhalb eines kleineren scopesblocks deklarieren, aber nur aus gutem Grund, von denen ich sehr wenig habe. Ein Beispiel könnte nach einem fork() , um Variablen zu deklarieren, die nur vom Kindprozess benötigt werden. Für mich ist dieser visuelle Indikator eine hilfreiche Erinnerung an ihren Zweck.

Die Gruppierung von Variablendeklarationen am oberen Ende des Blocks ist ein Vermächtnis, das wahrscheinlich auf Einschränkungen alter primitiver C-Compiler zurückzuführen ist. Alle modernen Sprachen empfehlen und manchmal sogar die Deklaration lokaler Variablen am letzten Punkt zu erzwingen: wo sie zuerst initialisiert werden. Denn dadurch wird das Risiko der zufälligen Verwendung eines zufälligen Werts beseitigt. Trennungserklärung und Initialisierung verhindert auch, dass Sie “const” (oder “final”) verwenden, wenn Sie könnten.

C ++ akzeptiert leider immer noch die alte, oberste Deklarationsweise für Rückwärtskompatibilität mit C (eine C-Kompatibilität schleppt sich aus vielen anderen heraus …) Aber C ++ versucht, sich davon zu entfernen:

  • Der Entwurf von C ++ – Verweisen erlaubt nicht einmal einen solchen oberen Teil der Blockgruppierung.
  • Wenn Sie Deklaration und Initialisierung eines lokalen C ++ – Objekts trennen, zahlen Sie die Kosten eines zusätzlichen Konstruktors für nichts. Wenn der no-arg-Konstruktor nicht existiert, dann dürfen Sie beide nicht einmal trennen!

C99 beginnt, C in dieselbe Richtung zu bewegen.

Wenn Sie sich Sorgen machen, nicht zu finden, wo lokale Variablen deklariert sind, bedeutet das, dass Sie ein viel größeres Problem haben: Der umschließende Block ist zu lang und sollte geteilt werden.

https://www.securecoding.cert.org/confluence/display/cplusplus/DCL19-CPP.+Initialize+automatic+local+variables+on+declaration

Wie von anderen angemerkt, ist GCC in dieser Hinsicht (und möglicherweise anderen Compilern, abhängig von den Argumenten, mit denen sie bezeichnet werden) auch im C89-Modus erlaubt, es sei denn, Sie verwenden eine “pedantische” Überprüfung. Um ehrlich zu sein, gibt es nicht viele gute Gründe, nicht pedantisch zu sein; moderner Qualitätscode sollte immer ohne Warnungen kompiliert werden (oder sehr wenige, bei denen Sie wissen, dass Sie etwas Bestimmtes tun, das dem Compiler als möglicher Fehler verdächtig ist). Wenn Sie also Ihren Code nicht mit einem pedantischen Setup kompilieren können, benötigt er wahrscheinlich etwas Aufmerksamkeit.

C89 erfordert, dass Variablen vor irgendwelchen anderen statementen in jedem Bereich deklariert werden, spätere Standards erlauben eine näher zu verwendende Deklaration (die sowohl intuitiver als auch effizienter sein kann), insbesondere die gleichzeitige Deklaration und Initialisierung einer Schleifensteuervariablen in For-Schleifen.

Wie bereits erwähnt, gibt es dazu zwei Denkrichtungen.

1) Erklären Sie alles an der Spitze der functionen, denn das Jahr ist 1987.

2) Erklären Sie möglichst nahe bei der ersten Verwendung und im kleinstmöglichen scope.

Meine Antwort darauf ist DO BEIDE! Lassen Sie mich erklären:

Für lange functionen macht 1) das Refactoring sehr schwierig. Wenn Sie in einer Codebasis arbeiten, in der die Entwickler gegen die Idee von Unterprogrammen sind, dann haben Sie am Anfang der function 50 Variablendeklarationen und einige von ihnen könnten nur ein “i” für eine For-Schleife sein, die am Ende ist Unterseite der function.

Ich entwickelte daher eine Deklaration-an-der-Spitze-PTSD und versuchte Option 2) religiös zu machen.

Ich kam wegen einer Sache zurück zur ersten Wahl: kurze functionen. Wenn Ihre functionen kurz genug sind, werden Sie nur wenige lokale Variablen haben. Da die function kurz ist und Sie sie an den Anfang der function stellen, sind sie immer noch nahe an der ersten Verwendung.

Auch das Anti-Muster von “deklariere und setze auf NULL”, wenn du oben deklarieren willst, aber du hast einige Berechnungen zur Initialisierung nicht durchgeführt, weil die Dinge, die du initialisieren musst, wahrscheinlich als Argumente empfangen werden.

So, jetzt ist mein Denken, dass Sie an der Spitze der functionen und so nah wie möglich zum ersten Mal erklären sollten. Also BEIDE! Und die Art, das zu tun, ist mit gut unterteilten Subroutinen.

Aber wenn Sie an einer langen function arbeiten, sollten Sie die Dinge der ersten Verwendung am nächsten bringen, da es auf diese Weise einfacher ist, Methoden zu extrahieren.

Mein Rezept ist das. Nehmen Sie für alle lokalen Variablen die Variable und verschieben Sie ihre Deklaration nach unten, kompilieren Sie sie, und verschieben Sie die Deklaration dann direkt vor dem Kompilierungserrors. Das ist die erste Verwendung. Tun Sie dies für alle lokalen Variablen.

 int foo = 0;  int bar = 1;   

Definieren Sie nun einen Bereichsblock, der vor der Deklaration beginnt, und verschieben Sie das Ende, bis das Programm kompiliert wird

 { int foo = 0;  } int bar = 1;  >>> First compilation error here  

Dies wird nicht kompiliert, da es mehr Code gibt, der foo verwendet. Wir können feststellen, dass der Compiler in der Lage war, den Code zu durchlaufen, der bar verwendet, weil er foo nicht verwendet. An diesem Punkt gibt es zwei Möglichkeiten. Die mechanische ist, das "}" einfach nach unten zu verschieben, bis es kompiliert wird, und die andere Möglichkeit besteht darin, den Code zu untersuchen und festzustellen, ob die Reihenfolge geändert werden kann zu:

 { int foo = 0;  }  int bar = 1;  

Wenn die Reihenfolge geändert werden kann, ist dies wahrscheinlich das, was Sie wollen, weil es die Lebensdauer von temporären Werten verkürzt.

Eine andere Sache, die man beachten sollte, ist, dass der Wert von foo zwischen den Code-Blöcken, die ihn benutzen, beibehalten werden muss, oder könnte es nur ein anderer foo in beiden sein. Beispielsweise

 int i; for(i = 0; i < 8; ++i){ ... }  for(i = 3; i < 32; ++i){ ... } 

Diese Situationen brauchen mehr als meine Vorgehensweise. Der Entwickler muss den Code analysieren, um zu bestimmen, was zu tun ist.

Aber der erste Schritt ist die erste Verwendung. Sie können es visuell tun, aber manchmal ist es einfacher, die Deklaration zu löschen, zu kompilieren und sie einfach über die erste Verwendung zu stellen. Wenn diese erste Verwendung innerhalb einer if-statement ist, lege sie dort hin und überprüfe, ob sie kompiliert wird. Der Compiler wird dann andere Verwendungen identifizieren. Versuchen Sie, einen Gültigkeitsbereich-Block zu erstellen, der beide Verwendungen umfasst.

Nachdem dieser mechanische Teil fertig ist, wird es einfacher zu analysieren, wo die Daten sind. Wenn eine Variable in einem großen Bereichsblock verwendet wird, analysieren Sie die Situation und sehen Sie, ob Sie nur die gleiche Variable für zwei verschiedene Dinge verwenden (wie ein "i", das für zwei for-Schleifen verwendet wird). Wenn die Verwendungen nicht miteinander verknüpft sind, erstellen Sie für jede dieser nicht verwandten Verwendungen neue Variablen.

Ich werde einige Aussagen aus dem Handbuch für gcc Version 4.7.0 zitieren, um eine klare Erklärung zu erhalten.

“Der Compiler kann mehrere Basisstandards wie” c90 “oder” c ++ 98 “und GNU-Dialekte dieser Standards, wie” gnu90 “oder” gnu ++ 98 “akzeptieren. Durch Angabe eines Basisstandards, des Compilers akzeptiert alle Programme, die diesem Standard folgen, und solche, die GNU-Erweiterungen verwenden, die dem nicht widersprechen, zum Beispiel deaktiviert “-std = c90″ bestimmte functionen von GCC, die nicht mit ISO C90 kompatibel sind, wie die Schlüsselwörter asm und typeof andere GNU-Erweiterungen, die in ISO C90 keine Bedeutung haben, z. B. das Auslassen des mittleren Ausdrucks eines?: -Ausdrucks. ”

Ich denke, der entscheidende Punkt Ihrer Frage ist, warum gcc nicht mit C89 übereinstimmt, selbst wenn die Option “-std = c89” verwendet wird. Ich kenne die Version deines GCC nicht, aber ich denke, dass es keinen großen Unterschied geben wird. Der Entwickler von gcc hat uns gesagt, dass die Option “-std = c89” nur bedeutet, dass die Erweiterungen, die C89 widersprechen, ausgeschaltet sind. Es hat also nichts mit einigen Erweiterungen zu tun, die in C89 keine Bedeutung haben. Und die Erweiterung, die die Platzierung der Variablendeklaration nicht einschränkt, gehört zu den Erweiterungen, die C89 nicht widersprechen.

Um ehrlich zu sein, wird jeder denken, dass es C89 vollständig auf den ersten Blick der Option “-std = c89” anpassen sollte. Aber das tut es nicht. Was das Problem angeht, dass alle Variablen am Anfang deklarieren besser oder schlechter ist nur eine Frage der Gewohnheit.