Variabel modifiziertes Array im Dateibereich

85

Ich möchte ein konstantes statisches Array erstellen, das in meiner Objective-C-Implementierungsdatei verwendet wird, ähnlich wie auf der obersten Ebene meiner ".m" -Datei:

static const int NUM_TYPES = 4;
static int types[NUM_TYPES] = { 
  1,
  2, 
  3, 
  4 };

Ich habe vor, es NUM_TYPESspäter in der Datei zu verwenden, also wollte ich es in eine Variable einfügen.

Wenn ich dies tue, erhalte ich jedoch den Fehler

"Variabel geänderte 'Typen' im Dateibereich"

Ich habe festgestellt, dass dies möglicherweise damit zu tun hat, dass die Arraygröße eine Variable ist (ich erhalte diese Meldung nicht, wenn ich dort ein ganzzahliges Literal eingebe, wie z static int types[4]. B. ).

Ich möchte das beheben, aber vielleicht mache ich alles falsch ... Ich habe hier zwei Ziele:

  1. Um ein Array zu haben, auf das in der gesamten Datei zugegriffen werden kann
  2. In NUM_TYPESeine Variable einkapseln , damit nicht dasselbe Literal über verschiedene Stellen in meiner Datei verteilt ist

Irgendwelche Vorschläge?

[BEARBEITEN] Gefunden in der C-FAQ: http://c-faq.com/ansi/constasconst.html

Sam
quelle
2
Was passiert, wenn Sie es stattdessen als Definition ausführen? #define kNUM_TYPES 4?
Jorge Israel Peña
Das funktioniert ... aus irgendeinem Grund habe ich versucht, mich von der Verwendung des Präprozessors fernzuhalten, weil ich dachte, ich erinnere mich daran, dass ich das irgendwo gelesen habe, aber ich habe nur etwas mehr recherchiert und konnte keinen guten Grund finden, ihn in diesem Fall nicht zu verwenden. Ich denke, es ist möglicherweise weniger wünschenswert, wenn ich Objekte im Präprozessor erstelle (wie @"An NSString literal"). Das einzige, was an Ihrem Code falsch ist, ist, dass das Semikolon nicht benötigt wird.
Sam
Ah ja, danke für das Heads-up und froh, dass ich helfen konnte.
Jorge Israel Peña

Antworten:

62

Der Grund für diese Warnung ist, dass const in c nicht konstant bedeutet. Es bedeutet "schreibgeschützt". Der Wert wird also an einer Speicheradresse gespeichert und kann möglicherweise durch Maschinencode geändert werden.

larsr
quelle
3
Das Ändern eines definierten Objekts const(z. B. durch Wegwerfen constvon einem Zeiger und Speichern eines Werts) ist ein undefiniertes Verhalten. Daher ist der Wert eines solchen Objekts eine Kompilierungs- oder Laufzeitkonstante (abhängig von der Speicherdauer). Der Wert kann nicht in einem konstanten Ausdruck verwendet werden, nur weil der C-Standard dies nicht sagt. (Das Wegwerfen constund Speichern eines Werts ist zulässig, wenn das Zielobjekt ohne definiert constoder dynamisch zugewiesen ist. Zeichenfolgenliterale werden nicht geschrieben const, können aber nicht beschrieben werden.)
jilles
3
@jilles "könnte möglicherweise durch Maschinencode geändert werden" bedeutet nicht, dass der Autor dieser Antwort "könnte möglicherweise durch C-Code geändert werden" bedeutet. Darüber hinaus hat dies einen weiteren sehr guten Grund: Es kann externKonstanten in verschiedenen TUs geben, deren Wert beim Kompilieren der aktuellen TU nicht bekannt ist.
14
Eine Möglichkeit, diese Antwort zu verbessern, besteht darin, zu zeigen, wie dieses Problem behoben werden kann.
George Stocker
32

Wenn Sie den Präprozessor gemäß den anderen Antworten trotzdem verwenden NUM_TYPESmöchten, können Sie den Compiler veranlassen, den Wert von automatisch zu bestimmen:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
caf
quelle
Wow, das ist wirklich cool ... Ich wusste nicht, dass das möglich ist. Ich gehe davon aus, dass die Kosten für diese Berechnung vernachlässigbar sind. Darf ich auch davon ausgehen, dass ein Compiler dies auf einen statischen Wert optimieren könnte?
Sam
2
Ja, das Ergebnis sizeofsolcher Objekte ist eine Konstante zur Kompilierungszeit.
Café
21
#define NUM_TYPES 4
Jim Buck
quelle
11

Es ist auch möglich, die Aufzählung zu verwenden.

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
Dave L Delaney
quelle
4

Wie bereits in anderen Antworten erläutert, constbedeutet C in C lediglich, dass eine Variable schreibgeschützt ist. Es ist immer noch ein Laufzeitwert. Sie können jedoch eine enumals echte Konstante in C verwenden:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};
CygnusX1
quelle
3

Imho ist dies ein Fehler in vielen C-Compilern. Ich weiß, dass die Compiler, mit denen ich gearbeitet habe, keine "statische const" -Variable an einer Adresse speichern, sondern die Verwendung im Code durch die sehr konstante ersetzen. Dies kann überprüft werden, da Sie dieselbe Prüfsumme für den erzeugten Code erhalten, wenn Sie eine Präprozessor-Direktive #define verwenden und wenn Sie eine statische const-Variable verwenden.

In beiden Fällen sollten Sie nach Möglichkeit statische const-Variablen anstelle von #defines verwenden, da die statische const typsicher ist.

hans lepoeter
quelle
Das klingt ziemlich schlecht, da Sie die Adresse einer static constVariablen übernehmen können. Das Verhalten, das Sie beschreiben, ist möglicherweise eine gültige Optimierung, aber es kann sicherlich nicht immer funktionieren.
Entspannen Sie
Es ist eigentlich in Ordnung. Es ist für den C-Compiler in Ordnung, einzelne Verwendungen von globalen Konstantenvariablen nach Möglichkeit durch den konstanten Wert zu ersetzen. Wenn alle Verweise auf eine Variable in Konstanten konvertiert werden, kann der Compiler diese vollständig entfernen. Wenn Sie die Adresse irgendwo verwenden, wird sie nicht entfernt. Nichts davon ändert, dass C gemäß dem Sprachstandard keine globalen Arrays mit einer Variablen als Größe zulässt, unabhängig davon, ob die Variable const ist oder nicht.
Evan