Warum ist int x[n] falsch, wo n ein konstanter Wert ist?

Lesezeit: 7 Minuten

Benutzeravatar von Yahia Farghaly
Yahia Farghaly

Ich kann nicht verstehen, warum das falsch ist:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

wenngleich n ist bereits ein konstanter Wert.

Dabei scheint dies für den GNU-Compiler richtig zu sein:

const int n = 5;
int x[n]; /*without initialization*/

Ich bin mir der VLA-Funktion von C99 bewusst und denke, dass sie mit dem zusammenhängt, was vor sich geht, aber ich brauche nur eine Klärung dessen, was im Hintergrund passiert.

  • Ich vermute, wenn Sie anheften static zur Definition von nes wird klappen; static const wäre eine echte globale Kompilierzeitkonstante, keine im Stapel gespeicherte Konstante.

    – ShadowRanger

    2. Februar 2016 um 19:24 Uhr

  • @ShadowRanger: C ist nicht C++. Die Semantik von const ist einer der Unterschiede. C hat keine symbolische Konstante. Z.B 1 ist ein ganzzahlige Konstante ab der C-Norm. const int i ist immer noch eine Variable!

    – zu ehrlich für diese Seite

    2. Februar 2016 um 19:31 Uhr

  • const ist nur ein Versprechen Sie Geben Sie dem Compiler eine Variable weder direkt noch indirekt. Bei ersterem werden die meisten Compiler warnen, bei letzterem (z. B. durch Zeiger) sind sie sehr eingeschränkt darin, alle möglichen Verstöße zu erkennen. Wenn Sie den Vertrag verletzen, berufen Sie sich auf undefiniertes Verhalten.

    – zu ehrlich für diese Seite

    2. Februar 2016 um 19:33 Uhr


  • @Olaf, C hat etwas symbolische Konstanten, wo man sie vielleicht nicht erwartet, nämlich Aufzählungen.

    – Jens Gustedt

    2. Februar 2016 um 20:27 Uhr

  • @JensGustedt: Ja, die vergesse ich immer. Sie sind von besonderer Art, vor allem, weil Sie ihren Typ nicht explizit angeben können (etwas, das C11 auch von C++ 11 kopieren sollte, um zu erstellen enumist endlich nützlich). Und die beiden “booleschen” Konstanten _True und _False die nicht benutzerdefinierbar sind. Hmm, wie Sie “nämlich” geschrieben haben, was impliziert, dass es auch andere gibt: Irgendwelche anderen benutzerdefinierbaren symbolischen Konstanten, die ich vergessen habe? (ernsthafte Frage, ich habe vielleicht etwas einfaches übersehen – wenn du im Wald bist, siehst du nur Bäume)

    – zu ehrlich für diese Seite

    2. Februar 2016 um 20:54 Uhr


Benutzeravatar von Keith Thompson
Keith Thompson

Das Wichtigste, woran man sich erinnern sollte, ist das const und “konstant” bedeuten zwei ganz verschiedene Dinge.

Das const Schlüsselwort bedeutet wirklich “schreibgeschützt”. EIN Konstante ist ein numerisches Literal, wie z 42 oder 1.5 (oder eine Aufzählung oder Zeichenkonstante). EIN ständiger Ausdruck ist eine bestimmte Art von Ausdruck, der zur Kompilierzeit ausgewertet werden kann, z 2 + 2.

Also eine Deklaration gegeben:

const int n = 5;

der Ausdruck n bezieht sich auf den Wert der Objekt, und es wird nicht als konstanter Ausdruck behandelt. Ein typischer Compiler optimiert einen Verweis auf nund ersetzen Sie es durch denselben Code, den es für ein Literal verwenden würde 5aber das ist nicht erforderlich — und die Regeln dafür, ob ein Ausdruck ist Konstante werden von der Sprache bestimmt, nicht von der Cleverness des aktuellen Compilers.

Ein Beispiel für den Unterschied zwischen const (schreibgeschützt) und Konstante (ausgewertet zur Kompilierzeit) ist:

const size_t now = time(NULL);

Das const Schlüsselwort bedeutet, dass Sie den Wert von nicht ändern dürfen now nach seiner Initialisierung, aber der Wert von time(NULL) kann natürlich erst zur Laufzeit berechnet werden.

Also das:

const int n = 5;
int x[n];

ist in C nicht gültiger als ohne die const Stichwort.

Die Sprache könnte (und sollte meiner Meinung nach wahrscheinlich) bewerten n als konstanter Ausdruck; es ist einfach nicht so definiert. (C++ hat eine solche Regel; sehen Sie sich den C++-Standard oder eine anständige Referenz für die blutigen Details an.)

Wenn Sie eine benannte Konstante mit dem Wert möchten 5ist die gebräuchlichste Methode, ein Makro zu definieren:

#define N 5
int x[N];

Ein anderer Ansatz besteht darin, eine Aufzählungskonstante zu definieren:

enum { n = 5 };
int x[n];

Aufzählungskonstanten sind konstante Ausdrücke und immer vom Typ int (was bedeutet, dass diese Methode nicht für andere Typen als funktioniert int). Und es ist wohl ein Missbrauch des enum Mechanismus.

Beginnend mit dem Standard von 1999 kann ein Array mit einer nicht konstanten Größe definiert werden; Dies ist ein VLA oder ein Array mit variabler Länge. Solche Arrays sind nur im Blockbereich zulässig und dürfen keine Initialisierer haben (da der Compiler nicht überprüfen kann, ob der Initialisierer die richtige Anzahl von Elementen hat).

Aber angesichts Ihres ursprünglichen Codes:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

Sie können den Compiler die Länge vom Initialisierer ableiten lassen:

int x[] = { 1,1,3,4,5 };

Und Sie können dann die Länge aus der Größe des Arrays berechnen:

const int x_len = sizeof x / sizeof x[0];

  • “Eine Konstante ist ein numerisches Literal”. Ist das genau richtig? Es muss einige andere Arten von Konstanten geben?

    – artm

    3. Februar 2016 um 8:39 Uhr

  • OT, aber: “C++ hat eine solche Regel [to evaluate n as a constant expression]”. Nun, nur manchmal; um diesen Satz wirklich genau zu machen, müssten Sie ersetzen constexpr. Nur verwenden const kann in einigen Fällen funktionieren, garantiert jedoch je nach Kontext keinen “konstanten Ausdruck” in C++. zB: stackoverflow.com/questions/18996258/…

    – Unterstrich_d

    3. Februar 2016 um 13:46 Uhr


  • @artm: Du hast Recht, es gibt 4 Arten von Konstanten in C: Integer, Floating, Enumeration und Character.

    – Keith Thompson

    3. Februar 2016 um 17:00 Uhr

  • @underscore_d: Ja, die C++-Regel ist etwas komplizierter – aber in diesem speziellen Fall trifft sie zu. Ich habe einige Wieselwörter hinzugefügt.

    – Keith Thompson

    3. Februar 2016 um 17:05 Uhr

Benutzeravatar von hackks
hackt

Warum int x[n] ist wo falsch n ist ein const Wert?

n ist keine Konstante. const verspreche das nur n ist eine schreibgeschützte Variable, die während der Programmausführung nicht geändert werden sollte.
Beachten Sie, dass in c im Gegensatz zu c++ const Qualifizierte Variablen sind nicht konstant. Daher ist das deklarierte Array ein Array mit variabler Länge.
Sie können die Initialisierungsliste nicht verwenden, um Arrays mit variabler Länge zu initialisieren.

C11-§6.7.9/3:

Der Typ der zu initialisierenden Entität soll ein Array unbekannter Größe oder ein vollständiger Objekttyp sein kein Array-Typ mit variabler Länge.

Sie können verwenden #define oder enum zu machen n eine Konstante

#define n 5
int x[n] = { 1,1,3,4,5 };   

  • @ Yahia Farghaly n ist eine Variable, es ist constaber nicht konstant. const effektiv bedeutet schreibgeschützt in C.

    – chux – Wiedereinsetzung von Monica

    2. Februar 2016 um 19:23 Uhr


  • @chux: Tatsächlich ist es nicht garantiert, dass es schreibgeschützt ist. Es ist nur eine Garantie des Programmierers gegenüber dem Compiler. Der Compiler wird nicht in der Lage sein, alle Verstöße zu erkennen, und die Laufzeitumgebung muss dies auch nicht.

    – zu ehrlich für diese Seite

    2. Februar 2016 um 19:43 Uhr

  • @Oalf – Stimmt, es war eine fehlgeleitete übermäßige Vereinfachung für das OP.

    – chux – Wiedereinsetzung von Monica

    2. Februar 2016 um 19:52 Uhr

  • Hinweis: in C++, const-qualifizierte Variablen können konstante Ausdrücke sein oder nicht, z const int n = rand() % 5; int x[n]; ist in C korrekt, aber nicht in C++!

    – MM

    2. Februar 2016 um 21:28 Uhr

  • @artm; Sie können verwenden #define oder enum.

    – Hacken

    3. Februar 2016 um 10:40 Uhr

Wenn Sie ein Array vollständig initialisieren, ist es einfacher, sicherer und wartungsfreundlicher, den Compiler die Arraygröße ableiten zu lassen:

int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ; 

Um dann die Arraygröße zu ändern, müssen Sie nur die Anzahl der Initialisierer ändern, anstatt die Größe zu ändern und die Initialisiererliste zum Abgleichen. Besonders nützlich, wenn viele Initialisierer vorhanden sind.

Benutzeravatar von R Sahu
R Sahu

Wenngleich n ist ein const, können Sie es nicht verwenden, um die Größe eines Arrays zu definieren, es sei denn, Sie möchten ein VLA erstellen. Sie können jedoch keine Initialisierungsliste verwenden, um ein VLA zu initialisieren.

Verwenden Sie ein Makro, um ein Array mit fester Größe zu erstellen.

#define ARRAY_SIZE 5

int x[ARRAY_SIZE] = { 1,1,3,4,5 };

Unterscheidet sich Ihr Code semantisch von myfunc() hier:

void myfunc(const int n) { 
    int x[n] = { 1,1,3,4,5 };
    printf("%d\n", x[n-1]);
    *( (int *) &n) = 17;    //  Somewhat less "constant" than hoped...
    return ;
}

int main(){
    myfunc(4);
    myfunc(5);
    myfunc(6);  //  Haven't actually tested this.  Boom?  Maybe just printf(noise)?
    return 0;
}

Ist n wirklich so konstant? Wie viel Platz sollte der Compiler Ihrer Meinung nach zuordnen? x[] (da es die Aufgabe des Compilers ist, dies zu tun)?

Wie andere bereits betont haben, der Lebenslauf-Qualifizierer const tut nicht bedeutet “ein Wert, der während der Kompilierung und für alle Zeiten danach konstant ist”. Es bedeutet “ein Wert, den der lokale Code nicht ändern soll (obwohl er es kann)”.

1393210cookie-checkWarum ist int x[n] falsch, wo n ein konstanter Wert ist?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy