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).

240voto

Ozgur Ozcitak Points 4111

Réponse courte

Votre i sera converti en un nombre entier non signé en ajoutant UINT_MAX + 1 l'addition sera effectuée avec les valeurs non signées, ce qui entraînera un grand nombre d'erreurs. result (en fonction des valeurs de u y i ).

Réponse longue

Selon la norme C99 :

6.3.1.8 Conversions arithmétiques habituelles

  1. Si les deux opérandes ont le même type, aucune autre conversion n'est nécessaire.
  2. Sinon, si les deux opérandes ont des types d'entiers signés ou si les deux ont des types d'entiers non signés, l'opérande ayant le type de rang de conversion d'entier le moins élevé est converti au type de l'opérande de rang le plus élevé.
  3. 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, alors l'opérande de type entier signé est converti au type de l'opérande de type entier non signé.
  4. 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 au type de l'opérande de type entier signé.
  5. Sinon, les deux opérandes sont convertis au type d'entier non signé correspondant au type de l'opérande de type entier signé.

Dans votre cas, nous avons un unsigned int ( u ) et les int signés ( i ). En se référant à (3) ci-dessus, puisque les deux opérandes ont le même rang, votre i devra être converti en un nombre entier non signé.

6.3.1.3 Entiers signés et non signés

  1. 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 est inchangée.
  2. Sinon, si le nouveau type est non signé, la valeur est convertie en ajoutant ou en soustrayant de manière répétée une valeur de plus que la valeur maximale qui peut être représentée dans le nouveau type jusqu'à ce que la valeur soit dans la plage du nouveau type.
  3. Sinon, le nouveau type est signé et la valeur ne peut pas être représentée dans ce type ; soit le résultat est défini par l'implémentation, soit un signal défini par l'implémentation est levé.

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 façon dont UINT_MAX est défini dans votre implémentation. Il sera grand, 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 d'entier non signé résultant est réduit modulo le nombre qui est supérieur d'une unité à la plus grande valeur qui peut ê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 l'essayer en ligne : https://repl.it/repls/QuickWhimsicalBytes

Bonus : Effet secondaire de la conversion arithmétique

Des règles de conversion arithmétiques 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

La portabilité est garantie, quelle que soit la représentation numérique signée du système, grâce aux règles de conversion décrites ci-dessus. Voir cette question SO pour plus d'informations : Est-il sûr d'utiliser -1 pour mettre tous les bits à vrai ?

0 votes

Je ne comprends pas pourquoi il ne peut pas simplement faire une valeur absolue et ensuite la traiter comme non signée, comme pour les nombres positifs ?

7 votes

@D.Singh pouvez-vous indiquer les parties incorrectes de la réponse ?

0 votes

Pour convertir une valeur signée en valeur non signée, nous ajoutons la valeur maximale de la valeur non signée (UINT_MAX +1). De même, quel est le moyen le plus simple de convertir une valeur non signée en valeur signée ? Devons-nous soustraire le nombre donné de la valeur maximale (256 dans le cas d'un caractère non signé) ? Par exemple : 140, converti en nombre signé, devient -116. Mais 20 devient 20 lui-même. Existe-t-il une astuce simple ?

24voto

La conversion de signé à non signé fait no nécessairement juste copier ou réinterpréter la représentation de la valeur signée. Citant la norme C (C99 6.3.1.3) :

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 est inchangée.

Sinon, si le nouveau type est non signé, la valeur est convertie en ajoutant de façon répétée ou en ajoutant ou en soustrayant une valeur de plus que la valeur maximale qui peut être représentée dans le nouveau type jusqu'à ce que la valeur soit dans la plage du nouveau type.

Sinon, le nouveau type est signé et la valeur ne peut pas être représentée dans ce type. résultat est défini par l'implémentation ou un signal défini par l'implémentation est émis.

Pour la représentation en 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 à un), l'implémentation C doit toujours s'arranger pour obtenir le même résultat, ce qui signifie que la conversion ne peut pas simplement copier les bits. Par exemple, (unsigned)-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, et non sur des représentations.

Pour répondre à la question initiale :

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

unsigned int result = u + i;

La valeur de i est convertie en unsigned int, ce qui donne UINT_MAX + 1 - 5678 . Cette valeur est ensuite ajoutée à la valeur non signée 1234, ce qui donne UINT_MAX + 1 - 4444 .

(Contrairement au dépassement de capacité non signé, le dépassement de capacité signé entraîne un comportement non défini. Le wraparound est courant, mais n'est pas garanti par la norme C -- et les optimisations du compilateur peuvent faire des ravages sur le code qui fait des suppositions injustifiées).

6voto

smh Points 635

Se référant à Le langage de programmation C, deuxième édition (ISBN 0131103628),

  • Votre opération d'addition entraîne la conversion de l'int en un int non signé.
  • En supposant une représentation en complément à deux et des types de taille égale, la configuration des bits ne change pas.
  • La conversion de unsigned int en signed int dépend de l'implémentation. (Mais cela fonctionne probablement comme vous l'attendez sur la plupart des plateformes de nos jours).
  • Les règles sont un peu plus compliquées dans le cas de la combinaison de signés et de non signés de tailles différentes.

4voto

Mats Fredriksson Points 7136

Lorsqu'on additionne une variable non signée et une variable signée (ou toute autre opération binaire), les deux sont implicitement converties en non signées, ce qui, dans ce cas, donnerait un résultat énorme.

Il est donc sûr dans le sens où le résultat peut être énorme et erroné, mais il ne se plantera jamais.

0 votes

C'est faux. 6.3.1.8 Conversions arithmétiques habituelles Si vous additionnez un int et un unsigned char, ce dernier est converti en int. Si vous additionnez deux unsigned chars, ils sont convertis en int.

3voto

Tim Ring Points 970

Lorsque l'on convertit des chiffres signés en chiffres non signés, il existe deux possibilités. Les nombres qui étaient positifs à l'origine conservent (ou sont interprétés comme) la même valeur. Les nombres qui étaient négatifs à l'origine sont maintenant interprétés comme des nombres positifs plus grands.

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