Comment fonctionne la fonction printf en C?

J'ai rencontré un problème lorsque j'ai testé la fonction printf:

D'abord j'écris le code comme ceci:

int main(void)
{
    char a = 'a';
    printf("a = %f\n", a);
    return 0;
}

La sortie est

enter image description here

Et puis j'écris le code:

int main(void)
{
    float b = 'a';
    printf("b = %f\n", b);
    return 0;
}

La sortie est

enter image description here

Et puis j'écris le code:

int main(void)
{
    char a = 'a';
    float b = 'a';
    printf("b = %f\n", b);
    printf("a = %f\n", a);
    return 0;
}

La sortie est

enter image description here

Je ne comprends donc pas pourquoi dans le premier programme a = 0.000000 et dans le troisième programme a = 97.000000 ? < br> Comment fonctionne la fonction printf() ?
Comment le symbole % f , % d fonctionne-t-il?

5
RTFM, par exemple lisez printf (3) et/ou printf
ajouté l'auteur Basile Starynkevitch, source
Vous êtes confus à propos de printf mais float b = 'a'; vous convient? ;)
ajouté l'auteur sebastian, source
@sebastian désolé, je ne sais pas ce que vous voulez dire.
ajouté l'auteur Mr.CodeMonkey, source

6 Réponses

Update: after doing some more research on this, it seems that the differences between the float and int memory representations are not the ones responsible for the behaviour of the three programs.

J'ai examiné le code objet du troisième programme et j'ai trouvé la cause du comportement étrange: les arguments en virgule flottante sont envoyés à un autre registre/pile que ceux contenant un nombre entier. Et printf s'appuie sur cela et les recherche dans des emplacements autres que ceux appelés par printf (c'est-à-dire que la méthode main ) place les arguments.

Voici le désassemblage pertinent du troisième programme (pour une architecture x86_64):

0000000100000f18    leaq    0x71(%rip), %rdi        ## literal pool for: "b = %f\n"
0000000100000f1f    movsd   0x61(%rip), %xmm0      ## b variable gets sent to xmm0
0000000100000f27    movl    $0x0, -0x4(%rbp)
0000000100000f2e    movb    $0x61, -0x5(%rbp)      ## a variable gets placed on the callee stack
0000000100000f32    movsd   %xmm0, -0x10(%rbp)
0000000100000f37    movsd   -0x10(%rbp), %xmm0
0000000100000f3c    movb    $0x1, %al
0000000100000f3e    callq   0x100000f66             ## symbol stub for: _printf
0000000100000f43    leaq    0x4e(%rip), %rdi        ## literal pool for: "a = %f\n"
0000000100000f4a    movsbl  -0x5(%rbp), %esi
0000000100000f4e    movl    %eax, -0x14(%rbp)
0000000100000f51    movb    $0x0, %al
0000000100000f53    callq   0x100000f66             ## symbol stub for: _printf

Et printf s'appuie sur cela, il suppose que l'appelé a placé des arguments % f dans les xmm0 / xmm1 /etc registres, et le comportement des trois programmes est le suivant:

  1. dans le premier programme printf recherche l'argument % f dans le registre xmm0, mais comme nous sommes au début du programme, le registre est propre et main a placé a dans eax , ainsi xmm0 contient une valeur nulle et voici ce que printf imprime
  2. dans le deuxième programme main place correctement b dans xmm0 , et printf le prend à partir de là, en imprimant la valeur correcte
  3. dans le troisième programme en raison du fait que b est imprimé en premier, le registre xmm0 conservera cette valeur, et puisque printf ne Ne bloquez pas le registre, lorsqu’il est appelé pour la deuxième fois, il extrait à nouveau de xmm0 qui est resté intact après le premier appel printf .

So it's all about caller/callee conventions on where integers and floats are being send by the caller and from where the callee tries to pick them up.


Original response: In the first program you are trying to print a float, but you pass an int (char is a smaller int). Due to the fact that ints and floats have different binary representations, the int 97 (corresponding to the character 'a') corresponds to a very small float: 1.36E-43, that gets printed as zero.

Here is the binary representation of 97 (the compiler expands any 1-byte char to a 4-byte argument when calling a function)
00000000 00000000 00000000 01100001

IEEE 754 is the standard format for binary representations of float/double numbers, you can play with an online converter here, and you can see how the same binary number has different values when its interpreted as an int or as a float.

6
ajouté
Eh bien, je pense que c'est l'un de ces comportements non définis que vous obtenez lorsque vous ne correspondez pas à la chaîne de formatage et à la liste des arguments passés à printf. Soyez heureux que vous n'ayez pas eu d'accident :)
ajouté l'auteur Cristik, source
Merci pour votre réponse. Mais comment peut-il expliquer le troisième programme?
ajouté l'auteur Mr.CodeMonkey, source
Je pense que c'est le tampon de sortie qui mène à la sortie dans le troisième programme. Parce que lorsque j'échange les deux phrases printf, la sortie est sortie a = 0.000000 b = 97.000000.
ajouté l'auteur Mr.CodeMonkey, source

Le % f représente ici un jeton de remplacement pour un float.

Pour remplacer un caractère, vous avez besoin de % c .

Here is a list that tells you what is the appropriate replacement token for each type.

5
ajouté
@ Mr.CodeMonkey, ce que vous faites est un comportement indéfini, donc n'importe quoi est un résultat valable, allant jusqu'à la destruction totale de l'univers. En d'autres termes, ne le faites pas :-)
ajouté l'auteur paxdiablo, source
Je sais cela. Mais je veux savoir quand j'utilise% f au lieu de% c, que fera la fonction printf? Comment lit-il le registre?
ajouté l'auteur Mr.CodeMonkey, source

Conformément à la dernière norme «C», il s'agit d'un comportement non défini. Vérifiez les paragraphes 7.21.6.1, point 9 ou c du projet standard.

Si une spécification de conversion n'est pas valide, le comportement est   undefined.282) Si un argument n’est pas du type correct pour le   la spécification de conversion correspondante, le comportement n'est pas défini.

Ainsi, quand on dit qu'un comportement est indéfini, tout est possible et le comportement peut varier d'un compilateur à l'autre. 'C' laisse-toi couper l'orteil avec une hache mais ça ne devrait pas être le cas.

1
ajouté

% f est pour Float  % c pour les personnages

The 97 which you have got is the ASCII value for 'a'
1
ajouté

Différence entre

printf("%f\n, 97.0);

et

printf("%c\n, 'a');

is that the printf function reads its parameters from the stack based on the %X you give, et interprets them (for display) as such.

For %c printf expects a char as parameter, so it will read a char (a byte, but often actually a int, it's implementation dependant) et displays it (it displays the less significant byte if an int is provided).

Pour % f , printf attend un float (dont la taille en octets est sizeof (float) , généralement 4 octets sur les processeurs gcc/Intel).

If you compile with gcc use the -Wall option that would give a warning when the %X format et the type of the parameter do not match.

0
ajouté
Lisez l'excellente réponse de Cristik (ci-dessus, je suppose) qui explique pourquoi vous obtenez 0 dans ce cas.
ajouté l'auteur Ring Ø, source
Je comprends ce que tu as dit. Mais ça ne peut pas expliquer mon programme. Quand je définis comme char a = 'a'; et printf ("a =% f \ n", a); printf lit-il des données de 4 octets à partir de l'adresse d'un? Si c'est le cas, la sortie ne peut pas être 0, n'est-ce pas?
ajouté l'auteur Mr.CodeMonkey, source

%f is for float. You must use %c for characters.

Si tu utilises

    printf("a = %c\n", a);

Vous aurez le personnage.

Donc, si vous changez votre premier code en

int main(void)
{
    char a = 'a';
    printf("a = %c\n", a);
    return 0;
}

Vous obtiendrez une sortie en tant que

a
0
ajouté
@ Mr.CodeMonkey, vous pouvez vérifier le résultat obtenu ici
ajouté l'auteur Arun A S, source
@ Mr.CodeMonkey, la différence de sortie est probablement due à la façon dont le compilateur l'interprète.
ajouté l'auteur Arun A S, source
Je connais. Mais je veux savoir quelle est la différence entre% f et% c?
ajouté l'auteur Mr.CodeMonkey, source
Oh pourquoi? J'utilise clodeblocks13.12. Mon OS est Windows7 64bit. Savez-vous pourquoi notre production est différente?
ajouté l'auteur Mr.CodeMonkey, source
Je suppose que cela dépend des données stockées dans la mémoire non initialisée.
ajouté l'auteur Mr.CodeMonkey, source