Conversion de Type-non signé à signé int / char

j'ai essayé d'exécuter le programme ci-dessous:

#include <stdio.h>

int main() {
    signed char a = -5;
    unsigned char b = -5;
    int c = -5;
    unsigned int d = -5;

    if (a == b)
        printf("rn char is SAME!!!");
    else
        printf("rn char is DIFF!!!");

    if (c == d)
        printf("rn int is SAME!!!");
    else
        printf("rn int is DIFF!!!");

    return 0;
}

pour ce programme, je reçois le résultat:

char is DIFF!!! int is SAME!!!

Pourquoi avons-nous des sorties différentes pour les deux?

Si la sortie sera comme ci-dessous ?

char est MÊME!!! int is SAME!!!

A codepad lien .

71
demandé sur legends2k 2013-06-26 09:58:07

5 réponses

c'est en raison des diverses règles de conversion de type implicite dans C. Il y a deux d'entre eux qu'un programmeur C doit savoir: les conversions arithmétiques habituelles et le promotions entières (les dernières font partie du premier).

dans le cas de char vous avez les types (signed char) == (unsigned char) . Ce sont tous les deux petits types entiers . D'autres tels petits types entiers sont bool et short . Le integer promotion rules stipule que chaque fois qu'un petit type entier est un opérande d'une opération, son type sera promu à int , qui est signé. Ce phénomène, peu importe si le type a été signé ou non signé.

dans le cas du signed char , le signe sera conservé et promu à un int contenant la valeur -5. Dans le cas du unsigned char , il contient une valeur qui est 251 (0xFB ). Il sera promu à un int contenant la même valeur. On finit avec

if( (int)-5 == (int)251 )

dans le cas entier vous avez les types (signed int) == (unsigned int) . Ils ne sont pas de petits types entiers, de sorte que les promotions integer ne s'appliquent pas. Au lieu de cela, ils sont équilibrés par les conversions arithmétiques habituelles , qui déclarent que si deux opérandes ont le même "rang" (taille) mais la signification différente, le signe signé l'opérande est converti au même type que le non signé. On finit avec

if( (unsigned int)-5 == (unsigned int)-5)
79
répondu Lundin 2014-08-07 07:52:12

question Cool!

la comparaison int fonctionne, parce que les deux ints contiennent exactement les mêmes bits, de sorte qu'ils sont essentiellement les mêmes. Mais qu'en est-il des char ?

Ah, C favorise implicitement char s int s à diverses occasions. C'est l'un d'entre eux. Votre code dit if(a==b) , mais ce que le compilateur transforme réellement en est:

if((int)a==(int)b) 

(int)a est de 5, mais (int)b est 251. Ce ne sont pas les mêmes.

édite: comme @Carbonic-Acid l'a souligné, (int)b est 251 seulement si un char est 8 bits de long. Si int est 32 bits de long, (int)b est -32764.

REDIT: il y a toute une série de commentaires qui discutent de la nature de la réponse si un octet ne fait pas 8 bits. La seule différence dans ce cas est que (int)b n'est pas 251 mais un autre positif numéro, qui n'est pas -5. Ce n'est pas vraiment pertinent pour la question qui est toujours très cool.

36
répondu zmbq 2013-06-30 21:05:53

Bienvenue integer promotion . Si je peux citer du site web:

si un int peut représenter toutes les valeurs du type original, la valeur est converti en int; sinon, il est converti en unsigned int. Ceux-ci sont appelés les promotions integer. Tous les autres types sont inchangés par les promotions integer.

C peut être vraiment déroutant quand vous faites des comparaisons comme celles-ci, je récemment intrigué certains de mes amis de programmation non-C avec le tease suivant:

#include <stdio.h>
#include <string.h>

int main()
{
    char* string = "One looooooooooong string";

    printf("%d\n", strlen(string));

    if (strlen(string) < -1) printf("This cannot be happening :(");

    return 0;
}

qui en effet imprime This cannot be happening :( et semble démontrer que 25 est plus petit que -1!

ce qui se passe en dessous cependant est que -1 est représenté comme un entier non signé qui en raison de la représentation des bits sous-jacents est égal à 4294967295 sur un système de 32 bits. Et naturellement 25 est plus petit que 4294967295.

si nous toutefois explicitement moulé le type size_t retourné par strlen comme un entier signé:

if ((int)(strlen(string)) < -1)

alors il comparera 25 contre -1 et tout ira bien avec le monde.

un bon compilateur devrait vous avertir de la comparaison entre un entier non signé et signé et pourtant il est si facile de le manquer (surtout si vous n'activez pas les Avertissements).

c'est particulièrement déroutant pour les programmeurs Java comme tous les types primitifs sont signés. Voici ce que James Gosling (un des créateurs de Java) avait à dire sur le sujet :

Gosling: pour moi en tant que concepteur de langue, ce que je ne compte pas vraiment moi - même comme ces jours-ci, ce qui "simple" a vraiment fini par le sens était pourrait J'attends de J. Random Developer qu'il garde la spécification dans sa tête. Que la définition dit que, par exemple, Java n'est pas -- et en fait beaucoup de ces langues se retrouvent avec un beaucoup de cas de coin, les choses que personne comprend vraiment. Interrogez n'importe quel développeur C sur non signé, et jolie bientôt, vous découvrez que les développeurs presque aucun C comprennent réellement ce que continue avec non signé, ce qu'est l'arithmétique non signé. Des choses comme ça fait C complexe. Le langage Java est, je pense, assez simple. Les bibliothèques que vous devez consulter.

20
répondu Nobilis 2013-07-03 08:59:30

la représentation hexadécimale de -5 est:

  • 8 bits en complément à deux signed char : 0xfb
  • 32 bits, complément de two signed int : 0xfffffffb

lorsque vous convertissez un numéro signé en un numéro non signé, ou vice versa, le compilateur le fait ... rien de précis. Qu'est ce qu'il y a à faire? Le nombre est soit convertibles ou il ne l'est pas, auquel cas indéfini ou le comportement défini par la mise en œuvre suit (je n'ai pas vraiment vérifié lequel) et le comportement défini par la mise en œuvre le plus efficace est de ne rien faire.

ainsi, la représentation hexadécimale de (unsigned <type>)-5 est:

  • 8-bits, unsigned char : 0xfb
  • 32 bits, unsigned int : 0xfffffffb

ça vous dit quelque chose? Ils sont bit-for-bit les mêmes que les versions signées.

lorsque vous écrivez if (a == b) , où a et b sont de type char , ce que le compilateur est réellement tenu de lire est if ((int)a == (int)b) . (C'est cette "promotion d'entier" dont tout le monde parle.)

que se passe-t-il lorsque nous convertissons char en int ?

  • 8-bit signed char à 32-bit signed int : 0xfb -> 0xfffffffb
    • Ça a du sens parce que ça correspond aux représentations de -5 ci-dessus!
    • on l'appelle un" sign-extend", parce qu'il copie le morceau supérieur du byte, le" sign-bit", vers la gauche dans la nouvelle, plus large valeur.
  • 8-bit unsigned char à 32-bit signed int : 0xfb -> 0x000000fb
    • cette fois, il fait un "zéro-étendre" parce que le type de source est non signé il n'est donc pas signe bits à copier.

, a == b vraiment 0xfffffffb == 0x000000fb => pas de match!

Et, c == d vraiment 0xfffffffb == 0xfffffffb => match!

9
répondu ams 2013-07-04 12:48:38

mon point est: n'avez-vous pas reçu un avertissement au moment de la compilation "comparant l'expression signée et non signée"?

Le compilateur essaie de vous informer qu'il est en droit de faire des trucs dingues! :) J'ajouterais, des choses folles se produiront en utilisant de grandes valeurs, proches de la capacité du type primitif. Et

 unsigned int d = -5;

attribue définitivement une grande valeur à d, c'est équivalent (même si, probablement pas garanti d'être équivalent) à:

 unsigned int d = UINT_MAX -4; ///Since -1 is UINT_MAX

Edit:

cependant, il est intéressant de noter que seule la seconde comparaison donne un avertissement (cochez le code) . Cela signifie donc que le compilateur appliquant les règles de conversion est confiant qu'il n'y aura pas d'erreurs dans la comparaison entre unsigned char et char (pendant la comparaison ils seront convertis en un type qui peut représenter en toute sécurité toutes ses valeurs possibles). Et il a raison sur ce point. Ensuite, il vous informe que ce ne sera pas le cas pour unsigned int et int : lors de la comparaison un des 2 sera converti en un type qui ne peut pas le représenter complètement.

pour l'exhaustivité, j'ai vérifié aussi pour le court : le compilateur se comporte de la même manière que pour les caractères, et, comme prévu, il n'y a pas d'erreurs à l'exécution.

.

en rapport avec ce sujet, j'ai récemment demandé cette question (encore, C++ orienté).

1
répondu Antonio 2017-05-23 12:31:55