Kann eine konstante Variable verwendet werden, um die Größe eines Arrays in C zu deklarieren?

Lesezeit: 7 Minuten

Benutzeravatar von user1901196
Benutzer1901196

Warum löst der folgende Code einen Fehler aus?

const int a = 5;
int b[a]={1,2,3,4,5};

Und auch als ich versuchte, den obigen Code ohne das Schlüsselwort “const” zu kompilieren, bekam ich den gleichen Fehler:

int a = 5; 
int b[a]={1,2,3,4,5};

Wieso ist es so? Was ist der Fehler, den ich hier mache?

Und noch eine Frage: Wann werden Konstanten in einem Code durch ihre tatsächlichen Werte ersetzt, dh wenn ich eine Variable deklariere sage: const int x= 5; Ich weiß, dass im RAM für die Variable x kein Speicher zugewiesen wird, aber der konstante Variablenbereich im ROM den Wert 5 enthält und dass x einfach durch den Wert 5 ersetzt wird, wo immer x im Code vorkommt. Aber wann passiert das? Zusammenstellungszeit? Startzeit? Vorlaufzeit?

PS: Ich spreche von Embedded C (das auf einem Mikrocontroller usw. läuft), nicht von C, das auf meinem Desktop läuft. Das eingebettete System muss also ein ROM (Flash, EEPROM …) haben. Was würde dann passieren?

  • Zusammenstellungszeit. Übrigens ist ROM wie DVDs und so, es gibt kein eingebautes ROM in einem PC (wann würde es überhaupt bestückt werden?)

    – Walerij

    17. September 2013 um 11:27 Uhr

  • @Vprimachenko: OT: Könnte das BIOS nicht als ROM betrachtet werden? Zumindest dort, wo man es tagelang aus der Steckdose ziehen musste, um es neu zu schreiben.

    – alk

    17. September 2013 um 12:57 Uhr


  • Sie könnten statt einer Konstante auch eine Aufzählung verwenden.

    – Simon Elliot

    17. September 2013 um 14:14 Uhr

  • Dies wäre eine bessere Frage, wenn es das Anti-Pattern vermeiden würde, eine Größe sowohl als die Länge der Initialisierungsliste redundant anzugeben und als Wert in []. Lassen Sie in diesem Fall einfach die [] leer und tun static const size_t size = sizeof(arr)/sizeof(arr[0]); nach die Array-Deklaration. Dies gilt global. Ein besseres Beispiel wäre arr[size] = {} um ein nullinitialisiertes Array einiger Größe zu deklarieren.

    – Peter Cordes

    13. September 2020 um 14:17 Uhr

  • @Razzle: Link-Time-Optimierung würde alle Kompilierungseinheiten das sehen lassen extern const size_t myArraySize; als konstanter Wert zur Kompilierzeit. Betreff: Initialisierungsreihenfolge: Vielleicht denken Sie an das Problem der Laufzeitinitialisierungsreihenfolge von C++? Ich glaube nicht, dass C dieses Problem hat, und schon gar nicht hier: die Definition von myArraySize wird einen Kompilierzeitkonstanten-Initialisierer haben, sodass alle Verweise darauf von anderen Kompilierungseinheiten nur auf den Wert im statischen Nur-Lese-Speicher verweisen. (Wenn Sie keine Link-Time-Optimierung verwenden, damit sie den Wert einbetten können.)

    – Peter Cordes

    30. November 2020 um 20:27 Uhr

Benutzeravatar von Kerrek SB
Kerrek SB

Es ist einfach eine Beschränkung der Sprache. Die Größen von statisch begrenzten Arrays müssen sein konstante Ausdrückeund leider ist das in C nur so etwas wie eine wörtliche Konstante oder a sizeof Ausdruck oder dergleichen, aber nicht a const-typisierte Variable.

(Wie Simon betonte, gibt es seit C99 auch laufzeitbegrenzt Arrays oder “Arrays variabler Länge”, deren Größe durch den Wert einer beliebigen Variablen angegeben werden kann. Aber das ist ein anderes Tier.)

Vielleicht interessiert es Sie zu hören, dass die Regeln in C++ anders sind, wo a static const int ist in der Tat ein konstanter Ausdruck, und C++11 fügt sogar ein neues Schlüsselwort hinzu, constexprum eine noch allgemeinere Verwendung konstanter Ausdrücke zu ermöglichen, die mehr Dinge umfassen, deren Wert “zur Kompilierzeit vernünftigerweise bestimmt werden könnte”.

  • Die Regeln sind auch in C99 anders, obwohl das Array als dynamisch betrachtet wird.

    – Simon Elliot

    17. September 2013 um 11:27 Uhr

  • @SimonElliott: Was sind die Regeln für C99? Oder noch wichtiger, für C11? (Oder hast du dich nur auf VLAs bezogen?)

    – Kerrek SB

    17. September 2013 um 13:17 Uhr


  • : Ich war gerade bei VLAs

    – Simon Elliot

    17. September 2013 um 14:13 Uhr


Benutzeravatar von Jens
Jens

In C, const ist eine falsche Bezeichnung für schreibgeschützt. const Variablen können ihren Wert ändern, zB ist es völlig in Ordnung zu deklarieren

const volatile int timer_tick_register; /* A CPU register. */

die Sie lesen und bei jedem Lesen einen anderen Wert erhalten, aber nicht schreiben können. Die Sprachspezifikation behandelt also const Qualifizierte Objekte nicht wie konstante Ausdrücke geeignet für Array-Größen.

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

2 Hauptalternativen zu VLA: enum und Makros

Mit einem anonymen enum:

enum { N = 5 };
int is[N];

wie in:

#include <stdio.h>

enum { N = 5 };
char is[N];

int main(void) {
    printf("%ju\n", sizeof(is));
}

Dies funktioniert, weil Enum-Mitglieder konstante Ausdrücke sind: Kann Enum-Mitglied die Größe eines Arrays in ANSI-C haben?

Mit Makros:

#define N 5
int is[N];

Der Vorteil von enums ist das enums haben einen Gültigkeitsbereich und sind Teil des Kompilierungsschritts, sodass sie möglicherweise auch zu besseren Fehlermeldungen führen.

Der Vorteil von Makros besteht darin, dass Sie mehr Kontrolle über den Typ der Konstanten haben (z #define N 1 vs #define N 1u), während enums sind auf einen implementierungsdefinierten Typ festgelegt: Ist sizeof(enum) == sizeof(int), immer? Aber das spielt in diesem Fall keine große Rolle.

Getestet auf Ubuntu 21.04, GCC 10.3.0, gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c.

Warum VLA vermeiden

  • sind nicht in C89 und nur optional in C11
  • kann einen Overhead verursachen: Gibt es einen Overhead für die Verwendung von Arrays mit variabler Länge? (wahrscheinlich wenig)

  • “Aber Sie sollten die Verwendung von Arrays mit variabler Länge vermeiden, da sie einen Overhead verursachen können.” – Welche Gemeinkosten?

    – MM

    18. Mai 2015 um 21:27 Uhr

  • @MattMcNabb mit Link zu stackoverflow.com/questions/2034712/… aktualisiert, in dem die Top-Antworten besagen, dass es möglicherweise Overhead gibt

    – Ciro Santilli OurBigBook.com

    18. Mai 2015 um 21:46 Uhr

  • Diese Antwort ist die einzige mit einer Lösung (enum statt #define – igitt).

    – ktn

    2. Juni 2016 um 12:12 Uhr

  • @ctn ja, die endlosen Freuden von C. Aber ich bin “zuversichtlich” constexpr wird es in C2x schaffen 🙂

    – Ciro Santilli OurBigBook.com

    2. Juni 2016 um 12:19 Uhr

  • Sie könnten untagged enum verwenden, um eine Verschmutzung des Namespace zu vermeiden: enum { N = 5 };

    – tstanisl

    1. Oktober 2021 um 16:14 Uhr

Benutzeravatar von Jimbo
Jimbo

BEARBEITEN: Lesen Sie einfach auf Wikipedia, dass C11 Arrays mit variabler Länge in ein verbannt hat Optional feature 🙁 Doh! Die erste Hälfte des Beitrags ist vielleicht nicht so nützlich, aber die zweite Hälfte beantwortet einige Ihrer anderen Fragen 🙂

Als Ergänzung zum Beitrag von Kerrek SB hat C99 (ISO/IEC 9899:1999) das Konzept von a Array mit variabler Länge. Die Norm gibt folgendes Beispiel:

#include <stddef.h>
size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}

Das sizeof Operator wird wie folgt erweitert:

Der sizeof-Operator liefert die Größe (in Byte) seines Operanden, der ein Ausdruck oder der eingeklammerte Name eines Typs sein kann. Die Größe wird durch den Typ des Operanden bestimmt. Das Ergebnis ist eine ganze Zahl. Wenn der Typ des Operanden ein Array-Typ mit variabler Länge ist, wird der Operand ausgewertet; Andernfalls wird der Operand nicht ausgewertet und das Ergebnis ist eine ganzzahlige Konstante.

Ein weiteres schönes Beispiel finden Sie unter Wikipedia.

Beachten Sie, dass statisch deklarierte keine Arrays mit variabler Länge sein können.

Zu einigen deiner anderen Fragen:

F: Wann werden Konstanten in einem Code durch ihre tatsächlichen Werte ersetzt?

Wenn die Konstante eine konstante Variable ist, kann sie niemals “ersetzt” werden und es könnte immer auf sie als Speicherbereich zugegriffen werden. Dies liegt daran, dass der address-of-Operator & muss noch mit der Variable arbeiten. Wenn die Variablenadresse jedoch nie verwendet wird, kann sie “ersetzt” werden und es wird kein Speicher zugewiesen. Aus dem C-Standard:

Die Implementierung kann ein konstantes Objekt, das nicht flüchtig ist, in einem Nur-Lese-Speicherbereich platzieren. Darüber hinaus muss die Implementierung einem solchen Objekt keinen Speicher zuweisen, wenn seine Adresse nie verwendet wird.

Nächste Frage…

F: Ich weiß, dass der Variablen x kein Speicher im RAM zugewiesen wird, aber der konstante Variablenbereich im ROM den Wert 5 enthält

Dies hängt von Ihrem System ab. Wenn Sie ein ROM haben und der Compiler weiß, wo sich das ROM befindet, kann es durchaus im ROM abgelegt werden. Wenn es kein ROM gibt, ist die einzige Wahl, die der Compiler (naja Linker wirklich) haben wird, RAM.

F: x wird einfach überall dort durch den Wert 5 ersetzt, wo x im Code vorkommt. Aber wann passiert das? Zusammenstellungszeit? Startzeit? Vorlaufzeit?

Wie bereits erwähnt, hängt dies eher davon ab, wie die Konstante verwendet wird. Wenn die Adresse der konstanten Variablen nie verwendet wird und der Compiler clever genug ist, dann zur Kompilierungszeit. Andernfalls tritt der “Ersatz” nie auf und es ist ein Wert mit einer Position im Speicher; In diesem Fall erfolgt die Platzierung der Variablen im Speicher zur Verbindungszeit. Es wird noch nie während der Vorverarbeitung auftreten.

Vielleicht ist es erwähnenswert, hier könnten Sie verwenden

int b[] = {1, 4, 5};

Falls Sie eine Anzahl von Elementen benötigen

 size_t sz = sizeof(b)/sizeof(b[0]);

Ich glaube, es liegt an der Toolkette, zu entscheiden, wo Konstanten, Flash oder RAM gespeichert werden sollen

1413650cookie-checkKann eine konstante Variable verwendet werden, um die Größe eines Arrays in C zu deklarieren?

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

Privacy policy