web-dev-qa-db-fra.com

Quelles sont les meilleures pratiques concernant les entrées non signées?

J'utilise des ints non signés partout et je ne sais pas si je devrais. Cela peut aller des colonnes d'ID de clé primaire de la base de données aux compteurs, etc. Si un nombre ne doit jamais être négatif, alors j'utiliserai toujours un entier non signé.

Cependant, je remarque d'après le code des autres que personne d'autre ne semble le faire. Y a-t-il quelque chose de crucial que j'oublie?

Edit: Depuis cette question, j'ai également remarqué qu'en C, le retour de valeurs négatives pour les erreurs est monnaie courante plutôt que de lever des exceptions comme en C++.

45
wting

Y a-t-il quelque chose de crucial que j'oublie?

Lorsque les calculs impliquent à la fois des types signés et non signés ainsi que des tailles différentes, les règles de promotion de type peuvent être complexes et conduire à comportement inattend .

Je crois que c'est la principale raison pour laquelle Java omis les types int non signés.

28
Michael Borgwardt

Je pense que Michael a un point valide, mais IMO la raison pour laquelle tout le monde utilise int tout le temps (surtout dans for (int i = 0; i < max, i++) est que nous l'avons appris de cette façon. Lorsque chaque exemple d'un livre ' comment apprendre la programmation ' utilise int dans une boucle for, très peu remettra jamais en question cette pratique.

L'autre raison est que int est 25% plus court que uint, et nous sommes tous paresseux ... ;-)

18
Treb

L'encodage des informations de plage en types est une bonne chose. Il applique l'utilisation de nombres raisonnables au moment de la compilation.

De nombreuses architectures semblent avoir des instructions spécialisées pour gérer les conversions int -> float. La conversion de unsigned peut être plus lente (un tout petit peu) .

11
Benjamin Bannier

Mélanger des types signés et non signés peut vous plonger dans un monde de douleur. Et vous ne pouvez pas utiliser tous les types non signés car vous rencontrerez des choses qui ont une plage valide qui comprend des nombres négatifs ou qui ont besoin d'une valeur pour indiquer une erreur et -1 est le plus naturel. Ainsi, le résultat net est que de nombreux programmeurs utilisent tous les types d'entiers signés.

8
David Schwartz

J'utilise unsigned int en C++ pour les indices de tableau, principalement, et pour tout compteur qui commence à 0. Je pense qu'il est bon de dire explicitement "cette variable ne peut pas être négative".

7
quant_dev

Pour moi, les types sont beaucoup de communication. En utilisant explicitement un entier non signé, vous me dites que les valeurs signées ne sont pas des valeurs valides. Cela me permet d'ajouter quelques informations lors de la lecture de votre code en plus du nom de la variable. Idéalement, un type non anonyme m'en dirait plus, mais cela me donne plus d'informations que si vous aviez utilisé des ints partout.

Malheureusement, tout le monde n'est pas très conscient de ce que leur code communique, et c'est probablement la raison pour laquelle vous voyez des ints partout, même si les valeurs sont au moins non signées.

7
daramarak

Vous devriez vous en soucier lorsque vous avez affaire à un entier qui pourrait réellement approcher ou dépasser les limites d'un entier signé. Étant donné que le maximum positif d'un entier 32 bits est de 2147483647, vous devez utiliser un entier non signé si vous savez qu'il ne sera jamais négatif et b) pourrait atteindre 2147483488. Dans la plupart des cas, y compris les clés de base de données et les compteurs, je n'aborderai jamais ce type de nombres, donc je ne me soucie pas de me demander si le bit de signe est utilisé pour une valeur numérique ou pour indiquer le signe.

Je dirais: utilisez int sauf si vous savez que vous avez besoin d'un int non signé.

3
Joel Etherton

C'est un compromis entre simplicité et fiabilité. Plus il y a de bogues détectables au moment de la compilation, plus le logiciel est fiable. Différentes personnes et organisations sont sur des points différents dans ce spectre.

Si jamais vous effectuez une programmation haute fiabilité dans Ada, vous utilisez même différents types pour des variables telles que la distance en pieds par rapport à la distance en mètres, et le compilateur la signale si vous l'assignez accidentellement l'un à l'autre. C'est parfait pour programmer un missile guidé, mais exagéré (jeu de mots voulu) si vous validez un formulaire Web. Il n'y a pas nécessairement de problème dans les deux cas tant qu'il correspond aux exigences.

3
Karl Bielefeldt

Je suis enclin à être d'accord avec le raisonnement de Joel Etherton, mais j'arrive à la conclusion opposée. La façon dont je le vois, même si vous savez qu'il est peu probable que les nombres approchent les limites d'un type signé, si vous savez que les nombres négatifs ne se produiront pas, alors il y a très peu de raisons d'utiliser la variante signée d'un type.

Pour la même raison, j'ai utilisé, dans quelques instances sélectionnées, BIGINT (entier 64 bits) plutôt que INTEGER (entier 32 bits) dans les tables SQL Server. La probabilité que les données atteignent la limite de 32 bits dans un délai raisonnable est minuscule, mais si cela se produit, les conséquences dans certaines situations pourraient être assez dévastatrices. Assurez-vous simplement de mapper correctement les types entre les langues, ou vous allez vous retrouver avec une bizarrerie intéressante très loin sur la route ...

Cela dit, pour certaines choses, telles que les valeurs de clé primaire de base de données, signées ou non signifiées, cela n'a vraiment pas d'importance, car à moins que vous ne répariez manuellement des données cassées ou quelque chose dans ce sens, vous ne traitez jamais directement la valeur; c'est un identifiant, rien de plus. Dans ces cas, la cohérence est probablement plus importante que le choix exact de la signature. Sinon, vous vous retrouvez avec des colonnes de clés étrangères signées et d'autres non signées, sans aucun motif apparent - ou encore cette bizarrerie intéressante.

2
a CVn

Je recommanderais qu'en dehors des contextes de stockage et d'échange de données à espace limité, on devrait généralement utiliser des types signés. Dans la plupart des cas où un entier signé 32 bits serait trop petit mais qu'une valeur non signée 32 bits suffirait pour aujourd'hui, il ne faudra pas longtemps avant que la valeur non signée 32 bits ne soit pas assez grande non plus.

Les principaux moments où l'on doit utiliser des types non signés sont quand on assemble plusieurs valeurs en une plus grande (par exemple, convertir quatre octets en un nombre 32 bits) ou décomposer des valeurs plus grandes en plus petites (par exemple, stocker un nombre 32 bits en quatre octets ), ou lorsque l'on a une quantité qui devrait "rouler" périodiquement et qu'il faut y faire face (pensez à un compteur d'utilité résidentielle; la plupart d'entre eux ont suffisamment de chiffres pour garantir qu'ils ne seront pas susceptibles de basculer entre les lectures) s'ils sont lus trois fois par an, mais pas assez pour garantir qu'ils ne se renverseront pas pendant la durée de vie utile du compteur). Les types non signés ont souvent suffisamment de "bizarrerie" pour qu'ils ne soient utilisés que dans les cas où leur sémantique est nécessaire.

1
supercat

J'utilise des entiers non signés pour rendre mon code et son intention plus clairs. Une chose que je fais pour me prémunir contre les conversions implicites inattendues lorsque je fais de l'arithmétique avec des types signés et non signés est d'utiliser un court non signé (2 octets généralement) pour mes variables non signées. Ceci est efficace pour plusieurs raisons:

  • Lorsque vous faites de l'arithmétique avec vos variables courtes et littéraux non signés (qui sont de type int) ou variables de type int, cela garantit que la variable non signée sera toujours promue en int avant d'évaluer l'expression, car int a toujours un rang plus élevé que short . Cela évite tout comportement inattendu faisant de l'arithmétique avec des types signés et non signés, en supposant que le résultat de l'expression rentre dans un entier signé bien sûr.
  • La plupart du temps, les variables non signées que vous utilisez ne dépasseront pas la valeur maximale d'un short non signé de 2 octets (65 535)

Le principe général est que le type de vos variables non signées doit avoir un rang inférieur à celui des variables signées afin d'assurer la promotion vers le type signé. Vous n'aurez alors aucun comportement de débordement inattendu. De toute évidence, vous ne pouvez pas garantir cela tout le temps, mais (le plus souvent) il est possible de le garantir.

Par exemple, récemment, j'ai eu des boucles pour quelque chose comme ceci:

const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
    if((i-2)%cuint == 0)
    {
       //Do something
    }
}

Le littéral "2" est de type int. Si j'étais un entier non signé au lieu d'un court non signé, alors dans la sous-expression (i-2), 2 serait promu en entier non signé (puisque l'int. Non signé a une priorité plus élevée que l'int. Signé). Si i = 0, alors la sous-expression est égale à (0u-2u) = une valeur massive due au débordement. Même idée avec i = 1. Cependant, comme i est un court non signé, il est promu au même type que le littéral '2', qui est signé int, et tout fonctionne bien.

Pour plus de sécurité: dans les rares cas où l'architecture que vous implémentez sur int fait 2 octets, cela pourrait entraîner la promotion des deux opérandes de l'expression arithmétique en entier non signé dans le cas où la variable courte non signée ne correspond pas en entier signé de 2 octets, ce dernier ayant une valeur maximale de 32 767 <65 535. (Voir https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned pour plus de détails). Pour éviter cela, vous pouvez simplement ajouter un static_assert à votre programme comme suit:

static_assert(sizeof(int) == 4, "int must be 4 bytes");

et il ne compilera pas sur les architectures où int est de 2 octets.

1
AdmiralAdama