web-dev-qa-db-fra.com

Pourquoi la taille maximale d'un tableau est-elle "trop ​​grande"?

J'ai la même impression que cette réponse , que size_t est toujours garanti par le standard d'être suffisamment grand pour contenir le type le plus grand possible d'un système donné.

Cependant, ce code ne parvient pas à être compilé sur gcc/Mingw:

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

typedef uint8_t array_t [SIZE_MAX];

erreur: la taille du tableau 'array_t' est trop grande

Est-ce que je comprends mal quelque chose dans la norme ici? Est size_t autorisé à être trop volumineux pour une implémentation donnée? Ou est-ce un autre bug dans Mingw?


EDIT: d'autres recherches montrent que

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

Quel est le même que

#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

Donc, j’ai maintenant tendance à croire qu’il s’agit d’un bogue dans Mingw, car définir la taille maximale autorisée en fonction d’un type entier signé n’a aucun sens.

54
Lundin

La limite SIZE_MAX/2 provient des définitions de size_t et de ptrdiff_t dans votre implémentation, qui déterminent que les types ptrdiff_t et size_t ont la même largeur.

C mandats standard1 ce type size_t est non signé et le type ptrdiff_t est signé.

Le résultat de la différence entre deux pointeurs, sera toujours2 avoir le type ptrdiff_t. Cela signifie que, dans votre implémentation, la taille de l'objet doit être limitée à PTRDIFF_MAX, sinon une différence valide de deux pointeurs ne pourrait pas être représentée dans le type ptrdiff_t, ce qui entraînerait un comportement non défini.

Ainsi, la valeur SIZE_MAX/2 est égale à la valeur PTRDIFF_MAX. Si l'implémentation choisit d'avoir la taille d'objet maximale égale à SIZE_MAX, il faudra alors augmenter la largeur du type ptrdiff_t. Mais il est beaucoup plus facile de limiter la taille maximale de l'objet à SIZE_MAX/2, il est alors préférable que le type ptrdiff_t ait une plage positive supérieure ou égale à celle du type size_t.

Standard propose ces3 commentaires4 sur le sujet.


(Cité de ISO/IEC 9899: 201x)

1 (7.19 Définitions communes 2)
Les types sont
ptrdiff_t
qui est le type entier signé du résultat de la soustraction de deux pointeurs;
taille_t
qui est le type entier non signé du résultat de l'opérateur sizeof;

2 (6.5.6 Opérateurs additifs 9)
Lorsque deux pointeurs sont soustraits, les deux doivent pointer vers des éléments du même objet de tableau, ou un passé après le dernier élément de l’objet de tableau; le résultat est la différence des indices des deux éléments du tableau. La taille du résultat est définie par l'implémentation et son type (un type entier signé) est ptrdiff_t défini dans l'en-tête. Si le résultat n'est pas représentable dans un objet de ce type, le comportement est indéfini.

3 (K.3.4 Types entiers 3)
Les très grandes tailles d’objets sont souvent le signe que la taille d’un objet a été mal calculée. Par exemple, les nombres négatifs apparaissent sous forme de très grands nombres positifs lorsqu’ils sont convertis en un type non signé comme size_t. De plus, certaines implémentations ne prennent pas en charge des objets aussi grands que la valeur maximale pouvant être représentée par le type size_t.

4 (K.3.4 Types de nombre entier 4)
Pour ces raisons, il est parfois avantageux de limiter la plage de tailles d’objets afin de détecter les erreurs de programmation. Pour les implémentations ciblant des machines avec de grands espaces d’adresses, il est recommandé de définir RSIZE_MAX comme la plus petite des tailles du plus grand objet pris en charge ou (SIZE_MAX >> 1), même si cette limite est inférieure à la taille de certains objets légitimes, mais très différents. gros objets. Les implémentations ciblant des machines avec de petits espaces d'adresses peuvent souhaiter définir RSIZE_MAX en tant que SIZE_MAX, ce qui signifie qu'aucune taille d'objet n'est considérée comme une violation de contrainte d'exécution.

62
2501

Le de gamme size_t est garanti suffisant pour stocker la taille du plus grand objet pris en charge par la mise en œuvre. L'inverse n'est pas vrai: vous n'êtes pas assuré de pouvoir créer un objet dont la taille remplit toute la plage de size_t.

Dans de telles circonstances, la question est de savoir ce que SIZE_MAX représenter? La plus grande taille d'objet prise en charge? Ou la plus grande valeur représentable dans size_t? La réponse est: c’est ce dernier, c’est-à-dire SIZE_MAX est (size_t) -1. Vous n'êtes pas assuré de pouvoir créer des objets SIZE_MAX octets volumineux.

La raison derrière cela est qu’en plus de size_t, les implémentations doivent également fournir ptrdiff_t, qui est destiné (mais non garanti) à stocker la différence entre deux pointeurs pointant dans le même objet tableau. Depuis le type ptrdiff_t est signé, les implémentations sont confrontées aux choix suivants:

  1. Autoriser les objets de tableau de taille SIZE_MAX et fais ptrdiff_tplus large que size_t. Il doit être plus large d'au moins un bit. Tel ptrdiff_t peut prendre en charge toute différence entre deux pointeurs pointant dans un tableau de taille SIZE_MAX ou plus petit.

  2. Autoriser les objets de tableau de taille SIZE_MAX et utilise ptrdiff_t de même largeur que size_t. Acceptez le fait que la soustraction de pointeur peut débordement et provoque un comportement indéfini, si les pointeurs sont plus éloignés que SIZE_MAX / 2 éléments séparés. La spécification de langue n'interdit pas cette approche.

  3. Utilisation ptrdiff_t de même largeur que size_t et restrict la taille maximale de l'objet tableau par SIZE_MAX / 2. Tel ptrdiff_t peut prendre en charge toute différence entre deux pointeurs pointant dans un tableau de taille SIZE_MAX / 2 ou plus petit.

Vous faites simplement face à une implémentation qui a décidé de suivre la troisième approche.

17
AnT

Cela ressemble beaucoup à un comportement spécifique à la mise en œuvre.

Je suis ici sous Mac OS, et avec gcc 6.3.0, la plus grande taille avec laquelle je peux compiler votre définition est SIZE_MAX/2; avec SIZE_MAX/2 + 1 il ne compile plus.

De l'autre côté, Witch Clang 4.0.0 le plus important est SIZE_MAX/8, et SIZE_MAX/8 + 1 pauses.

5
avysk

Juste raisonner à partir de zéro, size_t est un type pouvant contenir la taille d’un objet. La taille de tout objet est limitée par la largeur du bus d'adresses (en ignorant le multiplexage et les systèmes pouvant gérer, par exemple, un code de 32 et 64 bits, appelez cette "largeur de code"). Anologous to MAX_INT qui est la plus grande valeur entière, SIZE_MAX est la plus grande valeur de size_t. Ainsi, un objet de taille SIZE_MAX est toute la mémoire adressable. Il est raisonnable qu’une implémentation signale comme une erreur, mais j’accepte le fait que c’est une erreur seulement dans le cas où un objet réel est alloué, que ce soit sur la pile ou dans la mémoire globale. (Un appel à malloc pour ce montant échouera de toute façon)

0
Paul Ogilvie