Comment l’assemblage affecte-t-il un nombre négatif à un entier non signé?

J’ai appris sur 2's Complement et non signé et signé int. J’ai donc décidé de tester mes connaissances, pour autant que je sache qu’un nombre négatif est stocké dans 2's complement à 2's complement sorte que l’addition et la soustraction n’auraient pas un algorithme différent et que les circuits seraient simples.

Maintenant si j’écris

 int main() { int a = -1 ; unsigned int b = - 1 ; printf("%d %u \n %d %u" , a ,a , b, b); } 

La sortie peut être -1 4294967295 -1 4294967295 . Maintenant, j’ai regardé le motif de bits et diverses choses et puis j’ai réalisé que -1 en complément à 2 est 11111111 11111111 11111111 11111111 , donc quand je l’interprète en utilisant% d, il donne -1 , mais quand j’interprète en utilisant %u , traitez-le comme un nombre positif et il en 4294967295 . J’ai vérifié l’assemblage du code est

 .LC0: .ssortingng "%d %u \n %d %u" main: push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR [rbp-4], -1 mov DWORD PTR [rbp-8], -1 mov esi, DWORD PTR [rbp-8] mov ecx, DWORD PTR [rbp-8] mov edx, DWORD PTR [rbp-4] mov eax, DWORD PTR [rbp-4] mov r8d, esi mov esi, eax mov edi, OFFSET FLAT:.LC0 mov eax, 0 call printf mov eax, 0 leave ret 

Maintenant, ici -1 est déplacé vers le registre à la fois en heures non signée et signée. Ce que je veux savoir si la réinterprétation n’est que l’important, alors pourquoi avons-nous deux types unsigned et signed , c’est la printf format printf %d et %u qui compte?

En outre, que se passe-t-il lorsque j’assigne un nombre négatif à un entier non signé (j’ai appris que l’initialiseur convertit cette valeur de int en unsigned int .), Mais je n’ai rien vu de tel dans le code assembleur. Alors qu’est-ce qui se passe vraiment ??

Et comment Machine sait-elle quand elle doit faire 2's complement à 2's complement et sinon, voit-elle le signe négatif et effectue-t-elle 2's complement ?

J’ai lu à peu près toutes les questions et j’ai trouvé une réponse qui pourrait faire double emploi, mais je ne pouvais pas trouver de solution satisfaisante.

Les documents signés et non signés sont des morceaux de mémoire et, en fonction des opérations, leur comportement est déterminant.

Cela ne fait aucune différence lors de l’addition ou de la soustraction, car les opérations sont identiques à cause du complément à 2.

Il importe de comparer deux nombres: -1 est inférieur à 0 alors que 4294967295 ne l’est évidemment pas.

À propos de la conversion – pour la même taille, il faut simplement un contenu variable et le déplace vers un autre – donc 4294967295 devient -1. Pour une taille plus grande, il est d’abord signé étendu, puis le contenu est déplacé.

Comment fonctionne la machine maintenant – selon les instructions que nous utilisons. Les machines ont soit des instructions différentes pour comparer signé et non signé, soit elles fournissent des indicateurs différents (x86 a Carry pour dépassement de capacité non signé et Overflow pour dépassement de signature).

De plus, notez que C est assoupli dans la manière dont les numéros signés sont stockés, ils ne doivent pas nécessairement être complémentaires. Mais de nos jours, toutes les architectures courantes stockent le signé comme ceci.

Il existe quelques différences entre les types signés et non signés:

  1. Les comportements des opérateurs < , <= , > , >= , / , % et >> sont tous différents lorsqu'il s'agit de nombres signés et non signés.

  2. Les compilateurs ne doivent pas se comporter de manière prévisible si un calcul sur une valeur signée dépasse la plage de ce type. Même en utilisant des opérateurs qui se comporteraient de manière identique avec des valeurs signées et non signées dans tous les cas définis, certains compilateurs se comporteront de manière "intéressante". Par exemple, un compilateur donné x+1 > y pourrait le remplacer par x>=y si x est signé, mais pas si x n'est pas signé.

Comme exemple plus intéressant, sur un système où "short" est 16 bits et "int" 32 bits, un compilateur ayant la fonction suivante:

 unsigned mul(unsigned short x, unsigned short y) { return x*y; } 

pourrait supposer qu'aucune situation ne pourrait jamais se produire où le produit dépasserait 2147483647. Par exemple, s'il voyait la fonction invoquée comme unsigned x = mul(y,65535); et y était un unsigned short , il peut omettre ailleurs du code qui ne serait pertinent que si y était supérieur à 37268.

Il semble que vous sembliez avoir oublié le fait que premièrement, 0101 = 5 en valeurs entières signées et non signées et que deuxièmement, vous avez atsortingbué un nombre négatif à un entier non signé – quelque chose que votre compilateur peut être assez intelligent pour réaliser et, par conséquent, corriger à un int signé.

Définir un unsigned int sur -5 devrait techniquement causer une erreur car les inigned unsigned ne peuvent pas stocker de valeurs inférieures à 0.

Vous pourriez mieux le comprendre lorsque vous essayez d’atsortingbuer une valeur négative à un entier non signé de taille supérieure. Le compilateur génère le code d’assemblage pour l’extension de signe lors du transfert de la valeur négative de petite taille vers un entier non signé de taille supérieure.

voir cet article de blog pour une explication au niveau de l’assemblage.

Le choix de la représentation entière signée est laissé à la plate-forme. La représentation s’applique à la fois aux valeurs négatives et non négatives – par exemple, si 1101 2 (-5) est le complément à deux de 0101 2 (5), alors 0101 2 (5) est également le complément à deux de 1101 2 (-5 ).

La plate-forme peut ou non fournir des instructions distinctes pour les opérations sur les entiers signés et non signés. Par exemple, x86 fournit différentes instructions de multiplication et de division pour les idiv signés ( idiv et imul ) et non signés ( div et mul ), mais utilise les mêmes instructions d’addition ( add ) et de soustraction ( sub ) ( sub ).

De même, x86 fournit une instruction de comparaison unique ( cmp ) pour les entiers signés et non signés.

Les opérations arithmétiques et de comparaison définiront un ou plusieurs indicateurs de registre d’état (report, débordement, zéro, etc.). Celles-ci peuvent être utilisées différemment lorsqu’il s’agit de mots censés représenter des valeurs signées par rapport à des valeurs non signées.

En ce qui concerne printf , vous avez absolument raison de dire que le spécificateur de conversion détermine si le motif binary 0xFFFF est affiché sous la forme -1 ou 4294967295 , mais n’oubliez pas que si le type de l’argument ne correspond pas à l’attente du spécificateur de conversion, alors le comportement est indéfini. Utiliser %u pour afficher un entier négatif signed int peut ou non vous donner la valeur non signée équivalente attendue.