2014-07-18 3 views
-3

Le code ci-dessous sort des nombres différents chaque fois ..
apples.num imprime 2 ce qui est correct, et pommes.weight imprime des nombres différents à chaque fois, il a même imprimé "nan", et je ne '' Je sais pourquoi cela se produit ..
La chose vraiment étrange est que le double (pommes.volume) imprime 2.0 ..

Quelqu'un peut-il m'expliquer des choses?Comportement inattendu de l'Union

#include <stdio.h> 

typedef union { 
    short num; 
    float weight; 
    double volume; 
} Count; 

int main(int argc, char const *argv[]) 
{ 
    Count apples; 
    apples.num = 2; 

    printf("Num: %d\nWeight: %f\nVolume: %f\n", apples.num, apples.weight, apples.volume); 
    return 0; 
} 
+6

Ne vous attendez pas à ce que le comportement indéfini soit défini. – chris

+0

Vous définissez seulement la partie 'short' de cette union à une certaine valeur. Le reste contiendra des données aléatoires à chaque réexécution. – usr2564301

+0

Alors qu'est-ce qu'un garçon est supposé faire? Il suffit de prendre le poids de votre pomme hors de l'air –

Répondre

3

Il me semble que vous ne comprenez pas vraiment ce qu'est une union. Les membres d'une union sont des valeurs qui se chevauchent (en d'autres termes, les trois membres d'un Count union partagent le même espace).

En supposant, pour le plaisir de démonstration, une short est de 16 bits (2 octets), un float est de 32 bits (4 octets) et un double est de 64 bits (8 octets), alors l'union est de 8 octets dans Taille. Dans le format little-endian, le membre num fait référence aux 2 premiers octets, le membre weight fait référence aux 4 premiers octets (y compris les 2 octets de num) et le membre de volume fait référence aux 8 octets complets (y compris les 2 octets de num et les quatre octets de weight).

Dans un premier temps, votre syndicat contient des déchets, à savoir une configuration binaire inconnu, Affichons comme ça (en hexadécimal):

GG GG GG GG GG GG GG GG // GG stands for garbage, i.e. unknown bit patterns 

Si vous définissez num-2, les deux premiers octets sont 0x020x00, mais les autres octets sont encore des déchets:

02 00 GG GG GG GG GG GG 

Si vous lisez weight, vous lisez simplement les quatre premiers octets, interprétés comme float, de sorte que le float contient les octets

02 00 GG GG 

Puisque les valeurs à virgule flottante ont un format totalement différent que les types entiers comme short, vous ne pouvez pas prédire ce que ces octets (à savoir ce modèle de bits particulier) représentent. Ils ne représentent pas la valeur en virgule flottante 2.0f, ce que vous voulez probablement. En fait, la « plus importante » partie d'un float est stocké dans les octets supérieurs, à savoir dans la partie « déchets » de weight, il peut donc être presque tout, y compris un NaN, +infinity, -infinity, etc.

De même , si vous lisez volume, vous avez un double qui se compose des octets

02 00 GG GG GG GG GG GG 

et qui ne représente pas nécessairement 2.0 soit (bien que, par hasard, il PEUT venir très près, si par coïncidence les bons bits sont placés aux bons endroits, et si les bits faibles sont arrondis lorsque vous affichez une telle valeur).

Les unions ne sont pas destinées à effectuer une conversion appropriée de int à float ou double. Ils sont simplement destinés à être en mesure de stocker différents types de valeurs pour le même type, et lire un autre membre que vous définissez simplement signifie que vous êtes réinterpréter un nombre de bits qui sont présents dans l'union comme quelque chose de complètement différent. Vous n'êtes pas en train de convertir.

Alors comment convertir? Il est assez simple et ne nécessite pas une union:

short num = 2; 
float weight = num; // the compiler inserts code that performs a conversion to float 
double volume = num; // the compiler inserts code that performs a conversion to double 
+1

Il peut être intéressant de noter que les bits qui ont été définis sont les 16 bits les moins significatifs du significand. Les bits qui contrôlent si vous avez un NaN, infini, grand fini, minuscule fini, positif, négatif etc. restent des ordures. –

+1

Je pourrais le faire, mais je ne suis pas sûr si mentionner les termes «exponent», «significand», etc. ne confondrait pas le lecteur plus que cela l'aide. Il doit juste savoir que les valeurs à virgule flottante ont un format différent. –

+0

J'ai formulé mon commentaire en termes que je pensais que tu comprendrais. Vous faites un excellent travail d'expression de l'information en termes plus simples. Pendant ce temps, quelqu'un a-t-il réellement dit comment convertir correctement une valeur entière en float ou en double valeur? –

1

Vous accédez à des données non initialisées. Il fournira un comportement indéfini (c'est-à-dire: des valeurs inconnues dans ce cas). You also likely mean to use a struct instead of a union.

#include <stdio.h> 

typedef union { 
    short num; 
    float weight; 
    double volume; 
} Count; 

int main(int argc, char const *argv[]) 
{ 
    Count apples = { 0 }; 
    apples.num = 2; 

    printf("Num: %d\nWeight: %f\nVolume: %f\n", apples.num, apples.weight, apples.volume); 
    return 0; 
} 

Initialiser l'Union soit mise à zéro dehors, ou le réglage le plus grand membre à une valeur. Même si vous définissez le membre le plus grand, les autres valeurs peuvent même ne pas avoir de sens. This is commonly used for creating a byte/word/nibble/long-word data type and making the individual bits accessible.

+2

En toute honnêteté, ce n'est toujours pas un * int de faire flotter la conversion *, comme le OP semble le penser. – usr2564301

+0

Il pourrait toujours faire quelque chose comme 'pommes.weight = 1.0f' et vérifier la validité. – DevNull

+0

Cela efface les choses, je pensais que les syndicats utilisaient une conversion implicite .. –

2

Si vous accédez à un syndicat via l'élément « mauvais » (c.-à-un membre autre que celui auquel il a été affecté par), le résultat dépendra de la sémantique du modèle de bits particulier pour ce type. Lorsque le type affecté a une largeur de bit plus petite à laquelle il a accédé, certains de ces bits seront indéfinis.

+0

J'ai compris que je devais utiliser une énumération pour cela .. –

+0

@AmrAyman: Je ne suis pas sûr de ce que vous voulez dire. Je soupçonne que ce dont vous avez réellement besoin est un casting de type. – Clifford