146 votes

Conversion de Signed en unsigned en C - est-ce toujours sûr ?

Supposons que j'ai le code C suivant.

unsigned int u = 1234;
int i = -5678;

unsigned int result = u + i;

Quelles conversions implicites ont lieu ici, et ce code est-il sûr pour toutes les valeurs de u y i ? (Sûr, dans le sens où même si résultat dans cet exemple débordera vers un nombre positif énorme, je pourrais le retranscrire en un numéro d'identification de l'utilisateur. int et obtenir le vrai résultat).

1voto

Taylor Price Points 371

Comme il a été répondu précédemment, vous pouvez effectuer des castings entre signés et non signés sans problème. Le cas limite pour les entiers signés est -1 (0xFFFFFFFF). Essayez d'additionner et de soustraire à partir de cette valeur et vous constaterez que vous pouvez effectuer un transfert inverse et que le résultat est correct.

Cependant, si vous devez faire des allers-retours, je vous conseille fortement de nommer vos variables de manière à ce que leur type soit clair, par exemple :

int iValue, iResult;
unsigned int uValue, uResult;

Il est bien trop facile de se laisser distraire par des questions plus importantes et d'oublier quelle variable correspond à quel type si elles sont nommées sans indication. Vous ne voulez pas faire un cast vers un unsigned et ensuite l'utiliser comme un index de tableau.

0voto

plugwash Points 795

Quelles sont les conversions implicites qui se passent ici,

i sera converti en un entier non signé.

et ce code est-il sûr pour toutes les valeurs de u et i ?

Sûr dans le sens d'être bien défini oui (voir https://stackoverflow.com/a/50632/5083516 ).

Les règles sont écrites dans un langage standard difficile à lire, mais quelle que soit la représentation utilisée dans l'entier signé, l'entier non signé contiendra une représentation de complément à 2 du nombre.

L'addition, la soustraction et la multiplication fonctionnent correctement sur ces nombres, ce qui donne un autre nombre entier non signé contenant un nombre complémentaire à deux chiffres représentant le "résultat réel".

La division et le transfert vers des types d'entiers non signés plus grands auront des résultats bien définis, mais ces résultats ne seront pas des représentations en complément à 2 du "résultat réel".

(Sûr, dans le sens où même si le résultat dans cet exemple débordera sur un énorme nombre positif, je pourrais le retransformer en un int et obtenir le vrai résultat).

Alors que les conversions de signé à non signé sont définies par la norme, l'inverse est défini par l'implémentation ; gcc et msvc définissent la conversion de telle sorte que vous obtiendrez le "vrai résultat" lors de la conversion d'un nombre en complément à 2 stocké dans un entier non signé en un entier signé. Je pense que vous ne trouverez un autre comportement que sur des systèmes obscurs qui n'utilisent pas le complément à 2 pour les entiers signés.

https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx

-19voto

Elite Mx Points 50

Des réponses horribles à profusion

Ozgur Ozcitak

Lorsque vous passez du signé au non signé (et vice versa), la fonction interne interne du nombre ne change pas change pas. Ce qui change, c'est la façon dont le compilateur interprète le bit de signe.

C'est complètement faux.

Mats Fredriksson

Quand un non signé et un signé sont ajoutées (ou toute autre opération binaire) binaire), les deux sont implicitement implicitement converties en non signées, ce qui dans ce cas, le résultat serait énorme.

C'est également faux. Les ints non signés peuvent être promus en ints s'ils ont une précision égale en raison des bits de remplissage dans le type non signé.

smh

Votre opération d'addition fait que l'int en un int non signé.

Faux. Peut-être que oui, peut-être que non.

Conversion de unsigned int en signed int signé dépend de l'implémentation. (Mais cela fonctionne probablement comme vous l'attendez sur la plupart des plateformes de nos jours).

C'est faux. Soit le comportement est indéfini s'il provoque un débordement, soit la valeur est préservée.

Anonyme

La valeur de i est convertie en unsigned int ...

Faux. Cela dépend de la précision d'un int par rapport à un unsigned int.

Taylor Price

Comme il a été répondu précédemment, vous pouvez faire des allers-retours entre les valeurs signées et non signés sans problème.

Faux. Essayer de stocker une valeur en dehors de la plage d'un entier signé entraîne un comportement non défini.

Maintenant, je peux enfin répondre à la question.

Si la précision de int est égale à unsigned int, u sera promu en signed int et vous obtiendrez la valeur -4444 de l'expression (u+i). Maintenant, si u et i ont d'autres valeurs, vous pouvez obtenir un débordement et un comportement non défini, mais avec ces nombres exacts, vous obtiendrez -4444. [1] . Cette valeur sera de type int. Mais vous essayez de stocker cette valeur dans un int non signé, qui sera donc converti en un int non signé et la valeur qui en résultera sera (UINT_MAX+1) - 4444.

Si la précision de l'int non signé est supérieure à celle d'un int, l'int signé sera promu en int non signé, ce qui donnera la valeur (UINT_MAX+1) - 5678 qui sera ajoutée à l'autre int non signé 1234. Si u et i ont d'autres valeurs, qui font que l'expression tombe en dehors de la plage {0..UINT_MAX}, la valeur (UINT_MAX+1) sera ajoutée ou soustraite jusqu'à ce que le résultat tombe dans la plage {0..UINT_MAX) et aucun comportement indéfini ne se produira.

Qu'est-ce que la précision ?

Les nombres entiers ont des bits de remplissage, des bits de signe et des bits de valeur. Les entiers non signés n'ont évidemment pas de bit de signe. Il est également garanti que les caractères non signés n'ont pas de bits de remplissage. Le nombre de bits de valeur d'un nombre entier correspond à son degré de précision.

[Gotchas]

La macro sizeof seule ne peut pas être utilisée pour déterminer la précision d'un entier si des bits de remplissage sont présents. Et la taille d'un octet ne doit pas nécessairement être un octet (huit bits) comme le définit la norme C99.

[1] Le débordement peut se produire à l'un des deux points suivants. Soit avant l'addition (pendant la promotion) - lorsque vous avez un unsigned int qui est trop grand pour tenir dans un int. Le débordement peut également se produire après l'addition, même si le unsigned int était dans la plage d'un int, après l'addition le résultat peut encore déborder.

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