En C ++, size_t
(ou, plus correctement, T::size_type
qui est "habituellement" size_t
; c'est-à-dire un unsigned
type) est utilisé comme valeur de retour pour size()
, l'argument de operator[]
, etc. (voir std::vector
, et. Al.)
D'un autre côté, les langages .NET utilisent int
(et, éventuellement, long
) dans le même but; en fait, les langages compatibles CLS ne sont pas requis pour prendre en charge les types non signés .
Étant donné que .NET est plus récent que C ++, quelque chose me dit qu'il peut y avoir des problèmes d' utilisation unsigned int
même pour des choses qui "ne peuvent pas" être négatives comme un index ou une longueur de tableau. L'approche C ++ est-elle un «artefact historique» pour la compatibilité descendante? Ou existe-t-il des compromis de conception réels et importants entre les deux approches?
Pourquoi est-ce important? Eh bien ... que dois-je utiliser pour une nouvelle classe multidimensionnelle en C ++; size_t
ou int
?
struct Foo final // e.g., image, matrix, etc.
{
typedef int32_t /* or int64_t*/ dimension_type; // *OR* always "size_t" ?
typedef size_t size_type; // c.f., std::vector<>
dimension_type bar_; // maybe rows, or x
dimension_type baz_; // e.g., columns, or y
size_type size() const { ... } // STL-like interface
};
-1
est renvoyé par les fonctions qui renvoient un index, pour indiquer «introuvable» ou «hors de portée». Il est également revenu desCompare()
fonctions (implémentationIComparable
). Un entier 32 bits est considéré comme le type de frappe pour un nombre général, pour ce que j'espère être des raisons évidentes.Réponses:
Oui. Pour certains types d'applications comme le traitement d'images ou le traitement de tableaux, il est souvent nécessaire d'accéder à des éléments relatifs à la position actuelle:
Dans ces types d'applications, vous ne pouvez pas effectuer de vérification de plage avec des entiers non signés sans réfléchir soigneusement:
Au lieu de cela, vous devez réorganiser votre expression de vérification de plage. Voilà la principale différence. Les programmeurs doivent également se souvenir des règles de conversion des nombres entiers. En cas de doute, relisez http://en.cppreference.com/w/cpp/language/operator_arithmetic#Conversions
De nombreuses applications n'ont pas besoin d'utiliser de très grands indices de tableau, mais elles doivent effectuer des vérifications de plage. De plus, beaucoup de programmeurs ne sont pas formés pour faire cette gymnastique de réarrangement d'expression. Une seule occasion manquée ouvre la porte à un exploit.
C # est en effet conçu pour les applications qui n'auront pas besoin de plus de 2 ^ 31 éléments par tableau. Par exemple, une application de feuille de calcul n'a pas besoin de traiter autant de lignes, de colonnes ou de cellules. C # gère la limite supérieure en ayant une arithmétique vérifiée facultative qui peut être activée pour un bloc de code avec un mot-clé sans jouer avec les options du compilateur. Pour cette raison, C # favorise l'utilisation d'un entier signé. Lorsque ces décisions sont considérées dans leur ensemble, cela fait sens.
C ++ est tout simplement différent et il est plus difficile d'obtenir du code correct.
En ce qui concerne l'importance pratique de permettre à l'arithmétique signée de supprimer une violation potentielle du "principe du moindre étonnement", un exemple en est OpenCV, qui utilise un entier signé 32 bits pour l'index des éléments de la matrice, la taille du tableau, le nombre de canaux de pixels, etc. Image le traitement est un exemple de domaine de programmation qui utilise fortement un index de tableau relatif. Le dépassement d'entier non signé (résultat négatif enveloppé autour) compliquera gravement la mise en œuvre de l'algorithme.
la source
Cette réponse dépend vraiment de qui va utiliser votre code et des normes qu'ils souhaitent voir.
size_t
est une taille entière avec un objectif:Ainsi, chaque fois que vous souhaitez travailler avec la taille des objets en octets, vous doit utiliser
size_t
. Maintenant, dans de nombreux cas, vous n'utilisez pas ces dimensions / index pour compter les octets, mais la plupart des développeurs choisissent de les utiliser à des finssize_t
de cohérence.Notez que vous devez toujours utiliser
size_t
si votre classe est conçue pour avoir l'apparence d'une classe STL. Toutes les classes STL de la spécification utilisentsize_t
. Il est valide pour que le compilateur typedefsize_t
soitunsigned int
, et il est également valide pour qu'il soit typé àunsigned long
. Si vous utilisezint
oulong
directement, vous finirez par rencontrer des compilateurs où une personne qui pense que votre classe a suivi le style de la STL est piégée parce que vous n'avez pas suivi la norme.Quant à l'utilisation de types signés, il y a quelques avantages:
int
, mais beaucoup plus difficile à encombrer le codeunsigned int
.int32_t
etuint32_t
). Cela peut simplifier l'interopérabilité des APILe gros inconvénient des types signés est évident: vous perdez la moitié de votre domaine. Un numéro signé ne peut pas compter autant qu'un numéro non signé. Lorsque C / C ++ est apparu, c'était très important. L'un devait être en mesure de gérer toutes les capacités du processeur, et pour ce faire, vous deviez utiliser des numéros non signés.
Pour les types d'applications .NET ciblés, le besoin d'un index non signé de domaine complet n'était pas aussi fort. De nombreux objectifs pour de tels nombres ne sont tout simplement pas valides dans un langage géré (le regroupement de mémoire vient à l'esprit). De plus, avec la sortie de .NET, les ordinateurs 64 bits étaient clairement l'avenir. Nous sommes loin d'avoir besoin de la gamme complète d'un entier 64 bits, donc sacrifier un bit n'est pas aussi douloureux qu'auparavant. Si vous avez vraiment besoin de 4 milliards d'index, vous passez simplement à l'utilisation d'entiers 64 bits. Au pire, vous l'exécutez sur une machine 32 bits et c'est un peu lent.
Je considère le métier comme un métier de commodité. S'il vous arrive d'avoir une puissance de calcul suffisante pour que cela ne vous dérange pas de gaspiller un peu de votre type d'index que vous n'utiliserez jamais, alors il est pratique de simplement taper
int
oulong
et de s'en éloigner. Si vous trouvez que vous vouliez vraiment ce dernier morceau, alors vous auriez probablement dû faire attention à la signature de vos numéros.la source
size()
wasreturn bar_ * baz_;
; cela ne crée-t-il pas maintenant un problème potentiel de débordement d'entier (bouclage) que je n'aurais pas si je n'utilisais passize_t
?bar_ * baz_
peut déborder un entier signé mais pas un entier non signé. En nous limitant au C ++, il convient de noter que le débordement non signé est défini dans la spécification, mais le débordement signé est un comportement indéfini, donc si l'arithmétique modulo des entiers non signés est souhaitable, utilisez-les certainement, car elle est réellement définie!size()
débordement de la multiplication signée , vous êtes dans la langue UB land. (et enfwrapv
mode, voir suivant :) Quand alors , avec juste un tout petit peu plus, il a débordé la multiplication non signée , vous en terre de bug de code utilisateur - vous renverriez une taille fausse. Je ne pense donc pas que les achats non signés achètent beaucoup ici.Je pense que la réponse de rwong ci-dessus met déjà très bien en évidence les problèmes.
J'ajouterai mon 002:
size_t
c'est-à-dire une taille qui ...... n'est requis que pour les indices de plage lorsque
sizeof(type)==1
, c'est-à-dire si vous avez affaire à deschar
types byte ( ). (Mais, notons-le, il peut être plus petit qu'un type ptr :xxx::size_type
pourrait être utilisé dans 99,9% des cas, même s'il s'agissait d'un type de taille signée. (comparerssize_t
)std::vector
and friends ait choisisize_t
un type non signé pour la taille et l'indexation est considéré par certains comme un défaut de conception. Je suis d'accord. (Sérieusement, prenez 5 minutes et regardez la foudre parler CppCon 2016: Jon Kalb "non signé: une ligne directrice pour un meilleur code" .)size_t
pour être cohérent avec la bibliothèque standard, ou utilisez (une signature )intptr_t
oussize_t
pour des calculs d'indexation faciles et moins sujets aux bogues.intptr_t
si vous voulez aller signé et que vous voulez la taille du mot machine, ou utilisezssize_t
.Pour répondre directement à la question, il ne s'agit pas entièrement d'un "artefact historique", car la question théorique de la nécessité de traiter plus de la moitié de ("l'indexation" ou) de l'espace d'adressage doit être, aehm, traitée d'une manière ou d'une autre dans un langage de bas niveau comme C ++.
Avec le recul, je, personnellement , pense, il est un défaut de conception que la bibliothèque standard utilise non signé
size_t
dans le lieu même où elle ne représente pas une taille de mémoire brute, mais une capacité de données typées, comme pour les collections:Je vais répéter le conseil de Jon ici:
(* 1) c'est-à-dire unsigned == bitmask, ne faites jamais de calcul dessus (ici frappe la première exception - vous pouvez avoir besoin d'un compteur qui encapsule - ce doit être un type non signé.)
(* 2) quantités signifiant quelque chose sur lequel vous comptez et / ou faites des calculs.
la source
ssize_t
, défini comme le pendentif signé ausize_t
lieu deintptr_t
, qui peut stocker n'importe quel pointeur (non membre) et pourrait donc être plus grand?size_t
définition. Voir size_t vs intptr et en.cppreference.com/w/cpp/types/size_t J'ai appris quelque chose de nouveau aujourd'hui. :-) Je pense que le reste des arguments tient, je vais voir si je peux réparer les types utilisés.J'ajouterai simplement que pour des raisons de performances, j'utilise normalement size_t, pour garantir que les erreurs de calcul provoquent un dépassement de capacité, ce qui signifie que les deux vérifications de plage (en dessous de zéro et au-dessus de size ()) peuvent être réduites à un:
en utilisant int signé:
en utilisant un entier non signé:
la source