Lorsque j'ai appris le langage C ++ pour la première fois, j'ai appris qu'en plus de int, float, etc., des versions plus ou moins grandes de ces types de données existaient dans le langage. Par exemple, je pourrais appeler une variable x
int x;
or
short int x;
La principale différence étant que short int prend 2 octets de mémoire tandis que int prend 4 octets, et short int a une valeur moindre, mais nous pourrions également l'appeler pour la rendre encore plus petite:
int x;
short int x;
unsigned short int x;
ce qui est encore plus restrictif.
Ma question ici est de savoir si c'est une bonne pratique d'utiliser des types de données distincts en fonction des valeurs que votre variable prend dans le programme. Est-ce une bonne idée de toujours déclarer des variables en fonction de ces types de données?
c++
data-structures
Bugster
la source
la source
unsigned
manière ou d'une autre fait qu'un entier occupe moins d'espace, ce qui est bien sûr faux. Il aura le même nombre de valeurs représentables discrètes (donner ou prendre 1 selon la façon dont le signe est représenté), mais simplement décalé exclusivement dans le positif.Réponses:
La plupart du temps, le coût de l'espace est négligeable et vous ne devriez pas vous en soucier, mais vous devez vous soucier des informations supplémentaires que vous donnez en déclarant un type. Par exemple, si vous:
Vous donnez une information utile à un autre développeur: le salaire ne peut pas être négatif.
La différence entre court, int, long va rarement causer des problèmes d'espace dans votre application. Il est plus probable que vous fassiez accidentellement l'hypothèse erronée qu'un nombre rentre toujours dans un type de données. Il est probablement plus sûr de toujours utiliser int sauf si vous êtes sûr à 100% que vos chiffres seront toujours très petits. Même dans ce cas, il est peu probable que vous économisiez une quantité notable d'espace.
la source
unsigned
dans ce cas est une mauvaise idée: non seulement le salaire ne peut pas être négatif, mais la différence entre deux salaires ne peut pas non plus être négative. (En général, utiliser unsigned pour autre chose que le bit-twiddling et avoir un comportement défini sur le débordement est une mauvaise idée.)L'OP n'a rien dit sur le type de système pour lequel ils écrivent des programmes, mais je suppose que l'OP pensait à un PC typique avec des Go de mémoire puisque C ++ est mentionné. Comme le dit l'un des commentaires, même avec ce type de mémoire, si vous avez plusieurs millions d'éléments d'un même type - comme un tableau - alors la taille de la variable peut faire une différence.
Si vous entrez dans le monde des systèmes embarqués - ce qui n'est pas vraiment hors de portée de la question, puisque l'OP ne le limite pas aux PC - alors la taille des types de données est très importante. Je viens de terminer un projet rapide sur un microcontrôleur 8 bits qui n'a que 8K mots de mémoire de programme et 368 octets de RAM. Là, évidemment, chaque octet compte. On n'utilise jamais une variable plus grande que nécessaire (à la fois du point de vue de l'espace et de la taille du code - les processeurs 8 bits utilisent beaucoup d'instructions pour manipuler les données 16 et 32 bits). Pourquoi utiliser un CPU avec des ressources aussi limitées? En grandes quantités, elles peuvent coûter aussi peu qu'un quart.
Je fais actuellement un autre projet intégré avec un microcontrôleur basé sur MIPS 32 bits qui a 512K octets de flash et 128K octets de RAM (et coûte environ 6 $ en quantité). Comme avec un PC, la taille des données "naturelles" est de 32 bits. Maintenant, il devient plus efficace, au niveau du code, d'utiliser des entiers pour la plupart des variables au lieu des caractères ou des courts-circuits. Mais encore une fois, tout type de tableau ou de structure doit être considéré si des types de données plus petits sont garantis. Contrairement à compilateurs pour les grands systèmes, il est plus probable des variables dans une structure seront être emballés sur un système embarqué. Je prends soin de toujours essayer de mettre toutes les variables 32 bits en premier, puis 16 bits, puis 8 bits pour éviter tout "trou".
la source
La réponse dépend de votre système. En règle générale, voici les avantages et les inconvénients de l'utilisation de types plus petits:
Avantages
Désavantages
Mon conseil est d'aimer ça:
Alternativement, vous pouvez utiliser le
int_leastn_t
ouint_fastn_t
de stdint.h, où le n est le nombre 8, 16, 32 ou 64.int_leastn_t
type signifie "Je veux que ce soit au moins n octets mais je me fiche que le compilateur l'alloue comme un type plus grand pour s'adapter à l'alignement ".int_fastn_t
signifie "Je veux que ce soit long de n octets, mais si cela rend mon code plus rapide, le compilateur doit utiliser un type plus grand que celui spécifié".Généralement, les différents types de stdint.h sont une bien meilleure pratique que plain
int
etc, car ils sont portables. L'intentionint
était de ne pas lui donner une largeur spécifiée uniquement pour le rendre portable. Mais en réalité, il est difficile de porter car vous ne savez jamais quelle sera sa taille sur un système spécifique.la source
Selon le fonctionnement du système d'exploitation spécifique, vous vous attendez généralement à ce que la mémoire soit allouée non optimisée de sorte que lorsque vous appelez un octet, ou un mot ou un autre petit type de données à allouer, la valeur occupe un registre entier tout cela est très posséder. Le fonctionnement de votre compilateur ou interprète pour interpréter ceci est cependant autre chose, donc si vous compilez un programme en C # par exemple, la valeur pourrait occuper physiquement un registre pour elle-même, mais la valeur sera vérifiée pour vous assurer que vous ne le faites pas essayez de stocker une valeur qui dépassera les limites du type de données voulu.
En termes de performances, et si vous êtes vraiment pédant à propos de telles choses, il est probablement plus rapide d'utiliser simplement le type de données qui correspond le mieux à la taille du registre cible, mais vous passez à côté de tout ce joli sucre syntaxique qui rend le travail avec les variables si facile .
Comment cela vous aide-t-il? Eh bien, c'est vraiment à vous de décider pour quel type de situation vous codez. Pour presque tous les programmes que j'ai jamais écrits, il suffit de faire simplement confiance à votre compilateur pour optimiser les choses et utiliser le type de données qui vous est le plus utile. Si vous avez besoin d'une grande précision, utilisez les types de données à virgule flottante plus grands. Si vous ne travaillez qu'avec des valeurs positives, vous pouvez probablement utiliser un entier non signé, mais pour la plupart, il suffit d'utiliser le type de données int.
Si toutefois vous avez des exigences de données très strictes, telles que l'écriture d'un protocole de communication ou une sorte d'algorithme de chiffrement, l'utilisation de types de données à plage vérifiée peut s'avérer très utile, en particulier si vous essayez d'éviter les problèmes liés aux dépassements / sous-dépassements de données ou des valeurs de données non valides.
La seule autre raison à laquelle je peux penser du haut de ma tête pour utiliser des types de données spécifiques est lorsque vous essayez de communiquer l'intention dans votre code. Si vous utilisez un raccourci par exemple, vous dites à d'autres développeurs que vous autorisez les nombres positifs et négatifs dans une très petite plage de valeurs.
la source
Comme l'a expliqué Scarfridge , il s'agit d'un
Tenter d'optimiser l'utilisation de la mémoire peut avoir un impact sur d'autres domaines de performances, et les règles d'or de l'optimisation sont les suivantes:
Afin de savoir si le moment est venu d'optimiser, il faut des analyses comparatives et des tests. Vous devez savoir où votre code est inefficace, afin de pouvoir cibler vos optimisations.
Afin de déterminer si la version optimisée du code est réellement meilleure que l'implémentation naïve à un moment donné, vous devez les comparer côte à côte avec les mêmes données.
N'oubliez pas que le fait qu'une implémentation donnée soit plus efficace sur la génération actuelle de CPU ne signifie pas qu'elle le sera toujours . Ma réponse à la question La micro-optimisation est-elle importante lors du codage? détaille un exemple d'expérience personnelle où une optimisation obsolète a entraîné un ralentissement de l'ordre de grandeur.
Sur de nombreux processeurs, les accès à la mémoire non alignés sont nettement plus coûteux que les accès à la mémoire alignés. Emballer quelques courts métrages dans votre structure peut simplement signifier que votre programme doit effectuer une opération de pack / unpack chaque fois que vous touchez l'une ou l'autre valeur.
Pour cette raison, les compilateurs modernes ignorent vos suggestions. Comme le commente Nikie :
Devinez votre compilateur à vos risques et périls.
Il y a une place pour de telles optimisations, lorsque vous travaillez avec des ensembles de données de téraoctets ou des microcontrôleurs intégrés, mais pour la plupart d'entre nous, ce n'est pas vraiment un problème.
la source
Ceci est une erreur. Vous ne pouvez pas faire d'hypothèses sur le nombre d'octets que chaque type contient, à part
char
être un octet et au moins 8 bits par octet, la taille de chaque type étant supérieure ou égale à la précédente.Les avantages en termes de performances sont incroyablement minuscules pour les variables de pile - ils seront probablement alignés / remplis de toute façon.
Pour cette raison,
short
etlong
n'ont pratiquement aucune utilité de nos jours, et vous êtes presque toujours mieux d'utiliserint
.Bien sûr, il y a aussi
stdint.h
ce qui est parfaitement bien à utiliser quandint
il ne le coupe pas. Si jamais vous allouez d'énormes tableaux d'entiers / structures, alors unintX_t
est logique car vous pouvez être efficace et vous fier à la taille du type. Ce n'est pas du tout prématuré car vous pouvez économiser des mégaoctets de mémoire.la source
long
peut être différent deint
. Si votre compilateur est LP64,int
32 bits etlong
64 bits, vous constaterez queint
s peut toujours être aligné sur 4 octets (mon compilateur le fait, par exemple).int64_t
int32_t
,,int_fast32_t
etlong
sont toutes de bonnes options,long long
est juste un gaspillage etint
non portable.Ce sera d'une sorte de POO et / ou de point de vue entreprise / application et pourrait ne pas être applicable dans certains domaines / domaines, mais je voudrais en quelque sorte évoquer le concept d' obsession primitive .
C'est une bonne idée d'utiliser différents types de données pour différents types d'informations dans votre application. Cependant, ce n'est probablement PAS une bonne idée d'utiliser les types intégrés pour cela, sauf si vous avez de sérieux problèmes de performances (qui ont été mesurés et vérifiés, etc.).
Si nous voulons modéliser les températures en Kelvin dans notre application, nous POUVONS utiliser un
ushort
ouuint
ou quelque chose de similaire pour indiquer que "la notion de degrés négatifs Kelvin est absurde et une erreur de logique de domaine". L'idée derrière cela est saine, mais vous n'allez pas jusqu'au bout. Ce que nous avons réalisé, c'est que nous ne pouvons pas avoir de valeurs négatives, donc c'est pratique si nous pouvons obtenir le compilateur pour nous assurer que personne n'attribue une valeur négative à une température Kelvin. Il est également vrai que vous ne pouvez pas effectuer d'opérations au niveau du bit sur les températures. Et vous ne pouvez pas ajouter une mesure de poids (kg) à une température (K). Mais si vous modélisez à la fois la température et la masse enuint
s, nous pouvons le faire.L'utilisation de types intégrés pour modéliser nos entités DOMAIN est susceptible de conduire à un code désordonné, à des vérifications manquées et à des invariants cassés. Même si un type capture QUELQUE partie de l'entité (ne peut pas être négatif), il en manquera forcément d'autres (ne peut pas être utilisé dans des expressions arithmétiques arbitraires, ne peut pas être traité comme un tableau de bits, etc.)
La solution est de définir de nouveaux types qui encapsulent les invariants. De cette façon, vous pouvez vous assurer que l'argent est de l'argent et que les distances sont des distances, et vous ne pouvez pas les additionner, et vous ne pouvez pas créer une distance négative, mais vous POUVEZ créer un montant d'argent (ou une dette) négatif. Bien sûr, ces types utiliseront les types intégrés en interne, mais cela est caché aux clients. Relativement à votre question sur les performances / la consommation de mémoire, ce genre de chose peut vous permettre de changer la façon dont les choses sont stockées en interne sans changer l'interface de vos fonctions qui opèrent sur vos entités de domaine, si vous découvrez que putain, a
short
est tout simplement trop putain grand.la source
Oui bien sûr. C'est une bonne idée d'utiliser
uint_least8_t
pour les dictionnaires, les énormes tableaux de constantes, les tampons, etc. Il est préférable d'utiliseruint_fast8_t
à des fins de traitement.uint8_least_t
(stockage) ->uint8_fast_t
(traitement) ->uint8_least_t
(stockage).Par exemple, vous prenez un symbole de 8 bits
source
, des codes de 16 bitsdictionaries
et quelque 32 bitsconstants
. Que vous traitez avec eux des opérations 10-15 bits et que vous sortez 8 bitsdestination
.Imaginons que vous devez traiter 2 gigaoctets de
source
. Le nombre d'opérations sur les bits est énorme. Vous recevrez un excellent bonus de performance si vous passez à des types rapides pendant le traitement. Les types rapides peuvent être différents pour chaque famille de CPU. Vous pouvez inclurestdint.h
et de l' utilisationuint_fast8_t
,uint_fast16_t
,uint_fast32_t
, etc.Vous pouvez utiliser
uint_least8_t
au lieu deuint8_t
pour la portabilité. Mais personne ne sait réellement quel processeur moderne utilisera cette fonctionnalité. La machine VAC est une pièce de musée. Alors peut-être que c'est une exagération.la source