68 votes

Pourquoi préférer le langage signé au langage non signé en C++ ?

J'aimerais mieux comprendre pourquoi choisir int plus unsigned ?

Personnellement, je n'ai jamais aimé les valeurs signées, à moins qu'elles n'aient une raison valable. Par exemple, le nombre d'éléments dans un tableau, ou la longueur d'une chaîne de caractères, ou la taille d'un bloc de mémoire, etc. Une telle valeur n'a aucune signification possible. Pourquoi préférer int alors qu'elle est trompeuse dans tous ces cas ?

Je pose cette question parce que Bjarne Stroustrup et Chandler Carruth ont tous les deux conseillé de privilégier l'utilisation de l'Internet. int plus unsigned ici (environ 12:30') .

Je comprends l'argument en faveur de l'utilisation de int plus short o long - int est la largeur de données "la plus naturelle" pour l'architecture de la machine cible.

Mais la différence entre signé et non signé m'a toujours ennuyé. Les valeurs signées sont-elles réellement plus rapides sur les architectures de processeurs modernes ? Qu'est-ce qui les rend plus rapides ?

40voto

Conformément aux demandes formulées dans les commentaires : Je préfère int au lieu de unsigned parce que...

  1. il est plus court (je suis sérieux !)

  2. c'est plus générique et plus intuitif (c'est-à-dire que j'aime pouvoir supposer que 1 - 2 est -1 et non pas un nombre obscur et énorme)

  3. que faire si je veux signaler une erreur en renvoyant une valeur hors plage ?

Il y a bien sûr des contre-arguments, mais ce sont les principales raisons pour lesquelles j'aime déclarer mes nombres entiers comme int au lieu de unsigned . Bien sûr, ce n'est pas toujours le cas, dans d'autres cas, une unsigned est simplement un meilleur outil pour une tâche donnée, je réponds simplement à la question "pourquoi quelqu'un préfèrerait-il la signature par défaut".

32voto

Prashant Kumar Points 5220

Permettez-moi de paraphraser la vidéo, car les experts l'ont dit de manière succincte.

Andrei Alexandrescu :

  • Il n'y a pas de ligne directrice simple.
  • Dans la programmation des systèmes, nous avons besoin d'entiers de tailles et de signatures différentes.
  • De nombreuses conversions et règles obscures régissent l'arithmétique (comme pour auto ), il convient donc d'être prudent.

Chandler Carruth :

  • Voici quelques exemples simples
    1. Utilisez des entiers signés, sauf si vous avez besoin d'une arithmétique en complément à deux ou d'un modèle de bits.
    2. Utilisez le plus petit nombre entier possible.
    3. Dans le cas contraire, utiliser int si vous pensez pouvoir compter les éléments, et un entier de 64 bits si le nombre d'éléments est supérieur à celui que vous souhaitez compter.
  • Cessez de vous inquiéter et utilisez des outils qui vous indiquent quand vous avez besoin d'un autre type ou d'une autre taille.

Bjarne Stroustrup :

  • Utilice int jusqu'à ce que vous ayez une raison de ne pas le faire.
  • N'utiliser que les caractères non signés pour les motifs de bits.
  • Ne jamais mélanger signé et non signé

Mis à part la méfiance à l'égard des règles de signature, voici ce que les experts m'ont appris en une phrase :

Utilisez le type approprié, et si vous ne savez pas, utilisez un int jusqu'à ce que vous sachiez.

19voto

user4815162342 Points 27348

Plusieurs raisons à cela :

  1. Arithmétique sur unsigned donne toujours un résultat non signé, ce qui peut être un problème lors de la soustraction de quantités entières qui peuvent raisonnablement donner un résultat négatif - pensez à la soustraction de quantités d'argent pour obtenir le solde, ou d'indices de tableaux pour obtenir la distance entre les éléments. Si les opérandes sont non signés, vous obtenez un résultat parfaitement défini, mais presque certainement dépourvu de sens, et une valeur de result < 0 sera toujours fausse (ce dont les compilateurs modernes vous avertissent heureusement).

  2. unsigned a la désagréable propriété de contaminer l'arithmétique lorsqu'il est mélangé à des entiers signés. Ainsi, si vous additionnez un entier signé et un entier non signé et que vous demandez si le résultat est supérieur à zéro, vous risquez de vous faire piquer, en particulier lorsque le type intégral non signé est caché derrière un élément de type typedef .

18voto

AndreyT Points 139512

Il n'y a aucune raison de préférer signed plus unsigned Certains pensent que les programmeurs moyens ne sont pas assez compétents et/ou attentifs pour écrire un code correct en termes de unsigned types. C'est souvent le principal raisonnement utilisé par les différents "intervenants", quel que soit leur degré de respect.

En réalité, les programmeurs compétents développent et/ou apprennent rapidement l'ensemble des idiomes et des compétences de base en matière de programmation qui leur permettent d'écrire un code correct en termes de types intégraux non signés.

Notez également que les différences fondamentales entre la sémantique signée et non signée sont toujours présentes (sous une forme superficiellement différente) dans d'autres parties du langage C et C++, comme l'arithmétique des pointeurs et l'arithmétique des itérateurs. Ce qui signifie que dans le cas général, le programmeur n'a pas vraiment la possibilité d'éviter de traiter les questions spécifiques à la sémantique non signée et les "problèmes" qui en découlent. En d'autres termes, que vous le vouliez ou non, vous devez apprendre à travailler avec des plages qui se terminent brusquement à leur extrémité gauche et qui se terminent ici (et non quelque part au loin), même si vous évitez catégoriquement d'utiliser des chaînes de caractères non signées. unsigned entiers.

De plus, comme vous le savez probablement, de nombreuses parties de la bibliothèque standard s'appuient déjà sur les éléments suivants unsigned les types de nombres entiers. Forcer l'arithmétique signée dans le mélange, au lieu d'apprendre à travailler avec l'arithmétique non signée, n'aboutira qu'à un code désastreusement mauvais.

La seule réel raison de préférer signed dans certains contextes, ce qui me vient à l'esprit, c'est que dans un code mixte entier/floating-point signed Les formats entiers sont généralement directement pris en charge par le jeu d'instructions FPU, tandis que les formats entiers sont directement pris en charge par le jeu d'instructions FPU. unsigned ne sont pas du tout pris en charge, ce qui oblige le compilateur à générer du code supplémentaire pour les conversions entre les valeurs à virgule flottante et les formats unsigned valeurs. Dans un tel code signed pourraient être plus performants.

Mais en même temps, dans un code purement entier unsigned peuvent être plus performants que les types signed types. Par exemple, la division des nombres entiers nécessite souvent un code correctif supplémentaire afin de satisfaire aux exigences de la spécification du langage. La correction n'est nécessaire que dans le cas d'opérandes négatifs, ce qui gaspille des cycles de l'unité centrale dans des situations où les opérandes négatifs ne sont pas vraiment utilisés.

Dans ma pratique, je m'attache à unsigned chaque fois que je le peux, et j'utilise signed seulement si je dois vraiment le faire.

9voto

supercat Points 25534

Les types intégraux du langage C et de nombreux langages qui en dérivent ont deux utilisations générales : représenter des nombres ou représenter des membres d'un anneau algébrique abstrait. Pour ceux qui ne sont pas familiers avec l'algèbre abstraite, la notion primaire derrière un anneau est que l'addition, la soustraction ou la multiplication de deux éléments d'un anneau doit produire un autre élément de cet anneau - il ne doit pas se planter ou produire une valeur en dehors de l'anneau. Sur une machine 32 bits, l'addition de 0x12345678 non signé à 0xFFFFFF non signé ne "déborde" pas - elle donne simplement le résultat 0x12345677 qui est défini pour l'anneau des entiers congruents mod 2^32 (parce que le résultat arithmétique de l'addition de 0x12345678 à 0xFFFFFFFF, c'est-à-dire 0x112345677, est congru à 0x12345677 mod 2^32).

D'un point de vue conceptuel, les deux objectifs (représenter des nombres ou représenter des membres de l'anneau des entiers congrus mod 2^n) peuvent être servis à la fois par des types signés et non signés, et de nombreuses opérations sont les mêmes pour les deux cas d'utilisation, mais il y a quelques différences. Entre autres, une tentative d'addition de deux nombres ne devrait pas produire autre chose que la somme arithmétique correcte. Bien que l'on puisse se demander si un langage devrait être tenu de générer le code nécessaire pour garantir que ce ne sera pas le cas (par exemple, qu'une exception sera levée à la place), on pourrait affirmer que pour le code qui utilise des types intégraux pour représenter les nombres un tel comportement serait préférable à l'obtention d'une valeur arithmétiquement incorrecte et les compilateurs ne devraient pas être empêchés d'agir de la sorte.

Les responsables de la mise en œuvre des normes C ont décidé d'utiliser des types d'entiers signés pour représenter les nombres et des types non signés pour représenter les membres de l'anneau algébrique des entiers congruents mod 2^n. En revanche, Java utilise des entiers signés pour représenter les membres de ces anneaux (bien qu'ils soient interprétés différemment dans certains contextes ; les conversions entre des types signés de taille différente, par exemple, se comportent différemment de celles entre des types non signés) et Java ne dispose ni d'entiers non signés ni de types primitifs intégraux qui se comportent comme des nombres dans tous les cas non exceptionnels.

Si une langue offrait le choix entre des représentations signées et non signées pour les nombres et les anneaux algébriques, il pourrait être logique d'utiliser des nombres non signés pour représenter des quantités qui seront toujours positives. Toutefois, si les seuls types non signés représentent les membres d'un anneau algébrique et que les seuls types qui représentent les nombres sont les types signés, alors même si une valeur est toujours positive, elle doit être représentée à l'aide d'un type conçu pour représenter les nombres.

Par ailleurs, la raison pour laquelle (uint32_t)-1 est 0xFFFFFFFF provient du fait que convertir une valeur signée en valeur non signée équivaut à ajouter un zéro non signé, et que l'ajout d'un entier à une valeur non signée est défini comme l'ajout ou la soustraction de sa magnitude à la valeur non signée selon les règles de l'anneau algébrique qui spécifient que si X=Y-Z, alors X est le seul et unique membre de cet anneau tel que X+Z=Y. En mathématiques non signées, 0xFFFFFFFF est le seul nombre qui, ajouté à 1 non signé, donne zéro non signé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X