2016-07-09 1 views
0

Je ne comprends pas la sortie dans le code C suivant:sortie inattendue dans le code C avec union

#include <stdio.h> 
int main() 
{ 
    union U 
    { 
     int i; 
     char s[3]; 
    } u; 
    u.i=0x3132; 
    printf("%s", u.s); 
    return 0; 
} 

mémoire initial est 32 bits et est la valeur binaire de 0x3132 qui est

0000 0000 0000 0000 0011 0001 0011 0010 . Si les trois derniers octets de 0x3132 sont la valeur de s (sans les zéros en tête), alors s[0]=0011,s[1]=0001,s[2]=0011.

Cela donne les valeurs de s=0011 0001 0011=787.

Question: Pourquoi la sortie est 21 et non 787?

+2

'u.s' n'est pas une chaîne et vous ne pouvez pas utiliser'% s' pour l'imprimer. C'est un comportement techniquement indéfini. Cela dit, considérons que «0x31 == 49 == ''' et' 0x32 == 50 ==' 2'' et j'espère que cela devrait être clair pour vous. – kaylum

+0

Les deux derniers groupes de 8 bits sont (en little-endian) "0011 0010" et "0011 0001" qui en hexadécimal sont "32" et "31" qui en ASCII sont "2" et "1" . –

+1

En supposant que sizeof (int) est 4 sans les bits de remplissage et que l'ordre des octets est peu endian, et le codage ascii, alors ce comportement est défini. – 2501

Répondre

2

d'abord voir cet exemple de code:

#include <inttypes.h> 
#include <stdio.h> 
#include <stdint.h> 
int main() 
{ 
    union{ 
     int32_t i32; 
     uint32_t u32; 

     int16_t i16[2]; 
     uint16_t u16[2]; 

     int8_t i8[4]; 
     uint8_t u8[4]; 
    } u; 

    u.u8[3] = 52; 
    u.u8[2] = 51; 
    u.u8[1] = 50; 
    u.u8[0] = 49; 

    printf(" %d %d %d %d \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); // 52 51 50 49 
    printf(" %x %x %x %x \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); // 34 33 32 31 
    printf(" 0x%x \n", u.i32); // 0x34333231 

    return 0; 
} 

l'union est ici juste pour accéder à la mémoire de u de 6 manières différentes.
vous pouvez utiliser u.i32 pour lire ou écrire comme int32_t ou
vous pouvez utiliser u.u32 pour lire ou écrire comme uint32_t ou

vous pouvez utiliser u.i16[0] ou u.i16[1] pour lire ou écrire comme int16_t ou vous pouvez utiliser u.u16[0] ou u.u16[1] pour lire ou écrire comme uint16_t ou

ou comme celui-ci à écrire comme uint8_t:

u.u8[3] = 52; 
u.u8[2] = 51; 
u.u8[1] = 50; 
u.u8[0] = 49; 

et lu comme celui-ci comme int8_t:

printf(" %d %d %d %d \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); 

puis la sortie est:

52 51 50 49 

et lire comme int32_t:

printf(" 0x%x \n", u.i32); 

puis la sortie est:

0x34333231 

comme vous voyez dans cet exemple de code union partage un lieu de mémoire avec plusieurs noms/types.

dans votre exemple de code u.i=0x3132; ce écrit 0x3132 l'intérieur u.i mémoire, et selon boutisme de votre système qui est peu endian ici, vous a demandé printf("%s", u.s); du compilateur, donc nous est un tableau de type char signifiant pointeur constant type char , donc printf("%s", u.s); lit u.s[0] et imprime que sur la sortie stdout puis lit u.s[1] et imprime que sur la sortie stdout et ainsi de suite ..., jusqu'à ce que l'un de ce u.s[i] est zéro.
c'est ce que fait votre code, donc si aucun d'entre nous [0], nous [1], nous [2], nous [3] n'est nul, la mémoire en dehors de votre union sera lue jusqu'à un zéro trouvé ou système erreur de mémoire error se produit.

4

La valeur 0x3132 est représentée dans la mémoire en tant que: 0x32, 0x31, 0x0, 0x0, parce que l'ordre des octets est en petit boutiste.

L'appel printf imprime la chaîne représentée par le membre de l'union s. La chaîne est imprimée octet par octet. D'abord 0x32 puis 0x31 qui sont les valeurs ascii pour les caractères: '2' et '1'. Ensuite, l'impression s'arrête car le troisième élément est le caractère nul: 0x0.

Notez que la représentation de int est définie par l'implémentation et ne peut pas contenir 4 octets et peut avoir un remplissage. Ainsi, le membre de l'union s peut ne pas représenter une chaîne, auquel cas l'appel de printf avec le spécificateur %s provoquera un comportement indéfini.

+0

J'ai accidentellement dû appuyer sur le bouton downvote, s'il vous plaît modifier votre message afin que je puisse changer cela en un upvote =) – Cyclonecode

+0

@Cyclone Fait ... – 2501

+0

Désolé pour ça =) – Cyclonecode

1

Cela signifie que votre machine est petit-boutiste, de sorte que les octets sont stockés dans l'ordre inverse, comme ceci:

32 31 00 00 

Alors: s[0] = 0x32, s[1] = 0x31, s[2] = 0x00.

Même si, en théorie, l'impression d'un tableau de caractères en utilisant "%s" est un comportement indéfini, cela fonctionne, il imprime 0x32 (character '2'), 0x31 (character '1'), puis il arrête un 0x00.

0

si vous écrivez votre code comme ceci:

#include <stdio.h> 

int main(void) 
{ 
    union U 
    { 
     int i; 
     char s[3]; 
    } u; 

    u.i=0x3132; 
    printf("%s", u.s); 
    printf("%8x\n", (unsigned)u.i); 
} 

Ensuite, vous verrez que le contenu de ui est 0x0000000000003132, qui serait effectivement stocké comme: 0x3231000000000000 en raison de Endianness

et 0x00 n'est pas un caractère imprimable, de sorte que la sortie du deuxième appel à printf() est <blank><blank><blank><blank><blank><blank>3132 que vous attendez

et la ascii omble 1 est 0x31 et ascii omble 2 est 0x32 et le premier 0x00 arrête les opérations% s, donc le premier printf() sort 21.