Question

J'ai lu un peu dans C Spec que des variables non signées (en particulier court peu signé int) effectuer un soi-disant enrouler autour Sur Integer Overflow, même si je n'ai rien trouvé sur des variables signées, sauf que je suis partie avec comportement non défini.

Mon professeur m'a dit que leurs valeurs étaient également enroulées (peut-être qu'il voulait juste dire GCC). Je pensais que les bits étaient tronqués et que les bits que je me laissais me laissent donner une valeur étrange!

Ce qui se passe et en quoi est-il différent des bits tronqués.

Était-ce utile?

La solution

Les variables entières signées n'ont pas de comportement enveloppant dans le langage C. Le débordement entier signé pendant les calculs arithmétiques produit comportement non défini. Remarque BTW que le compilateur GCC que vous avez mentionné est connu pour la mise en œuvre Sémantique de débordement stricte Dans les optimisations, ce qui signifie qu'il tire parti de la liberté fournie par de telles situations de comportement non définies: le compilateur GCC suppose que les valeurs entières signées ne s'enroulent jamais. Cela signifie que GCC se trouve en fait être l'un des compilateurs dans lesquels vous ne peux pas Comptez sur le comportement enveloppant des types entiers signés.

Par exemple, le compilateur GCC peut supposer que pour la variable int i la condition suivante

if (i > 0 && i + 1 > 0)

équivaut à un simple

if (i > 0)

C'est exactement ce Sémantique de débordement stricte moyens.

Les types entiers non signés implémentent l'arithmétique modulo. Le modulo est égal 2^NN est le nombre de bits dans la représentation de valeur du type. Pour cette raison, les types entiers non signés semblent effectivement enrouler sur un débordement.

Cependant, le langage C n'effectue jamais de calculs arithmétiques dans des domaines plus petits que celui de int/unsigned int. Taper unsigned short int que vous mentionnez dans votre question sera généralement promu pour taper int dans les expressions avant le début des calculs (en supposant que la gamme de unsigned short s'intègre dans la gamme de int). Ce qui signifie que 1) les calculs avec unsigned short int sera préformé dans le domaine de int, avec un débordement qui se produit quand int Les débordements, 2) le débordement pendant de tels calculs conduiront à un comportement non défini, et non au comportement de l'enroulement.

Par exemple, ce code produit une enveloppe

unsigned i = USHRT_MAX;
i *= INT_MAX; /* <- unsigned arithmetic, overflows, wraps around */

Alors que ce code

unsigned short i = USHRT_MAX;
i *= INT_MAX; /* <- signed arithmetic, overflows, produces undefined behavior */

conduit à un comportement indéfini.

Sinon int un débordement se produit et le résultat est converti en un unsigned short int type, il est à nouveau réduit par modulo 2^N, qui apparaîtra comme si la valeur était enroulée.

Autres conseils

Imaginez que vous avez un type de données qui n'a que 3 bits de large. Cela vous permet de représenter 8 valeurs distinctes, de 0 à 7. Si vous ajoutez 1 à 7, vous "envelopperez" à 0, car vous n'avez pas assez de bits pour représenter la valeur 8 (1000).

Ce comportement est bien défini pour les types non signés. Il est ne pas Bien défini pour les types signés, car il existe plusieurs méthodes pour représenter des valeurs signées, et le résultat d'un débordement sera interprété différemment en fonction de cette méthode.

Magnitude des signes: le bit le plus haut représente le signe; 0 pour positif, 1 pour négatif. Si mon type est à nouveau de trois bits de large, je peux représenter les valeurs signées comme suit:

000  =  0
001  =  1
010  =  2
011  =  3
100  = -0
101  = -1
110  = -2
111  = -3

Puisque un bit est pris en charge pour le signe, je n'ai que deux bits pour coder une valeur de 0 à 3. Si j'ajoute 1 à 3, je déborderai avec -0 pour le résultat. Oui, il y a deux représentations pour 0, une positive et une négative. Vous ne rencontrerez pas souvent la représentation des signes de signature.

Complément One's: la valeur négative est la valeur positive de la valeur positive. Encore une fois, en utilisant le type à trois bits:

000  =  0
001  =  1
010  =  2
011  =  3
100  = -3
101  = -2
110  = -1 
111  = -0

J'ai trois bits pour coder mes valeurs, mais la plage est [-3, 3]. Si j'ajoute 1 à 3, je déborderai de -3 en conséquence. Ceci est différent du résultat de la magntude des signes ci-dessus. Encore une fois, il existe deux encodages pour 0 en utilisant cette méthode.

Complément à deux: la valeur négative est l'inverse bitwise de la valeur positive, plus 1. dans le système à trois bits:

000  =  0
001  =  1
010  =  2
011  =  3
100  = -4
101  = -3
110  = -2
111  = -1

Si j'ajoute 1 à 3, je déborderai de -4 en conséquence, ce qui est différent des deux méthodes précédentes. Notez que nous avons une plage de valeurs légèrement plus grande [-4, 3] et une seule représentation pour 0.

Le complément de deux est probablement la méthode la plus courante pour représenter les valeurs signées, mais ce n'est pas la seule, donc la norme C ne peut faire aucune garantie de ce qui se passera lorsque vous débordez d'un type entier signé. Alors ça laisse le comportement indéfini Le compilateur n'a donc pas à gérer l'interprétation de plusieurs représentations.

La comportement non défini vient des problèmes de portabilité précoce lorsque les types entiers signés pourraient être représentés soit comme signe et ampleur, complément ou complément de deux.

De nos jours, toutes les architectures représentent les entiers comme un complément de deux qui s'enroule. Mais soyez prudent: comme votre compilateur a raison de supposer que vous n'exécuterez pas un comportement indéfini, vous pouvez rencontrer des bogues étranges lorsque l'optimisation est activée.

Dans un entier 8 bits signé, la définition intuitive de Wrap Around peut sembler passer de +127 à -128 - dans Two's Complément Binary: 0111111 (127) et 1000000 (-128). Comme vous pouvez le voir, c'est le progrès naturel de l'incrémentation des données binaires - sans qu'il représente un entier, signé ou non signé. Contre intuitivement, le débordement réel a lieu lors du passage de -1 (11111111) à 0 (00000000) dans le sens de l'enveloppe de l'entier non signé.

Cela ne répond pas à la question plus profonde du comportement correct lorsqu'un entier signé déborde car il n'y a pas de comportement "correct" selon la norme.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top