Réponse courte
Votre i
sera converti en un entier non signé par addition UINT_MAX + 1
, puis l'ajout sera effectué avec les valeurs non signées, ce qui donnera un grand result
(en fonction des valeurs de u
et i
).
Longue réponse
Selon la norme C99:
6.3.1.8 Conversions arithmétiques usuelles
- Si les deux opérandes ont le même type, aucune conversion supplémentaire n'est nécessaire.
- Sinon, si les deux opérandes ont des types entiers signés ou les deux ont des types entiers non signés, l'opérande avec le type de rang de conversion d'entier inférieur est converti en type de l'opérande de rang supérieur.
- Sinon, si l'opérande de type entier non signé a un rang supérieur ou égal au rang du type de l'autre opérande, l'opérande de type entier signé est converti en type de l'opérande de type entier non signé.
- Sinon, si le type de l'opérande de type entier signé peut représenter toutes les valeurs du type de l'opérande de type entier non signé, alors l'opérande de type entier non signé est converti en type de l'opérande de type entier signé.
- Sinon, les deux opérandes sont convertis au type entier non signé correspondant au type de l'opérande avec le type entier signé.
Dans votre cas, nous avons un int ( u
) non signé et un int ( ) signé i
. En se référant à (3) ci-dessus, puisque les deux opérandes ont le même rang, vous i
devrez être converti en un entier non signé.
6.3.1.3 Entiers signés et non signés
- Lorsqu'une valeur de type entier est convertie en un autre type entier autre que _Bool, si la valeur peut être représentée par le nouveau type, elle reste inchangée.
- Sinon, si le nouveau type n'est pas signé, la valeur est convertie en ajoutant ou en soustrayant à plusieurs reprises une valeur de plus que la valeur maximale pouvant être représentée dans le nouveau type jusqu'à ce que la valeur se trouve dans la plage du nouveau type.
- Sinon, le nouveau type est signé et la valeur ne peut pas y être représentée; soit le résultat est défini par l'implémentation, soit un signal défini par l'implémentation est émis.
Nous devons maintenant nous référer à (2) ci-dessus. Votre i
sera converti en une valeur non signée en ajoutant UINT_MAX + 1
. Le résultat dépendra donc de la manière dont UINT_MAX
est défini votre implémentation. Il sera volumineux, mais il ne débordera pas, car:
6,2,5 (9)
Un calcul impliquant des opérandes non signés ne peut jamais déborder, car un résultat qui ne peut pas être représenté par le type entier non signé résultant est réduit modulo le nombre qui est supérieur de un à la plus grande valeur pouvant être représentée par le type résultant.
Bonus: Conversion arithmétique Semi-WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
Vous pouvez utiliser ce lien pour essayer ceci en ligne: https://repl.it/repls/QuickWhimsicalBytes
Bonus: effet secondaire de conversion arithmétique
Les règles de conversion arithmétique peuvent être utilisées pour obtenir la valeur de UINT_MAX
en initialisant une valeur non signée à -1
, c'est- à -dire:
unsigned int umax = -1; // umax set to UINT_MAX
Ceci est garanti pour être portable quelle que soit la représentation numérique signée du système en raison des règles de conversion décrites ci-dessus. Voir cette question SO pour plus d'informations: est-il sûr d'utiliser -1 pour définir tous les bits sur true?
La conversion de signé en non signé ne se contente pas nécessairement de copier ou de réinterpréter la représentation de la valeur signée. Citant le standard C (C99 6.3.1.3):
Pour la représentation du complément à deux qui est presque universelle de nos jours, les règles correspondent à la réinterprétation des bits. Mais pour les autres représentations (signe-et-magnitude ou complément à uns), l'implémentation C doit toujours organiser le même résultat, ce qui signifie que la conversion ne peut pas simplement copier les bits. Par exemple, (non signé) -1 == UINT_MAX, quelle que soit la représentation.
En général, les conversions en C sont définies pour opérer sur des valeurs, pas sur des représentations.
Pour répondre à la question initiale:
La valeur de i est convertie en un entier non signé, ce qui donne
UINT_MAX + 1 - 5678
. Cette valeur est ensuite ajoutée à la valeur non signée 1234, ce qui donneUINT_MAX + 1 - 4444
.(Contrairement aux débordements non signés, les débordements signés invoquent un comportement non défini. Le bouclage est courant, mais n'est pas garanti par le standard C - et les optimisations du compilateur peuvent faire des ravages sur le code qui fait des suppositions injustifiées.)
la source
En référence à la Bible :
la source
Quand une variable non signée et une variable signée sont ajoutées (ou toute opération binaire), les deux sont implicitement converties en non signées, ce qui dans ce cas entraînerait un résultat énorme.
Il est donc sûr que le résultat peut être énorme et faux, mais il ne plantera jamais.
la source
Lors de la conversion de signé à non signé, il existe deux possibilités. Les nombres initialement positifs conservent (ou sont interprétés comme) la même valeur. Les nombres initialement négatifs seront désormais interprétés comme des nombres positifs plus grands.
la source
Comme il a été répondu précédemment, vous pouvez passer sans problème entre signé et non signé. La casse de la bordure pour les entiers signés est -1 (0xFFFFFFFF). Essayez d'ajouter et de soustraire de cela et vous constaterez que vous pouvez renvoyer et que ce soit correct.
Cependant, si vous prévoyez de lancer un casting d'avant en arrière, je vous conseillerais fortement de nommer vos variables de manière à ce que leur type soit clair, par exemple:
Il est beaucoup trop facile de se laisser distraire par des problèmes plus importants et d'oublier quelle variable est de quel type s'ils sont nommés sans indice. Vous ne voulez pas convertir en un non signé, puis l'utiliser comme index de tableau.
la source
i sera converti en un entier non signé.
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 essentiellement quelle que soit la représentation utilisée dans l'entier signé, l'entier non signé contiendra une représentation complémentaire du nombre à 2.
L'addition, la soustraction et la multiplication fonctionneront correctement sur ces nombres résultant en un autre entier non signé contenant un nombre de complément à deux représentant le "résultat réel".
la division et la conversion en types entiers non signés plus grands auront des résultats bien définis mais ces résultats ne seront pas des représentations complémentaires de 2 du "résultat réel".
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 "résultat réel" lors de la conversion d'un nombre de complément à 2 stocké dans un entier non signé en un entier signé . J'espère que vous ne trouverez d'autres comportements que sur des systèmes obscurs qui n'utilisent pas le complément de 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
la source
Réponses horribles à gogo
Ozgur Ozcitak
C'est complètement faux.
Mats Fredriksson
Ceci est également faux. Les entiers non signés peuvent être promus en entiers s'ils ont une précision égale en raison de bits de remplissage dans le type non signé.
smh
Faux. Peut-être que oui et peut-être pas.
Faux. Il s'agit d'un comportement non défini s'il provoque un débordement ou la valeur est conservée.
Anonyme
Faux. Dépend de la précision d'un int par rapport à un int non signé.
Prix Taylor
Faux. Essayer de stocker une valeur en dehors de la plage d'un entier signé entraîne un comportement indéfini.
Maintenant, je peux enfin répondre à la question.
Si la précision de int est égale à unsigned int, u sera promu en int signé et vous obtiendrez la valeur -4444 à partir de l'expression (u + i). Maintenant, si u et moi avons d'autres valeurs, vous pouvez avoir un comportement de débordement et indéfini, mais avec ces nombres exacts, vous obtiendrez -4444 [1] . Cette valeur aura le type int. Mais vous essayez de stocker cette valeur dans un entier non signé afin qu'il soit ensuite converti en un entier non signé et la valeur que le résultat finira par avoir serait (UINT_MAX + 1) - 4444.
Si la précision de unsigned int est supérieure à celle d'un int, le signé int sera promu en unsigned int donnant la valeur (UINT_MAX + 1) - 5678 qui sera ajoutée à l'autre unsigned int 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 soit ajoutée soit 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 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. Le caractère non signé est en outre garanti pour ne pas avoir de bits de remplissage. Le nombre de bits de valeurs d'un entier correspond à sa précision.
[Gotchas]
La macro sizeof macro 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 défini par C99.
[1] Le débordement peut se produire à l'un des deux points. Soit avant l'ajout (pendant la promotion) - lorsque vous avez un int non signé qui est trop grand pour tenir dans un int. Le débordement peut également se produire après l'ajout, même si l'int non signé était dans la plage d'un int, après l'ajout, le résultat peut encore déborder.
la source
int
est convertiunsigned int
lorsque les conversions arithmétiques habituelles s'appliquent.int
ouunsigned int
en l'un de ces types où quelque chose de typeunsigned int
ouint
est attendu. Le "ou égal" a été ajouté dans TC2 pour permettre aux types énumérés de rang de conversion égalint
ouunsigned int
à être convertis en l'un de ces types. Il n'a jamais été prévu que la promotion décrite se transforme entreunsigned int
etint
. La détermination de type commun entreunsigned int
etint
est toujours régie par 6.3.1.8, même après TC2.