Il se trouve que je viens de vivre une situation similaire à celle de cette question de deux ans:
La fonction variée (va_arg) ne fonctionne pas avec float?
On dit que le problème est de faire en sorte que float double lorsque nous appelons des choses comme
va_arg(arg, float)
Ma question se trouve à la fin de cet article, mais examinons d’abord la réponse de @ Jack ci-dessous à la question liée ci-dessus:
#include #include void foo(int n, ...) { va_list vl; va_start(vl, n); int c; double val; for(c = 0; c < n; c++) { val = va_arg(vl, double); printf("%f\n", val); } va_end(vl); } int main(void) { foo(2, 3.3f, 4.4f); return 0; }
Sortie:
3.300000 4.400000
Maintenant, si nous changeons val = va_arg(vl, double)
en val = va_arg(vl, float)
, nous aurons (au moins, je recevrai dans MSVS 2012):
36893488147419103000.000000 2.162500
Passons à ma question maintenant.
Dans cette rubrique: C / C ++ va_list ne renvoie pas les arguments correctement, la réponse la plus votée. Le commentaire indique que printf
encourage également les doublons de float
.
Mais quelle est la différence? Si tous deux promeuvent float
en double
, pourquoi printf
écrit correctement les valeurs, alors que va_arg
nous donne un tel démon nasal?
Ce n’est pas printf
qui promeut l’argument float à double
, c’est le compilateur qui le fait. En d’autres termes, au moment où votre va_arg
ou printf
ou toute autre fonction avec un nombre variable d’arguments obtient le contrôle, tous les float
s sont déjà promus au double
s; les float
origine ne sont pas disponibles pour la récupération.
La différence entre printf
et va_arg
est que printf
suit les règles définies par le standard et demande le paramètre d’un type promu (c’est-à-dire double
) lorsqu’il voit le spécificateur de format correspondant dans la chaîne de format. Par conséquent, il obtient un double
avec la valeur promue du float
et produit le résultat souhaité.
Par ailleurs, lorsque va_arg
appelle val = va_arg(vl, float)
il ignore la règle de promotion et obtient une représentation non valide en retour.
printf
ne prend pas d’arguments de type float
.
Le spécificateur de format "%f"
, par exemple, nécessite un argument de type double
. "%Lf"
nécessite un argument de type long double
. Il n’existe pas de format nécessitant un argument de type float
(qui serait néanmoins promu au double
, pas par printf
mais simplement à cause de la sémantique des appels à des fonctions variadiques).
Donc, en supposant que printf
soit implémenté en C et qu’il utilise les mécanismes
pour lire ses arguments, il n’y a pas d’appel de va_arg()
pour le type float
dans l’implémentation de printf
.
Toute fonction variadique qui tente d’appeler va_arg()
pour le type float
aura un comportement indéfini, car il ne peut y avoir d’argument float
pour une telle fonction. printf
fonctionne parce qu’il ne le fait pas.
Les arguments des fonctions à arguments variables obtiennent des règles de promotion spéciales.
Ce qui est pertinent ici est que passer un float en tant qu’argument variable devient un double. Cela signifie que vous ne pouvez pas extraire l’argument sous forme de float, car il a été passé sous forme de double. Ceci est fait par le compilateur, cela n’a rien à voir avec printf
.
Cela signifie que le code val = va_arg(vl, float)
n’est pas valide, car l’argument n’est pas un float, c’est un double. Si vous avez vraiment besoin de traiter les valeurs transmises comme un float, au mieux, vous pouvez le faire.
float val = (float) va_arg(vl, double)
Notez que le spécificateur %f
pour printf attend un argument de type double
, pas un float
Mais quelle est la différence? Si tous deux promeuvent
float
endouble
, pourquoiprintf
écrit correctement les valeurs, alors queva_arg
nous donne un tel démon nasal?
Il n’y a pas de différence, à l’exception du fait (indiqué dans la question elle-même), que printf
est codé de manière à traiter le type float
comme double
. En d’autres termes, quelque part dans printf
, lorsque la chaîne de format contient des informations indiquant qu’il doit y avoir un nombre à virgule flottante, la fonction effectue va_arg(vl, double)
, comme vous l’avez fait.