Comment compter le nombre d'arguments passés à une fonction qui accepte un nombre variable d'arguments?

Comment compter les pas d'arguments passés à la fonction dans le programme suivant:

#include<stdio.h>
#include<stdarg.h>
void varfun(int i, ...);
int main(){
        varfun(1, 2, 3, 4, 5, 6);
        return 0;
}
void varfun(int n_args, ...){
        va_list ap;
        int i, t;
        va_start(ap, n_args);
        for(i=0;t = va_arg(ap, int);i++){
               printf("%d", t);
        }
        va_end(ap);
}

sortie de ce programme sur mon compilateur gcc sous ubuntu 10.04:

234561345138032514932134513792

alors comment trouver combien de no. des arguments passés à la fonction?

30
demandé sur Abizern 2010-12-12 15:40:16

11 réponses

vous ne pouvez pas. Vous devez gérer pour l'appelant d'indiquer le nombre d'arguments d'une façon ou d'une autre. Vous pouvez:

  • passez le nombre d'arguments comme la première variable
  • Exiger la dernière variable argument est nul, zéro ou que ce soit
  • le premier argument de décrire ce qui est attendu (par exemple. la chaîne de format printf dicte quels arguments doivent suivre)
41
répondu Alexandre C. 2016-11-15 19:09:26

vous pouvez laisser le préprocesseur vous aider à tricher en utilisant cette stratégie, volé et modifié de une autre réponse :

#include <stdio.h>
#include <stdarg.h>

#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
         _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
         _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
         _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
         _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
         _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
         _121,_122,_123,_124,_125,_126,_127,N,...) N
#define PP_RSEQ_N() \
         127,126,125,124,123,122,121,120, \
         119,118,117,116,115,114,113,112,111,110, \
         109,108,107,106,105,104,103,102,101,100, \
         99,98,97,96,95,94,93,92,91,90, \
         89,88,87,86,85,84,83,82,81,80, \
         79,78,77,76,75,74,73,72,71,70, \
         69,68,67,66,65,64,63,62,61,60, \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

void _variad(size_t argc, ...);
#define variad(...) _variad(PP_NARG(__VA_ARGS__), __VA_ARGS__)

void _variad(size_t argc, ...) {
    va_list ap;
    va_start(ap, argc);
    for (int i = 0; i < argc; i++) {
        printf("%d ", va_arg(ap, int));
    }
    printf("\n");
    va_end(ap);
}

int main(int argc, char* argv[]) {
    variad(2, 4, 6, 8, 10);
    return 0;
}

il y a quelques astuces ici.

1) au lieu d'appeler directement la fonction variadique, vous appelez une macro qui compte les arguments et passe l'argument count comme premier argument à la fonction. Le résultat final du préprocesseur sur main ressemble à:

_variad(5, 2, 4, 6, 8, 10);

2) PP_NARG est une macro intelligente pour compter les arguments.

Le bourreau de travail ici est PP_ARG_N . Il renvoie son 128ème argument, en ignorant les 127 premiers arguments (nommés arbitrairement _1 _2 _3 etc.), nommant le 128ème argument N , et définissant le résultat de la macro comme N .

PP_NARG invoque PP_ARG_N avec __VA_ARGS__ concaténé avec PP_RSEQ_N , une séquence inversée de nombres comptant de 127 à 0.

si vous ne fournissez aucun argument, la 128ème valeur de PP_RSEQ_N est 0. Si vous passez un argument PP_NARG , alors que l'argument est passé à PP_ARG_N comme _1 ; _2 127, et la 128ème argument PP_ARG_N sera de 1. Ainsi, chaque argument dans __VA_ARGS__ bosses PP_RSEQ_N plus d'un, laissant la bonne réponse dans le 128e slot.

(apparemment 127 arguments est le maximum C permet .)

8
répondu Dan Fabulich 2017-05-23 11:47:09

vous ne pouvez pas. Quelque chose d'autre doit vous dire (par exemple pour printf, il est implicite par le nombre de descripteurs de format % dans la chaîne de format)

6
répondu The Archetypal Paul 2010-12-12 12:44:12

si vous avez un compilateur compatible C99 (y compris le préprocesseur), vous pouvez contourner ce problème en déclarant une macro qui calcule le nombre d'arguments pour vous. Faire cela vous-même est un peu délicat, vous pouvez utiliser P99_VA_ARGS du paquet macro P99 pour atteindre cet objectif.

4
répondu Jens Gustedt 2010-12-12 22:18:49

vous ne pouvez pas. les varargs ne sont pas conçus pour rendre cela possible. Vous devez implémenter un autre mécanisme pour indiquer à la fonction combien il y a d'arguments. Un choix courant est de passer un argument sentinelle à la fin de la liste des paramètres, par exemple:

varfun(1, 2, 3, 4, 5, 6, -1);

un autre est de passer le compte au début:

varfun(6, 1, 2, 3, 4, 5, 6);

c'est plus propre, mais pas aussi sûr, car il est plus facile de se tromper sur le compte, ou d'oublier de le mettre à jour, qu'il ne l'est pour se souvenir et maintenir la sentinelle à la fin.

C'est à vous de décider comment vous le faire (pensez printf modèle, dans lequel la chaîne de format détermine combien et quel type d'arguments).

3
répondu Marcelo Cantos 2010-12-12 12:45:02

la manière la plus sûre est décrite ci-dessus. Mais si vous avez vraiment besoin de connaître le nombre d'arguments sans ajouter l'argument supplémentaire mentionné alors vous pouvez le faire de cette façon (mais notez qu'il est très dépendant de la machine, dépendant de L'OS et même, dans de rares cas, dépendant du compilateur). J'ai utilisé ce code avec Visual Studio 2013 sur un DELL E6440 64 bits.

un autre point, au point où j'ai divisé par sizeof(int), c'était parce que tous mes arguments étaient int. Si vous avez différents arguments de taille, là mon besoin d'être un peu d'ajustement là.

Cela repose sur le programme d'appel à utiliser le standard C convention d'appel. (varfun () obtient le nombre d'arguments du "add esp,xxx" et il y a deux formes de l'add, (1) forme courte et (2) forme longue. Dans le 2e test j'ai réussi une struct parce que je voulais simuler beaucoup d'arguments pour forcer la forme longue).

les réponses imprimées seront 6 et 501.

    varfun(1, 2, 3, 4, 5, 6);
00A03CC8 6A 06                push        6  
00A03CCA 6A 05                push        5  
00A03CCC 6A 04                push        4  
00A03CCE 6A 03                push        3  
00A03CD0 6A 02                push        2  
00A03CD2 6A 01                push        1  
00A03CD4 E8 E5 D3 FF FF       call        _varfun (0A010BEh)  
00A03CD9 83 C4 18             add         esp,18h  
    varfun(1, x);
00A03CDC 81 EC D0 07 00 00    sub         esp,7D0h  
00A03CE2 B9 F4 01 00 00       mov         ecx,1F4h  
00A03CE7 8D B5 28 F8 FF FF    lea         esi,[x]  
00A03CED 8B FC                mov         edi,esp  
00A03CEF F3 A5                rep movs    dword ptr es:[edi],dword ptr [esi]  
00A03CF1 6A 01                push        1  
00A03CF3 E8 C6 D3 FF FF       call        _varfun (0A010BEh)  
00A03CF8 81 C4 D4 07 00 00    add         esp,7D4h 



#include<stdio.h>
#include<stdarg.h>
void varfun(int i, ...);
int main()
{
    struct eddy
    {
        int x[500];
    } x = { 0 };
    varfun(1, 2, 3, 4, 5, 6);
    varfun(1, x);
    return 0;
}

void varfun(int n_args, ...)
{
    va_list ap;
    unsigned long *p;
    unsigned char *p1;
    unsigned int nargs;
    va_start(ap, n_args);
    p = (long *)(ap - _INTSIZEOF(int) - _INTSIZEOF(&varfun));
    p1 = (char *)*p;
    if (*p1 == 0x83)     // short add sp,x
    {
        nargs = p1[2] / sizeof(int);
    }
    else
    {
        nargs = *(unsigned long *)(p1+2) / sizeof(int);
    }
    printf("%d\n", nargs);
    va_end(ap);
}
3
répondu eddyq 2015-05-23 06:14:58

vous pouvez également utiliser une valeur significative qui indique la fin des arguments. Comme un 0 ou un -1. Ou une taille de type max comme 0xFFFF pour un ushort .

sinon, vous devez mentionner le compte à l'avance ou le rendre déductible d'un autre argument ( format pour printf() fonctions similaires) .

0
répondu CodeAngry 2014-05-14 07:17:01

dans ce code il est possible quand vous passez seulement le pointeur

# include <unistd.h>
# include <stdarg.h>
# include <string.h>
# include <errno.h>

size_t __print__(char * str1, ...);
# define print(...) __print__(NULL, __VA_ARGS__, NULL)
# define ENDL "\n"

int main() {

  print("1", ENDL, "2", ENDL, "3", ENDL);

  return 0;
}

size_t __print__(char * str1, ...) {
    va_list args;
    va_start(args, str1);
    size_t out_char = 0;
    char * tmp_str;
    while((tmp_str = va_arg(args, char *)) != NULL)
        out_char = out_char + write(1, tmp_str,strlen(tmp_str));
    va_end(args);
    return out_char;
}
0
répondu Bikash 2017-09-10 03:07:38

lire un pointeur vers les pointeurs de L'EBP.

#define getReturnAddresses() void ** puEBP = NULL; __asm { mov puEBP, ebp };

Utilisation

getReturnAddresses();
int argumentCount = *((unsigned char*)puEBP[1] + 2) / sizeof(void*) ;
printf("CalledFrom: 0x%08X Argument Count: %i\n", puEBP[1], argumentCount);

pas portable, mais je l'ai utilisé dans un détour x86 C++ de la méthode __cdecl qui a pris un nombre variable d'arguments à un certain succès.

vous devrez peut-être ajuster la partie -1 en fonction de votre pile/arguments.

Je n'ai pas inventé cette méthode. Suspect Je l'ai peut-être trouvé sur les forums UC à un moment donné.

Je ne peux pas recommander de l'utiliser dans le code propper, mais si vous avez un détour hacky sur un x86 exe avec la convention d'appel __cdecl avec 1 argument et alors les autres sont ... arguments variables il pourrait fonctionner. (Win32)

Exemple de convention d'appel de détour méthode.

void __cdecl hook_ofSomeKind_va_list(void* self, unsigned char first, ...)

preuve: capture d'Écran montrant la sortie de la console à côté de x32dbg sur la cible avec un détour appliquée

0
répondu Liam Mitchell 2018-07-18 08:31:17

ajouter ou NULL à la fin me permet d'avoir n'importe quel nombre d'arguments et ne pas s'inquiéter de le sortir de la pile

#include <cstdarg>
template<typename _Ty>
inline void variadic_fun1(_Ty param1,...)
{
    va_list arg1;
    //TO_DO

    va_end(arg1);
}
template<typename _Ty> 
void variadic_fun2(_Ty param1,...)
{
    va_list arg1;
    va_start(arg1, param1);
    variadic_fun1(param1, arg1, 0);
    va_end(arg1);
}
0
répondu Dexter's Code Lab 2018-08-05 13:52:54

de Ses possible et c'est simple, il suffit d'ajouter une autre variable k dans la boucle et l'affecter à l'origine le 1er, essayez ce code

#include <stdio.h>
#include <stdarg.h>

void varfun(int i, ...);

int main(){
        varfun(1,2);
        return 0;
}

void varfun(int n_args, ...)
        {
        va_list ap;
        int i, t, k;
        k = 1;
        va_start(ap, n_args);
        for(i=0;i <= va_arg(ap, int);i++){
               k+=1;
        }
        printf("%d",k);
        va_end(ap);
}
-2
répondu Faijz 2012-06-14 23:33:45