Qu’advient-il d’une variable float lorsque% d est utilisé dans une printf?

J’essaie d’apprendre le C en lisant le langage de programmation C, 2e édition . J’ai de l’expérience en programmation mais pas avec C.

Je suis actuellement au chapitre 1. J’ai le code suivant:


float f; for (f = 0.0; f <= 3; f += 1.1) printf("A: %3f B: %6.2f\n", f, f + 0.15); 

Il imprime la sortie:

 A: 0.000000 B: 0.15 A: 1.100000 B: 1.25 A: 2.200000 B: 2.35 

Semble bien.


Maintenant, je change le printf comme suit:

 printf("A: %3d B: %6.2f\n", f, f + 0.15); 

La nouvelle sortie est

 A: 0 B: 0.00 A: -1610612736 B: 0.00 A: -1610612736 B: -625777476808257557292155887552002761191109083510753486844893290688350183831589633800863219712.00 

Que se passe t-il ici? Je m’attendrais à ce que le float soit converti en int parce que j’ai utilisé% d mais ce n’est pas ce qui s’est passé. Aussi, pourquoi la valeur B at-elle aussi mal tourné? Qu’est-il arrivé à f ici?

Lorsque vous avez appelé:

 printf("A: %3d B: %6.2f\n", f, f + 0.15); 

C convertit automatiquement les valeurs float en double (il s’agit d’une conversion standard effectuée lorsque vous appelez une fonction prenant des arguments variables, telle que int printf(const char *fmt, ...); ). Par souci d’argumentation, supposons que sizeof(int) vaut 4 et sizeof(double) 8 (il existe des exceptions, mais elles sont rares).

Par conséquent, l’appel a placé un pointeur sur la stack, plus un double de 8 octets pour f et un autre double de 8 octets pour f + 0.15 . Lors du traitement de la chaîne de format, le %d indique à printf() que vous avez placé un int 4 octets sur la stack après la chaîne de format. Puisque ce n’est pas ce que vous avez fait, vous avez invoqué un comportement indéfini. tout ce qui se passe ensuite est OK selon le standard C.

Cependant, l’implémentation la plus probable lit allègrement 4 octets et les affiche comme s’il s’agissait d’un int (il vous dit de dire la vérité). Ensuite, il tombe sur le format %6.2f ; il lira 8 octets de la stack en double . Cela risquerait de provoquer une erreur de mémoire pour un access mal aligné (un ordinateur 64 bits nécessitant le double alignement sur une limite de 8 octets, telle qu’un SPARC) ou une lecture de 4 octets à partir de f et 4 octets à partir de f + 0.15 , en les réunissant pour créer une double valeur plutôt inattendue – comme le montre votre exemple.

Printf traitera la mémoire que vous pointez comme vous le lui direz. Il n’y a pas de conversion en cours. C’est traiter la mémoire qui représente le float comme un int. Parce que les deux sont stockés différemment, vous obtenez ce qui est essentiellement un nombre aléatoire.

Si vous voulez afficher votre float sous forme d’entier, vous devez d’abord le lancer:

 printf("A: %3d B: %6.2f\n", (int)f, f + 0.15);