57 votes

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 la norme comme étant suffisamment grand pour contenir le plus grand type possible d'un système donné.

Cependant, ce code ne se compile pas 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

Ai-je mal compris quelque chose dans la norme ? Est-ce que size_t peuvent-ils être trop importants pour une mise en œuvre donnée ? Ou s'agit-il d'un autre bogue 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

Ce qui se trouve être la même chose 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

J'ai donc tendance à croire qu'il s'agit d'un bogue dans Mingw, car fixer la taille maximale autorisée en fonction d'un type d'entier signé n'a aucun sens.

13 votes

Un tableau de taille SIZE_MAX consomme probablement toute la mémoire.

1 votes

@PaulOgilvie Alors pourquoi ont-ils choisi un nombre qui est trop grand pour l'implémentation donnée ?

0 votes

J'ai essayé et bizarrement, typedef uint8_t array_t [SIZE_MAX/2]; compile bien

67voto

2501 Points 4807

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

C Mandats standard 1 que le type size_t est non signé et que le type ptrdiff_t est signé.

Le résultat de la différence entre deux pointeurs sera toujours 2 sont de 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 entre deux pointeurs ne pourrait pas être représentée dans le type ptrdiff_t, ce qui entraînerait un comportement indéfini.

La valeur SIZE_MAX / 2 est donc égale à la valeur PTRDIFF_MAX. Si l'implémentation choisit de fixer la taille maximale de l'objet à SIZE_MAX, la largeur du type ptrdiff_t devra être augmentée. Mais il est beaucoup plus facile de limiter la taille maximale de l'objet à SIZE_MAX / 2 que de faire en sorte que le type ptrdiff_t ait une plage positive supérieure ou égale à celle du type size_t.

L'offre standard est la suivante 3 commentaires 4 sur le sujet.


(Extrait de la norme ISO/IEC 9899:201x)

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

2 (6.5.6 Opérateurs additifs 9)
Lorsque deux pointeurs sont soustraits, ils doivent tous deux pointer sur des éléments du même tableau, ou l'un après le dernier élément du tableau ; le résultat est la différence entre les indices des indices des deux éléments du tableau. La taille du résultat est définie par l'implémentation, et son type (un 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 de nombres entiers 3)
Des tailles d'objets extrêmement grandes sont souvent le signe que la taille d'un objet a été calculée incorrecte. Par exemple, les nombres négatifs apparaissent comme 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 nombres entiers 4)
Pour ces raisons, il est parfois utile de restreindre la gamme des tailles d'objets à détecter. des erreurs de programmation. Pour les implémentations visant des machines avec de grands espaces d'adressage, il est recommandé de définir RSIZE_MAX comme la plus petite taille de l'objet le plus grand supporté ou (SIZE_MAX >> 1), même si cette limite est plus petite que la taille de l'objet 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 grands. de certains objets légitimes, mais très volumineux. Les implémentations ciblant des machines avec de petits peuvent souhaiter définir RSIZE_MAX comme SIZE_MAX, ce qui signifie qu'il n'y a pas de taille d'objet qui soit considérée comme une violation des contraintes d'exécution.

1 votes

C'est logique. Il s'agit donc plutôt d'un défaut de la norme C ? C'est-à-dire que SIZE_MAX ne peuvent jamais être utilisés de manière significative, mais les size_t devrait plutôt utiliser PTRDIFF_MAX ?

4 votes

@Lundin Ce n'est pas un défaut car SIZE_MAX ne représente pas la valeur de la taille maximale autorisée d'un objet. Même l'utilisation de PTRDIFF_MAX comme limite n'est pas correcte, car elle pourrait théoriquement être plus grande que SIZE_MAX. Je pense que la valeur correcte est min(SIZE_MAX,PTRDIFF_MAX) .

0 votes

Gcc aurait cependant pu choisir de faire #define SIZE_MAX PTRDIFF_MAX . Cela aurait été cohérent avec le standard C sans avoir à inventer un énorme ptrdiff_t.

20voto

AndreyT Points 139512

La gamme de size_t est garanti suffisant pour stocker la taille du plus grand objet pris en charge par l'implémentation. L'inverse n'est pas vrai : il n'est pas garanti que vous puissiez créer un objet dont la taille remplit l'ensemble de la plage de valeurs de size_t .

Dans ces conditions, la question qui se pose est la suivante : qu'est-ce que SIZE_MAX représentent-ils ? La plus grande taille d'objet prise en charge ? Ou la plus grande valeur représentable en size_t ? La réponse est : c'est la seconde, c'est-à-dire SIZE_MAX est (size_t) -1 . Il n'est pas garanti que vous puissiez créer des objets SIZE_MAX octets de grande taille.

La raison en est qu'en plus des size_t Les implémentations doivent également fournir ptrdiff_t qui est destiné (mais non garanti) à stocker la différence entre deux pointeurs pointant vers le même objet tableau. Puisque le type ptrdiff_t est signé, les implémentations sont confrontées aux choix suivants :

  1. Autoriser les objets de type tableau de taille SIZE_MAX et faire ptrdiff_t plus large que size_t . Il doit être plus large d'au moins un bit. Tel est le cas ptrdiff_t peut s'accommoder de toute différence entre deux pointeurs pointant dans un tableau de taille SIZE_MAX ou moins.

  2. Autoriser les objets de type tableau de taille SIZE_MAX et utiliser ptrdiff_t de la même largeur comme size_t . Accepter le fait que la soustraction par pointeur peut débordement et provoquer un comportement indéfini, si les pointeurs sont plus éloignés que SIZE_MAX / 2 des éléments à part. La spécification du langage n'interdit pas cette approche.

  3. Utilisation ptrdiff_t de même largeur que size_t y restreindre la taille maximale des objets du tableau par SIZE_MAX / 2 . Tels qu'ils sont ptrdiff_t peut s'accommoder de toute différence entre deux pointeurs pointant dans un tableau de taille SIZE_MAX / 2 ou moins.

Vous avez simplement affaire à une mise en œuvre qui a décidé de suivre la troisième approche.

2 votes

Le code de la question ne tente pas de créer des objets de type tableau, il crée seulement un typedef. Est-il donc non-conforme que l'implémentation rejette le typedef ?

1 votes

Je pense vraiment que ceci est proche d'une réponse canonique (pour tout ce qui concerne ptrdiff/size/intptr) et je vous conseille vivement de fusionner votre autre réponse avec celle-ci, d'indiquer les constantes _MAX et _MIN pour chaque type en question avant de les expliquer (parce que les références facilitent les choses) et d'intégrer les détails des autres réponses dans celle-ci. Bon travail !

0 votes

Le fait d'avoir votre réponse et la réponse sur stackoverflow.com/questions/9386979/ J'ai presque l'impression que c'est un manque de service que de permettre à l'autre de rester ouvert. Peut-être pourriez-vous migrer votre réponse à cette question et fermer celle-ci en double.

5voto

avysk Points 1414

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

Je tourne 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.

D'autre part, avec clang 4.0.0, le plus gros problème est le suivant SIZE_MAX/8 y SIZE_MAX/8 + 1 pauses.

0 votes

SIZE_MAX/8+1 Intéressant. Quel est le message d'erreur ? Pouvez-vous réussir à malloc SIZE_MAX/8+1 ?

0 votes

Le message d'erreur est pratiquement le même : error: array is too large (2305843009213693952 elements)

0 votes

clang 4.0.0 Quelle est la valeur de RSIZE_MAX et quelle est la valeur de PTRDIFF_MAX ?

0voto

Paul Ogilvie Points 4852

Il suffit de raisonner à partir de zéro, size_t est un type qui peut contenir la taille de n'importe quel objet. La taille de tout objet est limitée par la largeur du bus d'adresse (sans tenir compte du multiplexage et des systèmes qui peuvent gérer des codes de 32 et 64 bits, ce que l'on appelle la "largeur de code"). Analogue à 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 une mémoire adressable. Il est raisonnable qu'une implémentation signale cela comme une erreur, cependant, je suis d'accord qu'il s'agit d'une erreur uniquement 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 car ce montant échouera de toute façon)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X