Frage

ich lange gedacht, dass in C, alle Variablen am Anfang der Funktion deklariert werden mußten. Ich weiß, dass in C99, die gleichen Regeln wie in C ++ sind, aber was ist die Variablendeklaration Platzierungsregeln für C89 / ANSI C?

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

#include <stdio.h>
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 nicht die Erklärungen von c und s einen Fehler in C89 / ANSI-Modus verursachen?

War es hilfreich?

Lösung

Es kompiliert erfolgreich, weil GCC es als eine GNU-Erweiterung ermöglicht es, auch wenn es nicht Teil des C89 oder ANSI-Standard ist. Wenn Sie sich strikt an diese Standards halten wollen, müssen Sie die -pedantic Flagge übergeben.

Andere Tipps

Für C89, müssen Sie alle Ihre Variablen am Anfang eines Umfang Block deklarieren .

Also, Ihre char c Erklärung gilt als es an der Spitze der for-Schleife Umfang Block ist. Aber sollte die char *s Erklärung ein Fehler sein.

Gruppierung Variablendeklarationen an der Spitze des Blockes ist ein Vermächtnis wahrscheinlich aufgrund der Beschränkungen von den alten, primitiven C-Compiler. Alle modernen Sprachen empfehlen und manchmal sogar die Deklaration von lokalen Variablen an der aktuellen Stelle erzwingen: wo sie zuerst initialisiert sind. Da diese bekommt einen zufälligen Wert der Verwendung versehentlich das Risiko zu befreien. Die Trennung Deklaration und Initialisierung verhindert, dass Sie auch mit „const“ (oder „final“), wenn man könnte.

C ++ leider hält die alte, oben Erklärung Art und Weise für die Abwärtskompatibilität mit C zu akzeptieren (eine C-Kompatibilität ziehen aus viele andere ...) Aber C ++ versucht, von ihm weg zu bewegen:

  • Das Design der C ++ Referenzen nicht einmal so oberen Rand der Blockgruppierung ermöglichen.
  • Wenn Sie Deklaration und Initialisierung eines C ++ trennen lokalen Objekt dann zahlen Sie die Kosten eines weiteren Konstruktor für nichts. Wenn der Konstruktor ohne Argumente nicht existiert, wieder sind Sie nicht einmal erlaubt, beide zu trennen!

C99 beginnt C in der gleichen Richtung zu bewegen.

Wenn Sie besorgt sind, nicht zu finden, wo lokale Variablen deklariert sind, dann bedeutet dies, Sie haben ein viel größeres Problem. Der umschließende Block zu lang und sollte aufgeteilt werden

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

Von einer Wartbarkeit, anstatt syntaktischem, Standpunkt, gibt es mindestens drei Gedankengänge:

  1. Deklarieren Sie alle Variablen, die am Anfang der Funktion, so dass sie an einem Ort sein werden und Sie werden die vollständige Liste auf einen Blick.

  2. sehen können
  3. Deklarieren Sie alle Variablen so nah wie möglich an den Ort, den sie zuerst gewöhnt sind, damit Sie wissen, Warum jeder benötigt wird.

  4. Deklarieren Sie alle Variablen zu Beginn des innersten Umfang Block, so werden sie so schnell wie möglich außerhalb des Gültigkeitsbereichs gehen und lassen Sie den Compiler-Speicher zu optimieren und Ihnen sagen, wenn Sie sie versehentlich verwenden, wo man nicht hatte vorgesehen.

Ich ziehe es im Allgemeinen die erste Option, da ich die andere oft zwingen, mich zu finden für die Erklärungen über Code zu jagen. Definition aller Variablen vorne macht es auch leichter zu initialisieren und beobachten sie von einem Debugger.

Ich werde manchmal Variablen in einem kleineren Umfang Block, erklären aber nur für einen guten Grund, von denen ich nur sehr wenige haben. Ein Beispiel könnte nach einer fork() sein, um Variablen zu deklarieren benötigt nur durch das Kind Prozess. Für mich ist diese visuelle Anzeige ist eine hilfreiche Erinnerung an ihren Zweck.

Wie bereits von anderen erwähnt, GCC ist permissive in dieser Hinsicht (und möglicherweise anderen Compilern, in Abhängigkeit von den Argumenten ihnen genannt werden), auch wenn in ‚C89‘ -Modus, wenn Sie sich erkundigt, ob pedantisch 'verwenden. Um ehrlich zu sein, gibt es nicht viele gute Gründe, nicht pedantisch haben auf; Qualität moderner Code immer ohne Warnungen kompiliert (oder nur sehr wenige, wo Sie wissen, dass Sie etwas Bestimmtes tun, die den Compiler als möglichen Fehler verdächtig ist), wenn Sie also nicht Ihren Code mit einem pedantischen Setup machen kompilieren sie wahrscheinlich einige Aufmerksamkeit brauchen.

C89 erfordert, dass Variablen vor allen anderen Anweisungen in jedem Rahmen, erlauben später Normen Erklärung näher (die beide intuitive und effizienter sein kann) zu verwenden, deklariert werden, vor allem die gleichzeitige Deklaration und Initialisierung einer Schleifensteuerungsvariable in ‚für "Schleifen.

Wie bereits erwähnt, gibt es zwei Denkschulen zu diesem Thema.

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

2) Erklären Sie am nächsten ersten Gebrauch und in dem kleinsten Umfang möglich.

Meine Antwort ist beides! Lassen Sie mich erklären:

Für lange Funktionen, 1) macht sehr hart Refactoring. Wenn Sie in einer Code-Basis arbeiten, wo die Entwickler gegen die Idee von Subroutinen, dann werden Sie 50 Variablendeklarationen haben zu Beginn der Funktion und einige von ihnen vielleicht nur eine seines „i“ für eine for-Schleife, die ganz ist Unterseite der Funktion.

Ich entwickeln deshalb Erklärung-at-the-Top-PTSD aus diesem und versuchte, Option 2) religiös zu tun.

Ich kam zurück um zu Option eins wegen einer: kurze Funktionen. Wenn Ihre Funktionen kurz genug sind, dann werden Sie einige lokale Variablen haben und da die Funktion kurz ist, wenn man sie an der Spitze der Funktion setzen, werden sie nach wie vor dem ersten Gebrauch nahe sein.

, auch das anti-Muster von „deklariert und auf NULL“, wenn Sie an der Spitze erklären wollen, aber Sie haben nicht einige Berechnungen, die für die Initialisierung aufgelöst, weil die Dinge, die Sie initialisieren wird wahrscheinlich empfangen werden vorgenommen, Argumente.

So, jetzt mein Denken ist, dass Sie an der Spitze der Funktionen und so nah wie möglich an den ersten Gebrauch zu erklären. Also beide! Und die Art und Weise zu tun, ist mit gut aufgeteilt Subroutinen.

Wenn Sie aber auf eine lange Funktion arbeiten, dann Dinge setzen am nächsten dem ersten Gebrauch, weil auf diese Weise wird es leichter sein, Methoden zu extrahieren.

Mein Rezept ist dies. Für alle lokalen Variablen, nehmen Sie die Variable und die Erklärung nach unten verschieben, kompilieren, dann die Erklärung bewegen, um kurz vor dem Übersetzungsfehler. Das ist der erste Einsatz. Tun Sie dies für alle lokalen Variablen.

int foo = 0;
<code that uses foo>

int bar = 1;
<code that uses bar>

<code that uses foo>

Nun definieren einen Umfang Block, der die Deklaration beginnt, bevor und das Ende bewegen, bis das Programm kompiliert

{
    int foo = 0;
    <code that uses foo>
}

int bar = 1;
<code that uses bar>

>>> First compilation error here
<code that uses foo>

Diese nicht kompilieren, weil es einige mehr Code, foo verwendet. Wir können feststellen, dass der Compiler in der Lage war, durch den Code zu gehen, die Bar verwendet, weil es nicht foo nicht verwendet. An diesem Punkt gibt es zwei Möglichkeiten. Die mechanische ist nur die „}“ nach unten zu bewegen, bis es kompiliert, und die andere Wahl ist, den Code zu überprüfen und festzustellen, ob der Auftrag geändert werden kann:

{
    int foo = 0;
    <code that uses foo>
}

<code that uses foo>

int bar = 1;
<code that uses bar>

Wenn die Reihenfolge umgeschaltet werden kann, das ist wahrscheinlich das, was Sie wollen, weil es die Lebensdauer der temporären Werte verkürzt.

Eine andere Sache zu beachten, ändert sich der Wert von foo zwischen den Blöcken von Code zu erhalten müssen, die sie benutzen, oder könnte es nur eine andere foo in beiden sein. Zum Beispiel

int i;

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

<some stuff>

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

Diese Situationen brauchen mehr als mein Verfahren. Der Entwickler wird den Code zu analysieren, um zu bestimmen, was zu tun ist.

Aber der erste Schritt ist, den ersten Einsatz zu finden. Sie können es visuell tun, aber manchmal, es ist nur einfacher, die Erklärung zu löschen, versuchen zu kompilieren und es direkt über dem ersten Gebrauch zurückstellen. Wenn die erste Verwendung innerhalb einer if-Anweisung ist, hat es dort und überprüfen, ob es kompiliert. Der Compiler wird dann andere Verwendungen identifizieren. Versuchen Sie, einen Bereich Block zu machen, die beide Anwendungen umfasst.

Nach diesem mechanischen Teil fertig ist, dann wird es einfacher zu analysieren, wo die Daten. Wenn eine Variable in einem großen Umfang Block verwendet wird, die Situation analysieren und sehen, ob Sie nur die gleiche Variable für zwei verschiedene Dinge verwenden (wie ein „i“, dass für zwei for-Schleifen verwendet wird). Wenn die Anwendungen nicht verwandt sind, neue Variablen für jede dieser unabhängigen Anwendungen.

werde ich einige Aussagen für gcc Version 4.7.0 für eine klare Erklärung aus dem Handbuch zitieren.

"Der Compiler kann mehrere Basisstandards akzeptieren, wie 'c90' oder 'c ++ 98' und GNU Dialekte dieser Standards wie 'gnu90' oder 'Gnu ++ 98'. Durch einen Basis-Standard spezifiziert wird der Compiler alle Programme Erweiterungen folgt, Standard und diejenigen mit GNU akzeptieren, dass sie nicht widersprechen. Zum Beispiel: ‚-std = c90‘ schaltet sich aus, bestimmte Funktionen von GCC, die mit ISO C90, wie die asm und typeof Schlüsselwörter unvereinbar sind , aber nicht andere GNU-Erweiterungen, die, wie das Weglassen der mittlere Laufzeit eines nicht eine Bedeutung in ISO C90 haben:. Ausdruck "

Ich glaube, der entscheidende Punkt Ihrer Frage ist, warum gcc nicht für C89 entsprechen, auch wenn die Option „-std = c89“ verwendet wird. Ich weiß nicht, welche Version des gcc, aber ich denke, dass es nicht großer Unterschied sein wird. Der Entwickler von gcc hat uns gesagt, dass die Option „-std = c89“ bedeutet nur die Erweiterungen, die C89 wieder ausgeschaltet sind. Also, es hat nichts mit einigen Erweiterungen zu tun, die keinen Sinn in C89 haben. Und die Erweiterung, die die Platzierung der Variablendeklaration nicht beschränken gehört zu den Erweiterungen, die nicht C89 widersprechen.

Um ehrlich zu sein, jeder wird denken, dass es C89 auf den ersten Blick der Option vollständig entsprechen sollten „-std = c89“. Aber es funktioniert nicht. Was das Problem, das alle Variablen zu Beginn erklären ist besser oder schlechter ist nur eine Frage der Gewohnheit.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top