web-dev-qa-db-fra.com

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 de tenter d'essayer . Mon intention est à peu près égale de créer parties-créer-utile-bibliothèque et apprendre-C-Edge-cas.

Ma fonction int- to -size_t déclenche un avertissement GCC -Wtype-limits qui déclare que je ne devrais pas vérifier si une int est supérieure à SIZE_MAX, car elle ne sera jamais vraie . (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 <stdint.h>  /* SIZE_MAX */
#include <stdlib.h>  /* exit EXIT_FAILURE size_t */

extern size_t i2st(int value) {
    if (value < 0) {
        exit(EXIT_FAILURE);
    }
    // Safe to cast --- not too small.
    unsigned int const u_value = (unsigned int) value;
    if (u_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 faux" Sur une combinaison particulière de matériel et de compilateur .)

La norme C

La norme C 1999 ne semble pas exclure qu'une int soit supérieure à 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 <stddef.h> (et autres en-têtes)".

La section "7.17 Définitions communes <stddef.h>" 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 d'entiers" Est plus utile ---it définit "Limite de size_t" comme:

SIZE_MAX65535

... signification SIZE_MAX pourrait être aussi petite que 65535 . Une int (signé ou non signé) pourrait être beaucoup plus grande que cela, selon le matériel et le compilateur.

Débordement de pile

La réponse acceptée à " unsigned int VS. size_t " Semble corroborer mon interprétation (Soulignement ajouté):

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 "Section 7.17" De la norme C que j'ai déjà citée.

Autres documents

Mes recherches ont mis en évidence le document du groupe ouvert " Neutralité de la taille des données et prise en charge 64 bits ", Qui figure sous "Modèles de données 64 bits" (Accentuation ajoutée):

ISO/IEC 9899: 1990, Langages de programmation - C (ISO C) laissé la définition du short int, du int, du long int et du pointer délibérément vague [...] Les seules contraintes étaient que ints ne doit pas être inférieur à shorts, et longs ne doit pas être inférieur à ints, 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 une int contre SIZE_MAX est en effet futile ... mais le présent document ne cite pas les chapitres, alors je ne peux pas dire comment ses auteurs sont arrivés à leur conclusion ... propre "Spécification de base version 7" sys/types.h docs

Ma question

Je comprends que size_t ne sera probablement pas plus étroit qu'un int, mais le standard C garantit-t-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 deux des questions plus générales sur ce que size_t est censé représenter et à quel moment il devrait/ne devrait pas être utilisé.

  • " Qu'est-ce que size_t en C? " N'aborde 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, puis change de cap, demandant à Quand size_t devrait être utilisé et pourquoi il a été introduit . Il s'agissait d'un duplicata de la question précédente.

7
Kevin J. Chase

La norme C actuelle n'exige pas que size_t soit au moins aussi large qu'une 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 objet; si l'implémentation limite la taille des objets à une largeur de 24 bits, alors size_t pourrait être un type non signé de 24 bits, quelle que soit la valeur de 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éclenche des avertissements GCC facultatifs.) Cela pourrait ne pas être ce que vous espériez que l'avertissement le fasse, mais il existe 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 non contrôlé par aucun drapeau -W. Cela a été rapporté comme bug 12963 par un utilisateur qui a évidemment estimé, 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 dynamique 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. Entre-temps, l'option -W a été renommée -Wextra et l'indicateur nouvellement créé (-Wtype-limits) a été ajouté à la collection -Wextra. Pour moi, cela semble être la résolution correcte.


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

-Wall, comme indiqué dans le manuel de GCC, n'active pas les avertissements tous. 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" && "dans" || ". (Il ne semble pas que le besoin de suggérer des parenthèses autour de "+", ce qui ne me semble pas différent, entre parenthèses.) 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, qui devrait être spécifié always, car ces avertissements exigent presque toujours une action visant à corriger une lacune. 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 pensent 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 tombe dans cette catégorie pour moi.

8
rici

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 n’importe lequel d’entre elles.

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

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

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

size_t est spécifié comme étant un entier standard non signé, mais la norme ne limite pas sa taille par rapport à aucun autre, sauf en indiquant qu'elle doit pouvoir contenir au moins 65535.

Il peut donc être plus petit, égal ou plus grand que unsigned int.

1
Cory Nelson