Frage Warum ist die maximale Größe eines Arrays "zu groß"?


Ich habe den gleichen Eindruck wie diese Antwort, Das size_t Durch den Standard wird immer gewährleistet, dass er groß genug ist, um den größtmöglichen Typ eines gegebenen Systems zu halten.

Dieser Code kann jedoch nicht mit gcc / Mingw kompiliert werden:

#include <stdint.h>
#include <stddef.h>

typedef uint8_t array_t [SIZE_MAX];

Fehler: Größe des Arrays 'array_t' ist zu groß

Verstehe ich etwas im Standard hier falsch? Ist size_t darf für eine gegebene Implementierung zu groß sein? Oder ist das ein weiterer Fehler in Mingw?


EDIT: weitere Untersuchungen zeigen das

typedef uint8_t array_t [SIZE_MAX/2];   // does compile
typedef uint8_t array_t [SIZE_MAX/2+1]; // does not compile

Das ist das Gleiche wie

#include <limits.h>

typedef uint8_t array_t [LLONG_MAX];           // does compile
typedef uint8_t array_t [LLONG_MAX+(size_t)1]; // does not compile

Ich bin nun geneigt zu glauben, dass dies ein Fehler in Mingw ist, weil das Festlegen der maximal zulässigen Größe basierend auf einem vorzeichenbehafteten Integer-Typ keinen Sinn ergibt.


51
2018-03-03 09:17


Ursprung


Antworten:


Das Limit SIZE_MAX / 2 leitet sich aus den Definitionen von size_t und ptrdiff_t in Ihrer Implementierung ab, die dafür sorgen, dass die Typen ptrdiff_t und size_t die gleiche Breite haben.

C Standardmandate1  Der Typ size_t ist vorzeichenlos und der Typ ptrdiff_t ist signiert.

Das Ergebnis der Differenz zwischen zwei Zeigern wird immer2 habe den Typ ptrdiff_t. Dies bedeutet, dass bei Ihrer Implementierung die Größe des Objekts begrenzt sein muss PTRDIFF_MAX, sonst könnte eine gültige Differenz von zwei Zeigern im Typ ptrdiff_t nicht dargestellt werden, was zu undefiniertem Verhalten führt.

Somit ist der Wert SIZE_MAX / 2 gleich dem Wert PTRDIFF_MAX. Wenn die Implementierung die maximale Objektgröße als SIZE_MAX auswählen möchte, müsste die Breite des Typs ptrdiff_t erhöht werden. Es ist jedoch viel einfacher, die maximale Größe des Objekts auf SIZE_MAX / 2 zu beschränken, dann ist der Typ ptrdiff_t größer oder gleich positiv als der Typ size_t.

Standard bietet diese an3  Bemerkungen4 zum Thema.


(Zitat aus ISO / IEC 9899: 201x)

1 (7.19 Gemeinsame Definitionen 2)
Die Typen sind
ptrdiff_t
was ist der vorzeichenbehaftete ganzzahlige Typ des Ergebnisses des Subtrahierens zweier Zeiger;
Größe_t
Dies ist der vorzeichenlose ganzzahlige Typ des Ergebnisses des Operators sizeof;

2 (6.5.6 Zusatzoperatoren 9)
Wenn zwei Zeiger subtrahiert werden, müssen beide auf Elemente desselben Array-Objekts zeigen. oder eine nach dem letzten Element des Array-Objekts; Das Ergebnis ist die Differenz der Indizes der beiden Array-Elemente. Die Größe des Ergebnisses ist implementationsdefiniert, und sein Typ (ein vorzeichenbehafteter Integer-Typ) ist ptrdiff_t, der in der Kopfzeile definiert ist. Wenn das Ergebnis in einem Objekt dieses Typs nicht darstellbar ist, ist das Verhalten nicht definiert.

3 (K.3.4 Ganzzahlarten 3)
Extrem große Objektgrößen sind häufig ein Zeichen dafür, dass eine Objektgröße berechnet wurde falsch. Zum Beispiel erscheinen negative Zahlen als sehr große positive Zahlen in einen vorzeichenlosen Typ wie size_t konvertiert. Einige Implementierungen unterstützen auch nicht Objekte so groß wie der Maximalwert, der vom Typ size_t dargestellt werden kann.

4(K.3.4 Ganzzahlige Typen 4)
Aus diesen Gründen ist es manchmal vorteilhaft, den Bereich der zu erkennenden Objektgrößen einzuschränken Programmierfehler. Für Implementierungen, die auf Maschinen mit großen Adressräumen abzielen, Es wird empfohlen, dass RSIZE_MAX als der kleinere der Größe des größten definiert wird Objekt unterstützt oder (SIZE_MAX >> 1), auch wenn dieses Limit kleiner ist als die Größe von einige legitime, aber sehr große Objekte. Implementierungen für Maschinen mit kleinen Adressräume möchten möglicherweise RSIZE_MAX als SIZE_MAX definieren, was bedeutet, dass es keine Objektgröße gibt, die als Laufzeitbeschränkungsverletzung betrachtet wird.


58
2018-03-03 10:01



Der Bereich size_t ist garantiert ausreichend, um die Größe des größten von der Implementierung unterstützten Objekts zu speichern. Das Umgekehrte ist nicht wahr: Es ist nicht garantiert, dass Sie ein Objekt erstellen können, dessen Größe den gesamten Bereich von size_t.

Unter solchen Umständen lautet die Frage: Was ist? SIZE_MAX stehen für? Die größte unterstützte Objektgröße? Oder der größte darstellbare Wert in size_t? Die Antwort ist: es ist das letztere, d.h. SIZE_MAX ist (size_t) -1. Es ist nicht garantiert, dass Sie Objekte erstellen können SIZE_MAX Bytes groß.

Der Grund dafür ist, dass zusätzlich zu size_tImplementierungen müssen auch bereitstellen ptrdiff_t, die (aber nicht garantiert) den Unterschied zwischen zwei Zeigern speichern soll, die auf das gleiche Array-Objekt zeigen. Seit dem Typ ptrdiff_t Ist signiert, stehen den Implementierungen folgende Möglichkeiten zur Verfügung:

  1. Erlaube Array-Objekte der Größe SIZE_MAX und mache ptrdiff_t  breiter als size_t. Es muss um mindestens ein Bit breiter sein. Eine solche ptrdiff_t kann jeden Unterschied zwischen zwei Zeigern berücksichtigen, die in ein Array von Größe zeigen SIZE_MAX oder kleiner.

  2. Erlaube Array-Objekte der Größe SIZE_MAX und benutzen ptrdiff_t von die gleiche Breite wie size_t. Akzeptieren Sie die Tatsache, dass Zeiger-Subtraktion kann Überlauf und verursachen undefiniertes Verhalten, wenn die Zeiger weiter als sind SIZE_MAX / 2 Elemente auseinander. Die Sprachspezifikation verbietet diesen Ansatz nicht.

  3. Benutzen ptrdiff_t von der gleichen Breite wie size_t und beschränken die maximale Array-Objektgröße nach SIZE_MAX / 2. Eine solche ptrdiff_t kann jeden Unterschied zwischen zwei Zeigern berücksichtigen, die in ein Array von Größe zeigen SIZE_MAX / 2 oder kleiner.

Sie haben es einfach mit einer Implementierung zu tun, die sich für den dritten Ansatz entschieden hat.


10
2018-03-04 09:31



Es ähnelt sehr dem implementierungsspezifischen Verhalten.

Ich laufe hier Mac OS, und mit gcc 6.3.0 die größte Größe, mit der ich deine Definition zusammenstellen kann SIZE_MAX/2; mit SIZE_MAX/2 + 1 Es kompiliert nicht mehr.

Auf der anderen Seite ist Hexe 4.0.0 der größte SIZE_MAX/8, und SIZE_MAX/8 + 1 geht kaputt.


5
2018-03-03 09:47



Nur von Grund auf neu zu denken, size_t ist ein Typ, der die Größe eines Objekts enthalten kann. Die Größe eines beliebigen Objekts ist durch die Breite des Adressbusses begrenzt (ignorieren Multiplexing und Systeme, die zB 32- und 64-Bit-Code verarbeiten können, nennen diese "Code-Breite"). Zu MAX_INT welches der größte ganzzahlige Wert ist, SIZE_MAX ist der größte Wert von size_t. Also ein Objekt der Größe SIZE_MAXist alles adressierbarer Speicher. Es ist vernünftig, dass eine Implementierung kennzeichnet, dass ich als ein Fehler jedoch zustimme, dass es ein Fehler nur in einem Fall ist, in dem ein tatsächliches Objekt zugewiesen wird, sei es auf dem Stapel oder im globalen Speicher. (Ein Anruf an malloc für diesen Betrag wird sowieso scheitern)


1
2018-03-03 09:38



Als erstes, size_t wird verwendet, um das Ergebnis zu halten sizeof Operator. Es ist also garantiert eine Größe, die den "Wert" halten kann SIZE_MAX. Theoretisch sollte das erlauben, jedes Objekt mit der Größe zu definieren SIZE_MAX.

Dann, wenn ich mich richtig erinnere, sind Sie durch die Obergrenze der systemweiten Ressourcen begrenzt. Dies ist nicht durch den C-Standard bedingt, sondern durch das Betriebssystem / die Umgebung.

Prüfen ulimit -a Ausgabe. Sie können die Grenzen auch mit ändern ulimit -s <size> für den Stapel, solange das Array, in dem Sie definieren, gespeichert ist Stapel. Andernfalls müssen Sie bei globalen Arrays wahrscheinlich nach der zulässigen Größe in .DATA oder .BSS nach Ihrem Betriebssystem suchen. Dies ist also abhängig von der Umgebung (oder von der Implementierung abhängig).


1
2018-03-03 09:21