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?
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)
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 .)
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)
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.
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).
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);
}
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) .
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;
}
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, ...)
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);
}
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);
}