La fonction variée (va_arg) ne fonctionne pas avec float, contrairement à printf? Quelle est la différence?

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 en double , pourquoi printf écrit correctement les valeurs, alors que va_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.