2010-12-12 43 views
23

Comment compter les pas d'arguments passés à la fonction dans le programme suivant:Comment compter le nombre d'arguments passés à une fonction qui accepte un nombre variable d'arguments?

#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); 
} 

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

234561345138032514932134513792 

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

+0

http://groups.google.com/group/comp.std.c/browse_frm/ thread/77ee8c8f92e4a3fb? pli = 1 – Nyan

+0

votre programme fonctionne bien sur ma machine. Il imprime tous les arguments passés à la fonction – Shweta

+0

question connexe: [Macro pour compter le nombre d'arguments] (http://stackoverflow.com/q/11317474/1336150) –

Répondre

31

Vous ne pouvez pas. Vous devez gérer pour l'appelant d'indiquer le nombre d'arguments en quelque sorte. Vous pouvez:.

  • passer le nombre d'arguments que la première variable
  • Exiger le dernier argument variable nul, zéro ou tout
  • Demandez le premier argument décrire ce qui est attendu (par exemple, le format de printf chaîne dicte quels arguments doivent suivre)
+0

@codeomnitrix: c'est triste mais vrai. Restez à l'écart des arguments de longueur variable. Sauf si vous faites C++ 0x. –

+1

Pour l'instant, évitez également C++ 0x. Cependant les modèles variadiques en C++ 0x sont vraiment sympa. –

+0

@Matt: Avec les modèles variadiques, vous pouvez également écrire des fonctions variées de type sécurité. –

6

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

3

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 d'arguments il y a. Un choix commun est de passer un argument sentinelle à la fin de la liste des paramètres, par exemple:

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

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

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

C'est plus propre, mais pas sûr, car il est plus facile de corriger le compte, ou d'oublier de le mettre à jour, que de se souvenir et de maintenir la sentinelle à la fin.

À vous de voir comment vous le faites (pensez au modèle de printf, dans lequel la chaîne de format détermine le nombre et le type d'arguments).

4

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 à partir du P99 macro package pour y parvenir.

+0

Ok merci, mais beaucoup plus au-delà de mes connaissances. Je vais essayer de comprendre celui-ci – codeomnitrix

-2

Il est possible et il est simple, il suffit d'ajouter une autre variable k dans la boucle et l'affecter initialement 1, 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); 
} 
+0

k indique le nombre d'arguments passés à la fonction – Faijz

+1

Son programme a déjà une variable 'i' qui est presque la même que' k'. 'k' sera toujours' i + 1'. De plus, votre compteur 'k' compte sur sa boucle pour parcourir les arguments correctement, mais sa boucle n'est pas correcte (elle dépasse la fin des nombres valides) donc votre compte ne sera pas correct. – steveha

+0

J'ai essayé ce code en changeant le nombre d'arguments d'entrée que je passe à la fonction et chaque fois que j'ai eu la bonne réponse ..... – Faijz

0

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

Sinon, vous devez mentionner le nombre initial ou le rendre déductibles d'un autre argument (format pour printf() comme des fonctions).

2

La manière la plus sûre est celle 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 que cela dépend de la machine, de l'OS et même, dans de rares cas, du compilateur). J'ai couru ce code en utilisant Visual Studio 2013 sur un 64 bits DELL E6440.

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

Cela dépend du programme appelant pour utiliser la convention d'appel C standard. (varfun() obtient le nombre d'arguments de "add esp, xxx" et il y a deux formes de l'add, (1) forme courte et forme longue (2) Dans le 2ème test j'ai passé une structure 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); 
} 
5

Vous pouvez laisser l'aide de préprocesseur vous tentez de tricher en utilisant cette stratégie, volée et peaufiné de another answer:

#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 trucs astucieux ici.

1) Au lieu d'appeler directement la fonction variadique, vous appelez une macro qui compte les arguments et transmet le nombre d'arguments comme premier argument de la fonction. Le résultat final du préprocesseur sur la principale ressemble:

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

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

Le cheval de trait ici est PP_ARG_N. Il retourne son 128ème argument, en ignorant les 127 premiers arguments (nommés arbitrairement _1_2_3 etc.), en nommant le 128ème argument N, et en définissant le résultat de la macro à N.

PP_NARGPP_ARG_N avec __VA_ARGS__ invoque concaténé avec PP_RSEQ_N, une séquence inversée de numéros à compter de 127 à 0.

Si vous fournissez pas d'arguments, la valeur 128e PP_RSEQ_N est 0. Si vous passez un argument à PP_NARG , alors cet argument sera passé à PP_ARG_N comme _1; _2 sera 127, et le 128e argument à PP_ARG_N sera 1. Ainsi, chaque argument dans __VA_ARGS__ bumps PP_RSEQ_N plus d'un, laissant la bonne réponse dans le 128e emplacement.

(Apparemment 127 arguments is the maximum C allows.)

0

Dans ce code, il est possible que lorsque vous passez seulement 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; 
}