L'entier est-il trop utilisé comme type de données?

9

La plupart des développeurs d'applications utilisent-ils des entiers signés là où ils veulent vraiment utiliser des entiers non signés? Je le fais tout le temps, mes collègues aussi. Je n'ai pas vu beaucoup d'autres bases de code étendues (autres que Delphi VCL) et des exemples sur Internet utilisent généralement des entiers. Alors que les développeurs VCL utilisent leurs propres types de données (ce qui serait la façon la plus simple de déclarer des variables).

Quelque chose semble un peu terrible à propos de ce code

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

quand il pourrait être écrit comme

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

Fonctionnellement, ces enregistrements fonctionnent presque toujours de la même manière (et, espérons-le, continueront de fonctionner de la même manière dans Delphi 64 bits). Mais un très grand nombre aura des problèmes de conversion.

Mais il y a aussi des inconvénients à utiliser des entrées non signées. Principalement dû au fait qu'il est ennuyeux de mélanger les deux.

La vraie question est, est-ce une chose qui est réellement réfléchie ou incluse dans les meilleures pratiques? Est-ce généralement uniquement au développeur?

Peter Turner
la source
5
Peter, cherchez-vous uniquement des réponses spécifiques à Delphi?
Adam Lear
3
@Anna Comprendre le fonctionnement des types de données Delphi serait une excellente réponse. Je suis raisonnablement sûr que les programmeurs C pourraient comprendre et répondre à cette question.
Peter Turner

Réponses:

9

L'une des raisons pour lesquelles je n'utilise pas autant de types entiers non signés dans Delphi est qu'ils peuvent créer des problèmes lorsqu'ils sont mélangés avec des entiers signés. En voici un qui m'a mordu une fois:

for i := 0 to List.Count - 1 do
  //do something here

J'avais idéclaré comme un entier non signé, (après tout, c'est un index dans une liste qui commence à 0, il ne doit jamais être négatif, non?), Mais quand List.Countétait 0, il ne court-circuiterait pas la boucle comme prévu car 0 - 1évalue à un nombre positif très élevé. Oops!

Entre les problèmes de sécurité potentiels inhérents au mélange d'entiers signés et non signés et les problèmes de plage (si vous avez besoin de nombres positifs plus grands que high(signed whatever), il est fort probable que vous finirez également par avoir besoin de nombres positifs plus grands que high(unsigned whatever)trop, donc jusqu'à la prochaine taille plus grande au lieu de passer de signé à non signé de la même taille est généralement la bonne action,) Je n'ai vraiment pas trouvé trop d'utilisations pour les entiers non signés lors de la représentation de la plupart des données.

Mason Wheeler
la source
2
Quelque peu lié, l'un des risques majeurs de l'utilisation d'un type de données potentiellement plus petit que nécessaire (par opposition à simplement non signé vs signé) est que si la condition de sortie est plus grande que ce que vous aviez prévu, vous pouvez réellement vous retrouver avec une boucle infinie comme le compteur déborde encore et encore. Cela semble stupide avec le recul, mais j'ai déjà écrit un programme qui était censé parcourir toutes les valeurs d'octets possibles, et il m'a fallu environ 15 minutes pour finalement me convaincre qu'il n'était pas possible de le faire avec un compteur d'octets.
Aaronaught
@Aaronaught: Pas dans Delphi. (Du moins pas à moins que vous ne fassiez quelque chose de stupide comme désactiver la vérification de débordement intégrée.) Vous vous retrouverez avec une exception lorsque le compteur déborde, au lieu d'une boucle infinie. C'est toujours un bug, mais c'est beaucoup plus facile à retrouver.
Mason Wheeler
Si tu le dis. J'ai toujours désactivé la vérification du débordement dans Delphi; après avoir été bombardé à l'infini de faux positifs de choses comme les codes de hachage et les sommes de contrôle, je viens d'abandonner complètement cette "fonctionnalité". Mais je suppose que vous avez raison, il aurait détecté cette erreur spécifique.
Aaronaught
@Aaronaught: Eh bien oui, vous voudriez le désactiver pour des choses comme les codes de hachage et les sommes de contrôle qui sont spécifiquement conçues pour déborder et boucler. Mais pour les calculs à usage général qui ne sont pas conçus pour déborder et s'enrouler, c'est une caractéristique de sécurité importante et la désactiver est un peu comme conduire sans ceinture de sécurité.
Mason Wheeler
Vous l'avez peut-être oublié, mais les directives de vérification du débordement et du compilateur étaient incroyablement boguées dans les anciennes versions de Delphi. Je me souviens très bien de m'être arraché les cheveux à plusieurs reprises après avoir vu le débogueur s'arrêter directement au milieu d'un bloc {$ O -} / {$ O +} pour signaler joyeusement un débordement. Après un moment, je ne pouvais plus le supporter et je l'ai juste désactivé globalement. Encore une fois, oui, cela aurait attrapé ce problème, mais je ne pense toujours pas que cela vaut le nombre de faux positifs. À chacun, bien sûr!
Aaronaught
3

Pour être honnête, j'ai tendance à utiliser des entiers par habitude. Je me suis habitué au fait qu'ils offrent des plages suffisamment grandes pour la plupart des situations et autorisent des valeurs négatives (telles que -1). En effet, beaucoup de fois en utilisant octets / mot / raccourci serait plus approprié. En y réfléchissant, je peux me concentrer sur ces points:

  • Perspective. La taille de Tilemap est limitée à 192x192 tuiles, donc je pourrais utiliser l'octet pour adresser les tuiles et les boucles. Mais si la taille de la carte doit être augmentée, je devrai passer par chaque utilisation et la remplacer par exemple par word. Lorsque je dois autoriser des objets hors carte, je dois recommencer pour passer à smallint.

  • Boucles. C'est souvent que j'écris une boucle "de i: = 0 à Count-1", que se passe-t-il si "i" est octet et Count = 0 est que la boucle va de 0 à 255. Pas que je le veuille.

  • Uniforme. Il est plus facile de se souvenir et d'appliquer "var i: integer;" que d'arrêter dans chaque cas et de penser "Hm .. ici, nous avons affaire à une plage de 0..120 .. octet .. non, attendez, nous pourrions avoir besoin de -1 pour non initialisé .. raccourci .. attendez .. et si 128 est pas assez .. Arrgh! " ou "Pourquoi est-ce petit dans cet endroit, pas un shortint?"

  • Combiner. Lorsque j'ai besoin de combiner deux ou plusieurs classes ensemble, elles peuvent utiliser différents types de données à leurs fins, l'utilisation de types plus larges permet d'ignorer les conversions inutiles.

  • -1. Même lorsque les valeurs sont sur la plage 0..n-1, j'ai souvent besoin de définir une valeur "aucune valeur / inconnue / non initialisée / vide", ce qui est la pratique courante -1.

L'utilisation d'Integers permet d'ignorer tous ces problèmes, d'oublier l'optimisation de bas niveau là où elle n'est pas nécessaire, d'aller plus haut et de se concentrer sur des problèmes plus réels.

PS Quand dois-je utiliser d'autres types?

  • Compteurs, ils ne sont jamais négatifs et sont en lecture seule en dehors de leur classe.
  • Raisons de performances / mémoire, obligent à utiliser des types de données plus courts à certains endroits.
Kromster
la source
1

La meilleure pratique consiste à utiliser un type de données qui correspond aux besoins des données utilisées (données attendues).

Exemples C #: si je n'avais besoin que de 0 à 255, j'utiliserais un octet.

Si j'avais besoin de supporter 1 000 000 de négatifs et de positifs, alors int.

Plus grand que 4,2 milliards, alors utilisez un long.

En choisissant le type correct, le programme utilisera la quantité optimale de mémoire ainsi que différents types utilisent différentes quantités de mémoire.

Voici une référence C # int de MSDN.

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer
Jon Raynor
la source
En C # (ou .net en général), est-ce que long et ulong deviendraient 128 bits sur une machine à 128 bits? Parce que dans Delphi, le Integertype de données est 32 bits sur une machine 32 bits et sera apparemment 64 bits sur une machine 64 bits.
Peter Turner
1
@Peter Turner: Non, en C # intest juste un raccourci pour System.Int32, quelle que soit la machine sur laquelle le code fonctionne.
nikie
@nikie, est-ce juste type int System.Int32ou quelque chose dans ce sens? Pourrait-il être modifié aussi facilement dans une future version du framework?
Peter Turner
@Peter Turner / nikie (sizeof (int) .ToString ()); ==> Renvoie 4 (sizeof (Int64) .ToString ()); ==> Renvoie 8 Sur mon système d'exploitation Windows 64 bits. En tant que nikie, stats, un int est vraiment juste et Int32.
Jon Raynor
1
Une chose à noter est que tous les types ne sont pas conformes à la spécification de langage commun . uintest l'un de ces types non conformes, ce qui signifie qu'il ne doit pas être utilisé dans une API exposée publiquement pour éviter de casser la capacité à utiliser cette API dans des langages .NET autres que celui dans lequel la bibliothèque est écrite. C'est également la raison pour laquelle le framework .NET L'API elle-même utilise intoù le uintferait.
Adam Lear
1

Les types entiers non signés ne doivent être utilisés que pour représenter des nombres cardinaux dans les langues où ils représentent des nombres cardinaux. En raison de la façon dont les ordinateurs qui exécutaient C fonctionnaient, les types entiers non signés se comportaient comme des membres des anneaux algébriques mod-2 ^ n (ce qui signifie que les calculs qui débordaient seraient "enveloppés" de manière prévisible), et le langage spécifie que dans de nombreux cas, ces types sont tenus de se comporter comme des anneaux algébriques abstraits même lorsque ce comportement serait incompatible avec le comportement des nombres cardinaux ou des entiers mathématiques.

Si une plate-forme prend entièrement en charge des types distincts pour les nombres cardinaux et les anneaux algébriques, je suggère que les nombres cardinaux soient traités à l'aide de types de nombres cardinaux (et de choses qui doivent être enveloppées à l'aide des types d'anneaux). Non seulement ces types pouvaient stocker des nombres deux fois plus grands que les types signés, mais une méthode recevant un paramètre d'un tel type n'aurait pas à vérifier s'il était négatif.

Étant donné le manque relatif de types de nombres cardinaux, cependant, il est généralement préférable d'utiliser simplement des entiers pour représenter à la fois des nombres mathématiques et des nombres cardinaux.

supercat
la source