La valeur maximale de size_t (SIZE_MAX) est-elle définie par rapport aux autres types d’entiers?

J’écris une bibliothèque de fonctions qui permettent de convertir en toute sécurité entre différents types numériques ou d’essayer. Mon intention est à peu près égale de créer parties-créer-utile-bibliothèque et apprendre-C-bord-cas.

Ma fonction int -to- size_t déclenche un -Wtype-limits GCC -Wtype-limits qui déclare que je ne devrais pas tester si un int est supérieur à SIZE_MAX , car il ne sera jamais vrai. (Une autre fonction qui convertit int en ssize_t produit un avertissement identique à propos de SSIZE_MAX .)

Mon MCVE, avec des commentaires supplémentaires et des pas de bébé, est:

 #include  /* SIZE_MAX */ #include  /* exit EXIT_FAILURE size_t */ extern size_t i2st(int value) { if (value  SIZE_MAX) { /* Line 10 */ exit(EXIT_FAILURE); } // Safe to cast --- not too big. return (size_t) u_value; } 

Les avertissements du compilateur

Je reçois des avertissements similaires de GCC 4.4.5 sur Linux 2.6.34:

 $ gcc -std=c99 -pedantic -Wall -Wextra -c -o math_utils.o math_utils.c math_utils.c: In function 'i2st': math_utils.c:10: warning: comparison is always false due to limited range of data type 

… et aussi de GCC 4.8.5 sur Linux 3.10.0:

 math_utils.c: In function 'i2st': math_utils.c:10:5: warning: comparison is always false due to limited range of data type [-Wtype-limits] if (u_value > SIZE_MAX) { /* Line 10 */ ^ 

Ces avertissements ne me paraissent pas justifiés, du moins pas dans le cas général. (Je ne nie pas que la comparaison puisse être “toujours fausse” sur une combinaison particulière de matériel et de compilateur.)

La norme C

La norme C 1999 ne semble pas exclure qu’un int soit supérieur à SIZE_MAX .

La section “6.5.3.4 L’opérateur sizeof ” ne traite pas du tout de size_t , sauf pour le décrire comme “défini dans (et autres en-têtes)”.

La section “7.17 Définitions communes ” définit size_t comme “le type entier non signé du résultat de l’opérateur sizeof “. (Merci les gars!)

La section “7.18.3 Limites d’autres types de nombres entiers” est plus utile — elle définit “limite de size_t ” comme size_t :

SIZE_MAX 65535

… signifiant que SIZE_MAX pourrait être aussi petit que 65535. Un int (signé ou non signé) pourrait être beaucoup plus grand que cela, en fonction du matériel et du compilateur.

Débordement de stack

La réponse acceptée à ” unsigned int vs. size_t ” semble corroborer mon interprétation (c’est nous qui soulignons):

Le type size_t peut être supérieur, égal ou inférieur à un unsigned int , et votre compilateur peut en déduire des hypothèses d’optimisation.

Cette réponse cite le même “paragraphe 7.17” de la norme C que j’ai déjà cité.

Autres documents

Mes recherches ont abouti au document de l’Open Group ” Neutralité de la taille des données et prise en charge 64 bits “, intitulé “Modèles de données 64 bits” (caractères gras ajoutés):

ISO / IEC 9899: 1990, Langages de programmation – C (ISO C) a laissé la définition du short int , du int , du long int et du pointer délibérément vagues […] Les seules contraintes étaient que int s doit être non plus petit que short s, et long s ne doit pas être inférieur à int s et size_t doit représenter le plus grand type non signé pris en charge par une implémentation . […] La relation entre les types de données fondamentaux peut être exprimée comme suit:

sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) = sizeof(size_t)

Si cela est vrai, alors tester un int contre SIZE_MAX est vraiment futile … mais cet article ne cite pas les chapitres, donc je ne peux pas dire comment ses auteurs sont arrivés à leur conclusion. Leurs propres documents sys/types.h “Spécifications de base version 7” sys/types.h pas cette question de toute façon.

Ma question

Je comprends que size_t sera probablement pas plus étroit qu’un int , mais le standard C garantit-il que la comparaison de some_unsigned_int > SIZE_MAX sera toujours fausse? Si oui où?

Non-doublons

Il y a deux demi-copies de cette question, mais elles posent toutes les deux des questions plus générales sur ce que size_t est censé représenter et quand il ne devrait pas / ne devrait pas être utilisé.

  • ” Qu’est-ce que size_t en C? ” size_t pas la relation entre size_t et les autres types d’entiers. Sa réponse acceptée est juste une citation de Wikipedia, qui ne fournit aucune information au-delà de ce que j’ai déjà trouvé.

  • ” Quelle est la définition correcte de size_t ? ” Commence presque comme une copie de ma question, mais change de cap, demandant ensuite quand size_t doit être utilisé et pourquoi il a été introduit. Il a été clôturé comme un duplicata de la question précédente.

La norme C actuelle ne nécessite pas que size_t soit au moins aussi large qu’un int , et je suis sceptique à propos de toute version de la norme qui le fasse jamais. size_t doit pouvoir représenter tout nombre pouvant correspondre à la taille d’un object; si l’implémentation limite la taille des objects à 24 bits, alors size_t pourrait être un type non signé de 24 bits, indépendamment de l’ int .

L’avertissement GCC ne fait pas référence aux possibilités théoriques. Il vérifie une plate-forme matérielle particulière ainsi qu’un compilateur et un environnement d’exécution particuliers. Cela signifie que cela déclenche parfois le code qui tente d’être portable. (Il existe d’autres cas où le code portable déclenchera des avertissements GCC facultatifs.) Cela pourrait ne pas être ce que vous espériez que l’avertissement fît, mais il y a probablement des utilisateurs dont les attentes correspondent exactement au comportement mis en œuvre, et la norme ne fournit aucune directive. pour les avertissements du compilateur.


Comme OP le mentionne dans un commentaire, cet avertissement a une longue histoire. L’avertissement a été introduit dans la version 3.3.2 ou plus (en 2003), apparemment pas contrôlé par aucun drapeau -W . Ceci a été rapporté comme bug 12963 par un utilisateur qui pensait évidemment, comme vous, que l’avertissement décourage la programmation portable. Comme on peut le voir dans le rapport de bogue, divers responsables de GCC (et d’autres membres bien connus de la communauté) ont émis des opinions contradictoires mais fortement ressenties. (Il s’agit d’une dynamic commune dans les rapports de bogues open source.) Après plusieurs années, il a été décidé de contrôler les avertissements avec un indicateur et de ne pas l’activer par défaut ou dans le cadre de -Wall . Dans l’intervalle, l’option -W avait été renommée -Wextra et l’indicateur nouvellement créé ( -Wtype-limits ) était ajouté à la collection -Wextra . Pour moi, cela semble être la résolution correcte.


Le rest de cette réponse contient mon opinion personnelle.

-Wall , comme -Wall dans le manuel de GCC, n’active pas tous les avertissements. Il active ces avertissements “à propos de constructions jugées discutables par certains utilisateurs et faciles à éviter (ou à modifier pour éviter l’avertissement), même en conjonction avec des macros”. GCC peut détecter plusieurs autres conditions:

Notez que certains drapeaux d’avertissement ne sont pas impliqués par -Wall . Certains d’entre eux mettent en garde contre des constructions que les utilisateurs ne considèrent généralement pas comme douteuses, mais que vous voudrez peut-être vérifier occasionnellement; d’autres avertissent des constructions nécessaires ou difficiles à éviter dans certains cas, et il n’existe aucun moyen simple de modifier le code pour supprimer l’avertissement. Certains d’entre eux sont activés par -Wextra mais beaucoup d’entre eux doivent être activés individuellement.

Ces distinctions sont quelque peu arbitraires. Par exemple, je dois serrer les dents chaque fois que GCC décide de “suggérer des parenthèses autour de ‘&&’ au sein de ‘||'”. (Il ne semble pas que le besoin de suggérer des parenthèses autour de “+” à l’intérieur de “+” soit évident, ce qui ne me semble pas différent.) Mais je reconnais que nous avons tous des niveaux de confort différents avec la priorité des opérateurs, et pas tous. des suggestions de GCC concernant les parenthèses me semble excessif.

Mais dans l’ensemble, la distinction semble raisonnable. Il existe des avertissements qui sont généralement applicables, et ceux-ci sont activés avec -Wall , ce qui doit toujours être spécifié, car ces avertissements exigent presque toujours une action pour corriger une anomalie. Il existe d’autres avertissements qui pourraient être utiles dans des circonstances particulières, mais qui contiennent également beaucoup de faux positifs. ces avertissements doivent être examinés individuellement car ils ne correspondent pas toujours (ni même souvent) à un problème de votre code.

Je suis conscient que certaines personnes estiment que le simple fait que GCC sache avertir de certaines conditions suffit à exiger des mesures pour éviter cet avertissement. Tout le monde a droit à ses jugements stylistiques et esthétiques, et il est juste que de tels programmeurs ajoutent -Wextra à leurs drapeaux de construction. Je ne suis pas dans cette foule, cependant. À un moment donné dans un projet, je vais essayer une construction avec une grande collection d’avertissements facultatifs activés, et envisager de modifier ou non mon code sur la base des rapports, mais je ne souhaite vraiment pas passer mon temps de développement. penser à des non-problèmes chaque fois que je reconstruis un fichier. Le drapeau -Wtypes-limit entre dans cette catégorie pour moi.

Rien ne nécessite que le maximum de size_t soit supérieur à int . De telles architectures où SIZE_MAX est <= INT_MAX sont rares et je doute que GCC les supporte.

En ce qui concerne le correctif , vous pouvez utiliser #if :

 #if INT_MAX > SIZE_MAX if (u_value > SIZE_MAX) { /* Line 10 */ exit(EXIT_FAILURE); } #endif 

Je suis d’accord avec l’interprétation de Remo.D

size_t est spécifié pour être un entier standard non signé, mais la norme ne limite pas sa taille par rapport à aucun autre que celle spécifiant qu’elle doit pouvoir contenir au moins 65535 .

Il peut donc être de taille plus petite, égale ou plus grande que unsigned int .