Warum ist die maximale Größe eines Arrays “zu groß”?

Lesezeit: 10 Minuten

Benutzeravatar von Lundin
Lundin

Ich habe den gleichen Eindruck wie diese Antwort, das size_t wird durch den Standard immer garantiert, groß genug zu sein, um den größtmöglichen Typ eines gegebenen Systems aufzunehmen.

Dieser Code kann jedoch nicht auf 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 hier etwas in der Norm falsch? Ist size_t darf für eine bestimmte Implementierung zu groß sein? Oder ist das ein weiterer Fehler in Mingw?


EDIT: Weitere Recherchen zeigen das

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

Was zufällig dasselbe ist 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

Daher neige ich jetzt zu der Annahme, dass dies ein Fehler in Mingw ist, da das Festlegen der maximal zulässigen Größe basierend auf einem vorzeichenbehafteten Integer-Typ keinen Sinn ergibt.

  • Ein Array der Größe SIZE_MAX verbraucht wahrscheinlich den gesamten Speicher.

    – Paul Ogilvie

    3. März 2017 um 9:20 Uhr

  • @PaulOgilvie Warum haben sie dann eine Zahl ausgewählt, die für die gegebene Implementierung zu groß ist?

    – Ludin

    3. März 2017 um 9:22 Uhr

  • Laut GCC Quellcode Das Limit wird durch das unterzeichnete Gegenstück von erzwungen sizetype (INT_MAX im Kommentar ist irreführend). index zugeordnet ist c_common_signed_type (sizetype); in Zeile 5933. Dies erklärt wahrscheinlich das “Half-Range”-Problem.

    – Grzegorz Szpetkowski

    3. März 2017 um 9:51 Uhr


  • @Lundin: Ich habe keine Kommentare gefunden, warum sie signierten Typ verwenden, also könnte es sich um einen Fehler handeln. Edit: Ich denke, dass 2501 richtig ist und es daran liegt ptrdiff_t Typ, der signiert ist.

    – Grzegorz Szpetkowski

    3. März 2017 um 10:02 Uhr


  • Sie werden feststellen, dass nichts im Standard impliziert, dass der Compiler Objekte jeder Größe bis zu zulassen muss SIZE_MAX. Es impliziert nur, dass der Compiler keine Objekte zulassen darf, die größer als sind SIZE_MAX. Das gilt auch dann, wenn Sie das Objekt nicht wirklich erstellen, da sizeof kann auch auf Typen angewendet werden.

    – Brian Bi

    3. März 2017 um 21:37 Uhr


Benutzeravatar von 2501
2501

Die Grenze SIZE_MAX / 2 ergibt sich aus den Definitionen von size_t und ptrdiff_t in Ihrer Implementierung, die festlegen, dass die Typen ptrdiff_t und size_t dieselbe Breite haben.

C Standardmandate1 dass der Typ size_t vorzeichenlos und der Typ ptrdiff_t vorzeichenbehaftet ist.

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

Somit ist der Wert SIZE_MAX / 2 gleich dem Wert PTRDIFF_MAX. Wenn die Implementierung wählt, dass die maximale Objektgröße SIZE_MAX ist, dann müsste die Breite des Typs ptrdiff_t erhöht werden. Aber es ist viel einfacher, die maximale Größe des Objekts auf SIZE_MAX / 2 zu begrenzen, dann ist es so, dass der Typ ptrdiff_t einen größeren oder gleichen positiven Bereich hat als der Typ size_t.

Standard bietet diese an3 Kommentare4 zum Thema.


(Zitiert aus ISO/IEC 9899:201x)

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

2 (6.5.6 Additive Operatoren 9)
Wenn zwei Zeiger subtrahiert werden, zeigen beide auf Elemente desselben Array-Objekts oder einen 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 ptrdiff_t, der im Header definiert ist. Wenn das Ergebnis in einem Objekt dieses Typs nicht darstellbar ist, ist das Verhalten undefiniert.

3 (K.3.4 Integer-Typen 3)
Extrem große Objektgrößen sind häufig ein Zeichen dafür, dass die Größe eines Objekts falsch berechnet wurde. Beispielsweise erscheinen negative Zahlen als sehr große positive Zahlen, wenn sie in einen vorzeichenlosen Typ wie size_t konvertiert werden. Außerdem unterstützen einige Implementierungen keine Objekte, die so groß sind wie der Maximalwert, der durch den Typ size_t dargestellt werden kann.

4 (K.3.4 Integer-Typen 4)
Aus diesen Gründen ist es manchmal vorteilhaft, den Bereich der Objektgrößen einzuschränken, um Programmierfehler zu erkennen. Für Implementierungen, die auf Maschinen mit großen Adressräumen abzielen, wird empfohlen, dass RSIZE_MAX als der kleinere Wert aus der Größe des größten unterstützten Objekts oder (SIZE_MAX >> 1) definiert wird, selbst wenn diese Grenze kleiner als die Größe einiger legitimer, aber sehr großer ist groß, Objekte. Implementierungen, die auf Maschinen mit kleinen Adressräumen abzielen, möchten möglicherweise RSIZE_MAX als SIZE_MAX definieren, was bedeutet, dass es keine Objektgröße gibt, die als Verletzung der Laufzeitbeschränkung betrachtet wird.

  • Das macht Sinn. Also eher ein Defekt im C-Standard? Bedeutet, dass SIZE_MAX kann nie sinnvoll eingesetzt werden, aber size_t sollte stattdessen lieber verwendet werden PTRDIFF_MAX?

    – Ludin

    3. März 2017 um 10:07 Uhr

  • @Lundin Es ist kein Fehler, da SIZE_MAX nicht den Wert der maximal zulässigen Größe eines Objekts darstellt. Auch die Verwendung von PTRDIFF_MAX als Limit ist nicht korrekt, da es theoretisch größer als SIZE_MAX sein könnte. Ich denke, der richtige Wert ist min(SIZE_MAX,PTRDIFF_MAX).

    – 2501

    3. März 2017 um 10:12 Uhr


  • @Lundin: Ich glaube nicht, dass das erlaubt wäre, da der C-Standard definiert SIZE_MAX als „Grenze von size_t” (C99 Abschnitt 7.20.3).

    – mtvec

    3. März 2017 um 13:21 Uhr


  • @Lundin: Sie scheinen den Zweck von falsch zu verstehen SIZE_MAX. es ist nicht soll überhaupt “die maximal mögliche Größe eines Objekts” sein. Es soll “der maximal mögliche Wert einer Ganzzahl des Typs sein size_t.” Während wir verwenden size_t um die Größe von Objekten zu messen, ist es nicht erforderlich, dass die Implementierung tatsächlich die Erzeugung solch riesiger Objekte erlaubt. Deutlich sein, SIZE_MAX / 2 ist immer noch eine absurd große Zahl auf einem 64-Bit-System; kein vernünftiger Programmierer wird jemals ein so großes Array erstellen wollen, selbst als statische globale Variable.

    – Kevin

    4. März 2017 um 6:56 Uhr

  • Der Standard erlaubt es einer Implementierung, Objekte anzubieten, die größer als PTRDIFF_MAX sind; Der einzige Nachteil ist, dass das Subtrahieren von zwei Zeigern, die weit genug voneinander entfernt sind, zu undefiniertem Verhalten führen würde. (Was ein ziemlich großer Nachteil ist und erklärt, warum sie sich dagegen entscheiden).

    – MM

    23. März 2018 um 11:24 Uhr


AnT steht mit Russlands Benutzer-Avatar
AnT steht zu Russland

Die Reichweite size_t garantiert ausreicht, um die Größe des größten von der Implementierung unterstützten Objekts zu speichern. Das Gegenteil gilt nicht: Sie können nicht garantiert ein Objekt erstellen, dessen Größe den gesamten Bereich von ausfüllt size_t.

Unter solchen Umständen ist die Frage: was tut 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 letzteres, dh 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_tmüssen Implementierungen ebenfalls bereitstellen ptrdiff_t, die (aber nicht garantiert) den Unterschied zwischen zwei Zeigern speichern soll, die auf dasselbe Array-Objekt zeigen. Da Typ ptrdiff_t unterzeichnet ist, stehen die Implementierungen vor den folgenden Entscheidungen:

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

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

  3. Verwenden ptrdiff_t gleich breit wie size_t und beschränken die maximale Array-Objektgröße von SIZE_MAX / 2. Eine solche ptrdiff_t kann jeden Unterschied zwischen zwei Zeigern berücksichtigen, die auf ein Array der 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.

  • Der Code in der Frage versucht jedoch nicht, Array-Objekte zu erstellen, sondern erstellt nur eine Typedef. Ist es also nicht konform, wenn die Implementierung die Typedef ablehnt?

    – MM

    4. März 2017 um 22:54 Uhr

  • Ich denke wirklich, dass dies einer kanonischen Antwort nahe kommt (für all dieses ptrdiff/size/intptr-Zeug) und möchte Sie dringend bitten, Ihre andere Antwort mit dieser zusammenzuführen und die _MAX- und _MIN-Konstanten für jeden fraglichen Typ zu skizzieren, bevor Sie mit der Erklärung beginnen sie (weil Referenzen es einfacher machen) und bringen Sie die Details der anderen Antworten in diese ein. Gut gemacht!

    – Evan Caroll

    15. Juni 2018 um 19:55 Uhr


  • Tatsächlich habe ich Ihre Antwort und die Antwort auf stackoverflow.com/questions/9386979/… Ich habe fast das Gefühl, dass es ein schlechter Dienst ist, dem anderen zu erlauben, offen zu bleiben. Vielleicht könnten Sie Ihre Antwort auf diese Frage migrieren und dies einfach vollständig schließen.

    – Evan Caroll

    15. Juni 2018 um 20:00 Uhr

  • Sie sind sich nicht sicher, ob Sie ein Update für mehr Symmetrie in Ihrer sehr großartigen Aufschlüsselung wünschen, zögern Sie nicht, dies durchzuziehen, um die Verarbeitung zu vereinfachen. (meiner bescheidenen Meinung nach) gist.github.com/EvanCarroll/121dfb870fd3da0dc52ba4e7edefe3ee

    – Evan Caroll

    8. Juli 2018 um 2:44 Uhr


Es sieht sehr nach implementierungsspezifischem Verhalten aus.

Ich verwende hier Mac OS und mit gcc 6.3.0 ist die größte Größe, mit der ich Ihre Definition kompilieren kann SIZE_MAX/2; mit SIZE_MAX/2 + 1 es kompiliert nicht mehr.

Auf der anderen Seite ist Witch Clang 4.0.0 das Größte SIZE_MAX/8und SIZE_MAX/8 + 1 geht kaputt.

  • SIZE_MAX/8+1 Interessant. was ist da die fehlermeldung? Können Sie erfolgreich malloc SIZE_MAX/8+1?

    – 2501

    3. März 2017 um 10:16 Uhr


  • Fehlermeldung ist sehr ähnlich: error: array is too large (2305843009213693952 elements)

    – awysk

    3. März 2017 um 10:23 Uhr

  • clang 4.0.0 Was ist der Wert RSIZE_MAX und was ist der Wert von PTRDIFF_MAX?

    – 2501

    3. März 2017 um 10:23 Uhr

  • SIZE_MAX: 18446744073709551615 RSIZE_MAX: 9223372036854775807 PTRDIFF_MAX: 9223372036854775807

    – awysk

    3. März 2017 um 10:25 Uhr

  • In der Tat. Nein, ich kann nicht mallocieren SIZE_MAX/128/1024: blabla, set a breakpoint in malloc_error_break to debug. SIZE_MAX/256/1024 mallocs gut. +1/-1 dazu ändern das Verhalten nicht.

    – awysk

    3. März 2017 um 10:28 Uhr


Benutzeravatar von Paul Ogilvie
Paul Ogilvie

Nur von Grund auf neu argumentieren, size_t ist ein Typ, der die Größe eines beliebigen Objekts aufnehmen kann. Die Größe jedes Objekts ist durch die Breite des Adressbusses begrenzt (ohne Multiplexing und Systeme, die zB 32- und 64-Bit-Code verarbeiten können, nennen Sie das “Codebreite”). Analog zu MAX_INT welches der größte ganzzahlige Wert ist, SIZE_MAX ist der größte Wert von size_t. Also ein Objekt von Größe SIZE_MAX ist alles adressierbarer Speicher. Es ist vernünftig, dass eine Implementierung dies als Fehler kennzeichnet, aber ich stimme zu, dass es sich nur dann um einen Fehler handelt, wenn ein tatsächliches Objekt zugewiesen wird, sei es auf dem Stapel oder im globalen Speicher. (Ein Anruf bei malloc für diesen Betrag wird sowieso scheitern)

1412590cookie-checkWarum ist die maximale Größe eines Arrays “zu groß”?

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

Privacy policy