Der C11-Standard besagt, dass die Arrays in Größe und variabler Länge "einen Wert größer als Null haben sollen". Was ist die Rechtfertigung dafür, eine Länge von 0 nicht zuzulassen?
Insbesondere für Arrays mit variabler Länge ist es durchaus sinnvoll, ab und zu eine Größe von Null zu haben. Es ist auch nützlich für statische Arrays, wenn ihre Größe aus einem Makro oder einer Build-Konfigurationsoption stammt.
Interessanterweise bieten GCC (und Clang) Erweiterungen, die Arrays mit einer Länge von Null ermöglichen. Java erlaubt auch Arrays der Länge Null.
struct { int p[1],q[1]; } foo; int *pp = p+1;
,pp
wäre ein legitimer Zeiger,*pp
hätte aber keine eindeutige Adresse. Warum könnte dieselbe Logik bei einem Array mit Nulllänge nicht gelten? Sagen Sie, dass gegebeneint q[0];
innerhalb einer Struktur ,q
zu einer Adresse , deren Gültigkeit wie die der wäre beziehen würdep+1
Beispiel oben.Antworten:
Das Problem, auf das ich wetten würde, ist, dass C-Arrays nur Zeiger auf den Anfang eines zugewiesenen Speicherbereichs sind. Eine 0-Größe würde bedeuten, dass Sie einen Zeiger auf ... nichts haben? Du kannst nichts haben, also hätte es eine willkürliche Entscheidung geben müssen. Sie können nicht verwenden
null
, da Ihre Arrays mit der Länge 0 dann wie Nullzeiger aussehen würden. Und zu diesem Zeitpunkt wird jede andere Implementierung unterschiedliche willkürliche Verhaltensweisen auswählen, was zu Chaos führt.quelle
sizeof
0, und wie das Ärger verursachen würde. All dies kann mit den richtigen Konzepten und Begriffen erklärt werden, ohne an Kürze oder Klarheit zu verlieren. Das Verwechseln von Arrays und Zeigern birgt nur das Risiko, die Arrays zu verbreiten = Zeiger-Missverständnisse (was in anderen Zusammenhängen wichtiger ist), die keinen Nutzen bringen.Schauen wir uns an, wie ein Array normalerweise im Speicher angeordnet ist:
Beachten Sie, dass es kein separates Objekt gibt
arr
, das die Adresse des ersten Elements speichert. Wenn ein Array in einem Ausdruck erscheint, berechnet C die Adresse des ersten Elements nach Bedarf.Also, lassen Sie uns darüber nachdenken: ein 0-Element - Array haben würde keine Lagerung beiseite Satz für sie, was bedeutet , es gibt nichts , das Array - Adresse zu berechnen aus (anders ausgedrückt, gibt es keine Objektzuordnung für den Identifier). Es ist so, als würde man sagen: "Ich möchte eine
int
Variable erstellen , die keinen Speicher belegt." Es ist eine unsinnige Operation.Bearbeiten
Java-Arrays unterscheiden sich grundlegend von C- und C ++ - Arrays. Sie sind kein primitiver Typ, sondern ein Referenztyp, von dem sie abgeleitet sind
Object
.Bearbeiten 2
Ein in den Kommentaren unten dargestellter Punkt - Die Einschränkung "größer als 0" gilt nur für Arrays, deren Größe durch einen konstanten Ausdruck angegeben wird .
Eine VLA darf eine Länge von 0 haben. DasDeklarieren einer VLA mit einem 0-wertigen nicht konstanten Ausdruck ist keine Einschränkungsverletzung, ruft jedoch undefiniertes Verhalten auf.Es ist klar, dass VLAs andere Tiere sind als reguläre Arrays
, und ihre Implementierung kann eine Größe von 0 zulassen. Sie können nicht deklariertstatic
oder im Dateibereich gespeichert werden, da die Größe solcher Objekte bekannt sein muss, bevor das Programm gestartet wird.Es ist auch nichts wert, dass ab C11 keine Implementierungen erforderlich sind, um VLAs zu unterstützen.
quelle
T a[0]
alsT *a
, aber warum nicht einfach verwendenT *a
?struct
Hack. Ich habe es nie persönlich benutzt; Ich habe nie an einem Problem gearbeitet, das einenstruct
Typ mit variabler Größe benötigte .Normalerweise möchten Sie, dass das Array mit der Größe Null (tatsächlich variabel) zur Laufzeit die Größe kennt. Dann packe das in ein
struct
und verwende flexible Array-Mitglieder , wie zB:Offensichtlich muss das flexible Array-Mitglied das letzte sein,
struct
und Sie müssen vorher etwas haben. Häufig hängt dies mit der tatsächlichen Laufzeitlänge dieses flexiblen Arraymitglieds zusammen.Natürlich würden Sie zuordnen:
AFAIK, das war schon in C99 möglich und es ist sehr nützlich.
Übrigens gibt es in C ++ keine flexiblen Array-Mitglieder (da es schwierig ist zu definieren, wann und wie sie erstellt und zerstört werden sollen). Siehe jedoch den zukünftigen std :: dynarray
quelle
Wenn der Ausdruck
type name[count]
in einer Funktion geschrieben ist, weisen Sie den C-Compiler an, die Stack-Frame-sizeof(type)*count
Bytes zuzuweisen und die Adresse des ersten Elements im Array zu berechnen.Ist der Ausdruck
type name[count]
außerhalb aller Funktionen und Strukturdefinitionen geschrieben wird, weisen Sie den C-Compiler an, die Datensegmentbytes zuzuweisensizeof(type)*count
und die Adresse des ersten Elements im Array zu berechnen.name
Tatsächlich ist es ein konstantes Objekt, das die Adresse des ersten Elements im Array speichert. Jedes Objekt, das eine Adresse eines Speichers speichert, wird als Zeiger bezeichnet. Aus diesem Grund behandeln Sie diesenname
als Zeiger und nicht als Array. Beachten Sie, dass auf Arrays in C nur über Zeiger zugegriffen werden kann.Wenn
count
es sich um einen konstanten Ausdruck handelt, der zu Null ausgewertet wird, weisen Sie den C-Compiler an, entweder im Stack-Frame oder im Datensegment Null-Bytes zuzuweisen und die Adresse des ersten Elements im Array zurückzugeben. Das Problem dabei ist jedoch, dass das erste Element ist Array mit der Länge Null existiert nicht und Sie können die Adresse von etwas, das nicht existiert, nicht berechnen.Dies ist rational das Element Nr.
count+1
existiert nicht imcount
-length-Array, daher verbietet der C-Compiler, ein Array mit der Länge Null als Variable innerhalb und außerhalb einer Funktion zu definieren. Worin besteht dann der Inhaltname
? Welche Adressename
speichert genau?Wenn
p
es sich um einen Zeiger handelt, ist der Ausdruckp[n]
äquivalent zu*(p + n)
Wenn das Sternchen * im rechten Ausdruck eine dereferenzierte Operation des Zeigers ist, dh auf den Speicher zugreifen, auf den verwiesen wird,
p + n
oder auf den Speicher, in dem die Adresse gespeichert istp + n
, undp + n
Zeigerausdruck, nimmt er die Adresse vonp
undn
multipliziert die Zahl mit dieser Adresse Größe des Zeigertypsp
.Ist es möglich, eine Adresse und eine Nummer hinzuzufügen?
Ja, das ist möglich, da es sich bei der Adresse um eine vorzeichenlose Ganzzahl handelt, die üblicherweise in hexadezimaler Schreibweise dargestellt wird.
quelle
N
hatN+1
Adressen zugeordnet, die ersteN
von denen einzigartige Bytes und die letzte identifizieren ,N
von denen gerade hinter einer jener Bytes jeden Punkt. Eine solche Definition würde auch in dem entarteten Fall gut funktionieren, in demN
0 ist.Wenn Sie einen Zeiger auf eine Speicheradresse möchten, deklarieren Sie einen. Ein Array zeigt tatsächlich auf einen Speicherbereich, den Sie reserviert haben. Arrays verwandeln sich in Zeiger, wenn sie an Funktionen übergeben werden, aber wenn sich der Speicher, auf den sie zeigen, auf dem Heap befindet, ist dies kein Problem. Es gibt keinen Grund, ein Array der Größe Null zu deklarieren.
quelle
Aus den Tagen des ursprünglichen C89, als ein C-Standard festlegte, dass etwas undefiniertes Verhalten aufwies, bedeutete dies "Was auch immer eine Implementierung auf einer bestimmten Zielplattform am besten für den beabsichtigten Zweck geeignet machen würde". Die Autoren des Standards wollten nicht erraten, welches Verhalten für einen bestimmten Zweck am besten geeignet ist. Bestehende C89-Implementierungen mit VLA-Erweiterungen hatten möglicherweise ein anderes, aber logisches Verhalten, wenn eine Größe von Null angegeben wurde (z. B. haben einige das Array möglicherweise als Adressausdruck behandelt, der NULL ergibt, während andere es als Adressausdruck behandeln, der der Adresse von entspricht eine andere willkürliche Variable, die aber ohne weiteres mit Null versehen werden kann, ohne dass ein Trapping erfolgt). Wenn sich ein Code auf ein derart unterschiedliches Verhalten verlassen hätte, würden die Autoren des Standards nicht
Anstatt zu erraten, was Implementierungen bewirken könnten, oder anzunehmen, dass ein Verhalten einem anderen überlegen sein sollte, erlaubten die Autoren des Standards den Implementierern lediglich , den Fall nach eigenem Ermessen zu beurteilen . Implementierungen, die hinter den Kulissen malloc () verwenden, können die Adresse des Arrays als NULL behandeln (wenn malloc mit der Größe Null null ergibt), diejenigen, die Stapeladressenberechnungen verwenden, können einen Zeiger ergeben, der mit der Adresse einer anderen Variablen übereinstimmt, und einige andere Implementierungen können dies tun andere Dinge. Ich glaube nicht, dass sie damit gerechnet haben, dass Compiler-Autoren alles daran setzen, dass sich der Eckfall mit der Größe Null absichtlich nutzlos verhält.
quelle