Ich verstehe, dass die Hardware die während der Programmausführung zugewiesene Speichermenge begrenzt. Allerdings ist meine Frage ohne Rücksicht auf die Hardware. Angenommen, es gäbe keine Begrenzung für die Speichermenge, gäbe es dann keine Begrenzung für das Array?
Was ist die maximale Größe eines Arrays in C?
Nyxm
Keith Thompson
Es gibt kein Fest Begrenzung der Größe eines Arrays in C.
Die Größe jedes einzelnen Objekts, einschließlich jedes Array-Objekts, ist begrenzt durch SIZE_MAX
der maximale Wert des Typs size_t
das ist das Ergebnis der sizeof
Operator. (Es ist nicht ganz klar, ob der C-Standard Objekte erlaubt, die größer sind als SIZE_MAX
Bytes, aber in der Praxis werden solche Objekte nicht unterstützt; siehe Fußnote.) Seit SIZE_MAX
wird durch die Implementierung bestimmt und kann von keinem Programm geändert werden, das eine Obergrenze von vorschreibt SIZE_MAX
Bytes für ein einzelnes Objekt. (Das ist eine Obergrenze, nicht zuletzt eine Obergrenze; Implementierungen können kleinere Grenzen auferlegen und tun dies normalerweise auch.)
Die Breite des Typs void*
ein generischer Zeigertyp, erlegt der Gesamtgröße aller Objekte in einem ausgeführten Programm eine Obergrenze auf (die größer sein kann als die maximale Größe eines einzelnen Objekts).
Der C-Standard erlegt diesen festen Größen Untergrenzen, aber keine Obergrenzen auf. Keine konforme C-Implementierung kann Objekte unendlicher Größe unterstützen, aber im Prinzip kann sie Objekte jeder endlichen Größe unterstützen. Obergrenzen werden durch einzelne C-Implementierungen, durch die Umgebungen, in denen sie arbeiten, und durch die Physik, nicht durch die Sprache, auferlegt.
Beispielsweise könnte eine konforme Implementierung haben SIZE_MAX
gleich 21024-1, was bedeutet, dass es möglich ist allgemein gesagt have objects up to 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 bytes.
Viel Glück beim Finden von Hardware, die solche Objekte tatsächlich unterstützt.
Fußnote: Es gibt keine explizite Regel, dass kein Objekt größer sein darf als SIZE_MAX
Byte. Sie konnten das nicht sinnvoll anwenden sizeof
Operator zu einem solchen Objekt, aber wie jeder andere Operator, sizeof
kann überlaufen; Das bedeutet nicht, dass Sie an einem solchen Objekt keine Operationen ausführen können. Aber in der Praxis wird jede vernünftige Implementierung machen size_t
groß genug, um die Größe jedes unterstützten Objekts darzustellen.
-
@JCLeitão: Im Prinzip könnte ein Compiler eine Bibliothek mit beliebiger Genauigkeit verwenden, um sehr breite Integer-Typen zu implementieren. Aber das können Sie als Programmierer nicht; Tatsächliche Integer-Typen (mit Literalen, Operatoren usw.) sind auf das beschränkt, was der Compiler bereitstellt.
– Keith Thompson
28. November 2012 um 6:08 Uhr
-
tut die Existenz von weite Hinweise und das entsprechende Speichermodell Ihre Antwort in irgendeiner Weise ändern?
– jfs
17. Februar 2014 um 19:05 Uhr
-
@JFSebastian: Ich habe es nie benutzt
far
Zeiger selbst, aber ich verstehe, dass sie sowohl die Segmentnummer als auch den Offset innerhalb des Segments enthalten. Wenn ein 16-Bit-Zeiger auf einen Adressraum von 20 Bit zugreifen kann, müssen an anderer Stelle mindestens 4 Bit implizite Informationen vorhanden sein, was meines Wissens genau das ist, was es istfar
Zeiger nicht tun. In einer konformen C-Implementierung avoid*
kann jedes Byte jedes aktuell existierenden Objekts eindeutig adressieren.– Keith Thompson
17. Februar 2014 um 21:12 Uhr
-
@caot: Ihr Array ist in einer Funktion ohne die definiert
static
Schlüsselwort, hat also eine automatische Speicherdauer. Bei den meisten Implementierungen werden solche Objekte auf dem Stapel zugewiesen. Es ist wahrscheinlich, dass Ihr System die Größe des Stapels begrenzt. Verwendenstatic
oder global definieren, oder verwendenmalloc()
.– Keith Thompson
1. April 2017 um 0:03 Uhr
-
@MM: Es gibt nichts im Standard, das Objekte verbietet, die größer sind als
SIZE_MAX
Bytes, aber in der Praxis wird jede vernünftige Implementierung, die Objekte unterstützen kann, die größer als beispielsweise 2 ** 32 Bytes sindsize_t
breiter als 32 Bit. Eine Implementierung, mit der Sie so große Objekte definieren können, sich aber nicht die Mühe macht, sie zu erstellensize_t
groß genug, um ihre Größe zu halten, könnte konform sein, aber es wäre pervers.– Keith Thompson
1. April 2017 um 0:06 Uhr
Ciro Santilli OurBigBook.com
C99 5.2.4.1 “Übersetzungsgrenzen” minimale Größe
Die Implementierung muss in der Lage sein, mindestens ein Programm zu übersetzen und auszuführen, das mindestens eine Instanz jeder der folgenden Grenzen enthält: 13)
- 65535 Bytes in einem Objekt (nur in einer gehosteten Umgebung)
- Implementierungen sollten nach Möglichkeit vermeiden, feste Übersetzungsgrenzen aufzuerlegen.
Dies deutet darauf hin, dass eine konforme Implementierung sich weigern könnte, ein Objekt (das Arrays enthält) mit mehr als zu kompilieren short
Bytes.
PTRDIFF_MAX
legt auch einige Grenzen für Array sagt fest
Der C99-Standard 6.5.6 Additive Operatoren sagt:
9 Wenn zwei Zeiger subtrahiert werden, müssen beide auf Elemente desselben Array-Objekts zeigen oder um eins nach dem letzten Element des Array-Objekts; das Ergebnis ist die Differenz der Indizes der beiden Array-Elemente. Die Größe des Ergebnisses ist implementierungsdefiniert, und sein Typ (ein vorzeichenbehafteter ganzzahliger Typ) ist es
ptrdiff_t
definiert in der<stddef.h>
Header. Wenn das Ergebnis in einem Objekt dieses Typs nicht darstellbar ist, ist das Verhalten undefiniert.
Was für mich bedeutet, dass Arrays größer als sind ptrdiff_t
sind theoretisch erlaubt, aber dann können Sie die Differenz ihrer Adressen nicht portabel nehmen.
Vielleicht scheint GCC Sie aus diesem Grund nur darauf zu beschränken ptrdiff_t
. Dies wird auch erwähnt unter: Warum ist die maximale Größe eines Arrays “zu groß”?
Experimente
Vielleicht kommt es letztendlich darauf an, was Ihr Compiler akzeptiert, also los geht’s:
Haupt c
#include <stdint.h>
TYPE a[(NELEMS)];
int main(void) {
return 0;
}
Größen.c
#include <stdint.h>
#include <stdio.h>
int main(void) {
printf("PTRDIFF_MAX 0x%jx\n", (uintmax_t)PTRDIFF_MAX);
printf("SIZE_MAX 0x%jx\n", (uintmax_t)SIZE_MAX);
return 0;
}
Und dann versuchen wir zu kompilieren mit:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o sizes.out sizes.c
./sizes.out
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out \
-DNELEMS='((2lu << 62) - 1)' -DTYPE=uint8_t main.c
Ergebnisse:
-
PTRDIFF_MAX: 0x7fffffffffffffff = 2^63 – 1
-
SIZE_MAX: 0xffffffffffffffff = 2^64 – 1
-
-DNELEMS='((2lu << 62) - 1)' -DTYPE=uint8_t
: kompiliert (== 2^63 – 1). Wenn ich es auf meinem System mit nur 32 GB RAM ausführe, tritt sofort ein Segfault auf 🙂 -
-DNELEMS='(2lu << 62)' -DTYPE=uint8_t
: Kompilierung schlägt fehl mit:error: size of array ‘a’ is too large
-
-DNELEMS='(2lu << 62 - 1)' -DTYPE=uint16_t
: Kompilierung schlägt fehl mit:error: size ‘18446744073709551614’ of array ‘a’ exceeds maximum object size ‘9223372036854775807’
wo
9223372036854775807 == 0x7fffffffffffffff
Daraus verstehen wir, dass GCC zwei Einschränkungen mit unterschiedlichen Fehlermeldungen auferlegt:
- Anzahl der Elemente darf 2^63 nicht überschreiten (passiert mit == PTRDIFF_MAX)
- Arraygröße darf 2^63 nicht überschreiten (passiert auch == PTRDIFF_MAX)
Getestet auf Ubuntu 20.04 amd64, GCC 9.3.0.
Siehe auch
- Gibt es Größenbeschränkungen für C-Strukturen?
- Was ist der richtige Typ für Array-Indizes in C?
-
gcc unterstützt auch keine dynamischen Arrays, die größer als die halbe Zeigerbreite sind. Beachten Sie, dass die Ergebnisse der Zeigersubtraktion durch die Objektgröße skaliert werden, also theoretisch ein 3-GiB-Array von
int
auf einem ILP32-Ziel sollte funktionieren, undend - start
sollte geben3 * 1024**3 / 4
berechnet ohne Überlauf, weil der C-Standard sagt, dass es nicht UB ist, wenn das Endergebnis darstellbar ist. Aber gcc gibt Code aus, der eine Subtraktion mit Zeigerbreite durchführt und diese dann arithmetisch nach rechts verschiebt, wodurch der Übertrag aus der Subtraktion verloren geht. godbolt.org/g/NG6zZ6.– Peter Cordes
22. März 2018 um 14:35 Uhr
-
Ich habe mit getestet
-m32
und bekam-536870896
(Zeigersubtraktion) vs.536870928
(Handbuch). Glibcmalloc
war für eine Zuweisung von 2 GiB + 16 im 32-Bit-Benutzerraum auf x86 (unter einem 64-Bit-Kernel) erfolgreich. Es schlug jedoch bei einer 3GiB-Zuweisung fehl. Wie auch immer, anscheinend ist dies “kein Fehler”, da gcc PTRDIFF_MAX als maximale Objektgröße betrachtet: developer.redhat.com/blog/2017/02/22/….-Walloc-size-larger-than=PTRDIFF_MAX
wird durch aktiviert-Wall
und ich habe diese Warnung beim Kompilieren meines Testprogramms erhalten.– Peter Cordes
22. März 2018 um 14:37 Uhr
-
Ist diese Grenze (
PTRDIFF_MAX
) die Grenze in der Anzahl der Elemente oder in Bytes? Aus meiner Sicht ist das eine Grenze in Elementen, aber wenn die Elemente groß genug sind, könnten Sie leicht zuerst die harte Grenze in Bytes erreichen (SIZE_MAX
). Das wäre jedoch gefährlich, da Funktionen, die auf dieses Array zugreifen, über achar *
könnte problematisch sein.– alx
24. Juli 2020 um 15:40 Uhr
-
@CacahueteFrito Mein Gedanke ist, dass Sie den Zeiger der größeren Größe auf uint8_t umwandeln können, und dann wären Sie in der Lage, Unterschiede zu erreichen, die größer als PTRDIFF_MAX sind. Nur eine Heuristik, warum GCC zu tun scheint, was es tut. Ich habe die Experimente jetzt etwas verbessert.
– Ciro Santilli OurBigBook.com
25. Juli 2020 um 9:20 Uhr
-
@alx: Es ist in Bytes. Der Grund dafür ist, dass die Pointer-Subtraktion genau sein muss, wenn sie den Endwert nicht überläuft, selbst wenn der Byte-Abstand nicht passen würde (wenn die Pointer gecastet worden wären).
char*
vor dem Abzug). Das Festlegen eines Implementierungslimits bedeutet, dass GCC implementieren kannend - start
für breitere Typen als rohe ganzzahlige Subtraktion der Zeiger und Division durchsizeof(T)
(natürlich mit einer Verschiebung oder einer multiplikativen Umkehrung). Wenn das auf einem 32-Bit-System selbst für Zeiger mit einem Abstand von 2,1 GiB genaue Ergebnisse liefern müsste, wäre ext prec erforderlich. oder drehen Sie die Carry-Flagge nach innen, um durch 2 zu teilen.– Peter Cordes
27. November 2021 um 12:25 Uhr
Unabhängig vom Arbeitsspeicher ist die maximale Größe eines Arrays durch den Integertyp begrenzt, der zum Indizieren des Arrays verwendet wird.
-
When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand
(C11 n1570, Abschnitt 6.5.6 Additive Operatoren). Wenn Sie also einen Integer-Typ verwenden, der breiter als ein Zeiger ist, dürfen Sie nicht auf mehr Speicher zugreifen, als Sie sonst hätten. (Erinnere dich daranarr[idx]
ist genau gleichbedeutend mit*((arr) + (idx))
und so definiert der C-Standard die[]
Array-Subscript-Operator.) Wie auch immer, das ist eine Grenze, die alle C-Implementierungen gemeinsam haben, aber in der Praxis haben Implementierungen kleinere Grenzen, wie PTRDIFF_MAX für gcc.– Peter Cordes
22. März 2018 um 15:08 Uhr
Ein 64-Bit-Rechner könnte theoretisch maximal 2^64 Byte Speicher adressieren.
Ich denke, das größte theoretische Array wäre der maximale Wert von “unsigned long” (oder was auch immer die größte ganze Zahl ist, die der neueste Standard / Ihr Compiler unterstützt).
-
Nicht, wenn das breiter als ein Zeiger ist. (z.B
long long
bei einer Implementierung mit 32-Bit-Zeigern).– Peter Cordes
22. März 2018 um 15:10 Uhr
Frauref
Die Größe des Zeigers begrenzt den Speicher, auf den Sie zugreifen können. Selbst wenn die Hardware Unterstützung für unbegrenzten Speicher bietet, können Sie nur auf 2^64 Byte Speicher zugreifen, wenn der größte Datentyp, den Sie verwenden können, 64 Bit ist.
-
Nicht, wenn das breiter als ein Zeiger ist. (z.B
long long
bei einer Implementierung mit 32-Bit-Zeigern).– Peter Cordes
22. März 2018 um 15:10 Uhr
Chris
Ich suchte nach einer Möglichkeit, die maximale Größe für ein Array zu bestimmen. Diese Frage scheint dasselbe zu stellen, also möchte ich meine Erkenntnisse teilen.
Anfänglich stellt C keine Funktion bereit, um die maximale Anzahl von Elementen zu bestimmen, die in einem Array zur Kompilierzeit zuweisbar sind. Dies liegt daran, dass es vom verfügbaren Speicher der Maschine abhängt, auf der es ausgeführt wird.
Andererseits habe ich festgestellt, dass Speicherzuweisungsfunktionen (calloc()
und malloc()
) ermöglichen die Zuweisung größerer Arrays. Darüber hinaus können Sie mit diesen Funktionen Laufzeitfehler bei der Speicherzuordnung behandeln.
Ich hoffe, das hilft.
Tatsächlich ist Software (OS) die Sache, die normalerweise die Speicherbegrenzung verursacht, die von Ihrem C-Programm gesehen wird.
– TJD
21. Februar 2012 um 23:33 Uhr
Ohne Speicherbegrenzung gibt es keine Begrenzung der Zeigergröße. Ohne Begrenzung der Zeigergröße sind alle Wetten ungültig.
– Sergej Kalinitschenko
21. Februar 2012 um 23:34 Uhr
es wäre durch die Größe des Zeigers begrenzt (32 Bit gegenüber 64 Bit)
– Mitch Weizen
21. Februar 2012 um 23:34 Uhr