Comment est-conversion de float, double, int manipulé dans un printf?
Estime que ce programme
int main()
{
float f = 11.22;
double d = 44.55;
int i,j;
i = f; //cast float to int
j = d; //cast double to int
printf("i = %d, j = %d, f = %d, d = %d", i,j,f,d);
//This prints the following:
//i = 11, j = 44, f = -536870912, d = 1076261027
return 0;
}
Quelqu'un peut m'expliquer pourquoi le casting de double/float en int fonctionne correctement dans le premier cas, et ne fonctionne pas lorsqu'il est effectué dans printf?
Ce programme a été compilé sur gcc-4.1.2 sur 32 bits machine linux.
EDIT:
Zach répondre semble logique, c'est à dire utiliser des spécificateurs de format pour comprendre ce qu'il faut de pop hors de la pile. Toutefois considérer cette question de suivi:
int main()
{
char c = 'd'; //sizeof c is 1, however sizeof character literal
//'d' is equal to sizeof(int) in ANSI C
printf("lit = %c, lit = %d , c = %c, c = %d", 'd', 'd', c, c);
//this prints: lit = d, lit = 100 , c = d, c = 100
//how does printf here pop off the right number of bytes even when
//the size represented by format specifiers doesn't actually match
//the size of the passed arguments(char(1 byte) & char_literal(4 bytes))
return 0;
}
Comment cela fonctionne?
J'ai eu le même doute. Voir ce fil: stackoverflow.com/questions/2377733/how-does-this-program-work
char est un caractère unique - c'est tout simplement un entier 8 bits. Lorsque vous effectuez une opération sur le type intégral qui sont plus petits que int, elles sont promues en entier. Cela inclut l'appel d'une fonction. Donc en fait il n'est pas hasard la cause de votre printf appel à travailler, ce comportement est défini. En pratique, dans la plupart des C ABIs vous toujours allouer au moins une machine de mot par variable passés sur la pile.
Oui,
Comment mon commentaire a été supprimé?
Très étrange. Ce site a beaucoup de modérateurs et parfois les choses au hasard à disparaître.
char est un caractère unique - c'est tout simplement un entier 8 bits. Lorsque vous effectuez une opération sur le type intégral qui sont plus petits que int, elles sont promues en entier. Cela inclut l'appel d'une fonction. Donc en fait il n'est pas hasard la cause de votre printf appel à travailler, ce comportement est défini. En pratique, dans la plupart des C ABIs vous toujours allouer au moins une machine de mot par variable passés sur la pile.
Oui,
<stdarg>
passage de paramètres prend uniquement en compte les types promu selon l'argument par défaut promotions (C11 §6.5.2.2/6, §7.16.1.1/2), ce qui ne garantit pas que char
et int
sont compatibles. Cependant, ce n'est pas tout à fait les mêmes que les promotions utilisé pour l'arithmétique. Aussi, il est dangereux de raisonner sur la langue en termes de l'ABI. Confirmer que c'est OK, faut vraiment vérifier les règles.Comment mon commentaire a été supprimé?
Très étrange. Ce site a beaucoup de modérateurs et parfois les choses au hasard à disparaître.
OriginalL'auteur Sandip | 2010-03-08
Vous devez vous connecter pour publier un commentaire.
La
printf
fonction utilise les spécificateurs de format pour comprendre ce qu'il faut de pop hors de la pile. Donc, quand il voit%d
, il apparaît de 4 octets, et les interprète comme unint
, ce qui est faux (la représentation binaire de(float)3.0
n'est pas le même que(int)3
).Vous aurez besoin d'utiliser soit le
%f
les spécificateurs de format ou de fonte des arguments de laint
. Si vous utilisez une nouvelle version assez degcc
, puis, se tournant sur le renforcement des mises en garde des captures de ce genre d'erreur:Réponse à la modification de la partie de la question:
C est entier règles de promotion de dire que tous les types plus petits que
int
obtenir une promotion àint
lorsqu'elle est transmise comme un vararg. Donc dans votre cas, le'd'
est la promotion d'unint
, puis printf est l'éclate d'unint
et un casting pour unchar
. La meilleure référence que j'ai pu trouver pour ce comportement a été cette entrée de blog.J'ai une question de suivi, veuillez consulter le édités partie de ma question. S'il vous plaît commentaire si la question n'est pas claire...merci
Il est vrai que
char
arguments à obtenir une promotion àint
lorsqu'il est passé dans la partie variable de la liste d'arguments (c'est ce qui se passe quandc
est passé dans l'exemple), mais le caractère constantes littérales (comme'd'
) sont déjà de typeint
. (Note également que, en théorie, en vertu de certains compilateurs,char
arguments pourraient être promus àunsigned int
à la place).la bonne réponse. Ce comportement n'est pas particulier à printf, mais à toutes les variadic fonctions. Comment il fonctionne, c'est bien documentée va_arg page de manuel, y compris la promotion de caractères de type int. Pour un exemple d'utilisation vous pouvez voir ma réponse dans stackoverflow.com/questions/1688942
OriginalL'auteur
Il n'y a pas une telle chose comme "casting pour
int
dansprintf
".printf
ne pas le faire et ne peut pas faire de casting. Incompatible spécificateur de format conduit à un comportement indéfini.Dans la pratique
printf
reçoit simplement les données brutes et réinterprète comme le type implicite par le spécificateur de format. Si vous passer undouble
de la valeur et de spécifier unint
spécificateur de format (comme%d
),printf
prendra quedouble
valeur et aveuglément réinterpréter unint
. Les résultats seront complètement imprévisible (c'est pourquoi faire ce officiellement provoque un comportement indéfini dans C).A. Schneider: Vraiment? Ce que les situations où la chaîne de format est une valeur d'exécution? Comment voulez-vous que le compilateur de "cast" de quoi que ce soit même si "la conversion des prescripteurs font partie de la norme"? Plus généralement, si, c'est un principe fondamental du langage C design: sa bibliothèque standard ne repose pas sur "magique" fonctionnalités du compilateur (à quelques exceptions près, peut-être). Presque tous de la fonction de bibliothèque peuvent être mis en œuvre par l'utilisateur en langage C lui-même. Tant que langage C suit ce principe fondamental,
printf
ne sera pas "augmentée" avec un compilateur de la magie.Certes je n'ai pas pensé au moment de l'exécution des chaînes de format (parce que dans la réalité: Quand est la dernière fois que vous avez utilisé?). Mais le standard pouvait distinguer le cas, regardez ce qu'ils ont fait à
sizeof
. Comme pour le casting: Il est assez évident que le compilateur sait comment fonte types en général, donc je ne peut pas détecter toutes magiques supplémentaires ici; et je pense aussi que le casting pourrait probablement être mis en œuvre en C de toute façon. Comme un grand si/d'autre si l'interrupteur sur la conversion des opérateurs, et puis le jette. La modification de rupture serait plutôt l'évolution de la sémantique des points de suspension prototype.Comme une suggestion pour une mise en œuvre je crois que dans le cas où les types de paramètres après le dernier paramètre déclaré sont connus/peut être déduit au moment de la compilation (comme dans la statique fomat chaîne printf), le compilateur crée une fonction sans nom protoype avec le nombre de paramètres avec les types de donnée et traite les arguments comme avec toute autre fonction; par exemple, il refuse de compiler ou d'un lien lorsque les arguments sont incompatibles avec l'déduit le type de paramètre. Ce serait probablement similaire à certains cas de l'inférence de type en C++ moderne, c'est à dire les mécanismes existent.
OriginalL'auteur
Jack répondre explique comment résoudre votre problème. Je vais expliquer pourquoi vous obtenez vos résultats inattendus. Votre code est équivalent à:
La raison en est que
f
etd
sont passés àprintf
comme des valeurs, et ces valeurs sont interprétées comme desint
s. Cela ne change pas la valeur binaire, de sorte que le nombre affiché est la représentation binaire d'unfloat
ou undouble
. La réelle projetée à partir defloat
àint
est beaucoup plus complexe dans l'assembly généré.Mon code n'a (pour moi, et probablement pour lui) ce que son code est fait (pour lui). Les deux sont un comportement indéfini, de sorte que ni l'un est la garantie de faire quelque chose en particulier.
OriginalL'auteur
Parce que vous n'êtes pas à l'aide de la flotte spécificateur de format, essayez avec:
Sinon, si vous voulez 4 ints vous avez à jeter avant de passer à l'argument de printf:
OriginalL'auteur
La raison de votre suivi de code fonctionne est parce que la constante de caractère est promu en int avant, il est poussé sur la pile. Donc printf pop off 4 octets de %c et %d. En effet, les constantes de caractères sont de type int, pas de type char. C est étrange cette façon.
OriginalL'auteur
printf utilise la variable longueur des listes d'arguments, ce qui signifie que vous avez besoin de fournir ce type d'informations. Vous êtes en fournissant des informations erronées, de sorte qu'il devient confus. Jack fournit la solution la plus pratique.
OriginalL'auteur
Il est intéressant de noter que
printf
, étant une fonction avec une longueur variable de la liste d'arguments, ne reçoit jamais de flotteur; float arguments sont "old school" promu doubles.Un récent projet de norme introduit la "vieille école" par défaut promotions première (n1570, 6.5.2.2/6):
Puis il aborde les listes d'arguments variable (6.5.2.2/7):
La conséquence pour
printf
est qu'il est impossible "d'imprimer" un véritable flotteur. Un flotteur expression est toujours promu le double, qui est de 8 octets de la valeur pour les implémentations de la norme IEEE 754. Cette promotion se produit dans la partie appelante; printf aura déjà de 8 octets argument sur la pile lors de son exécution commence.Si nous cédons
11.22
d'un lit double et d'examiner son contenu, avec mon x86_64-pc-cygwin gcc-je voir la séquence d'octets 000000e0a3702640.Qui explique la valeur int imprimé par
printf
: Ints sur cette cible ont toujours 4 octets, de sorte que seuls les quatre premiers octets 000000e0 sont évalués, et de nouveau en little endian, c'est à dire que 0xe0000000. C'est -536870912 en décimal.Si on inverse tous les 8 octets, parce que le processeur Intel magasins doubles en little endian, trop, nous obtenons 402670a3e0000000. On peut vérifier la valeur de cette séquence d'octets représente dans le format IEEE sur ce site web, c'est près de 1.122E1, c'est à dire 11.22, le résultat attendu.
OriginalL'auteur