Comment compter le nombre 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);
}
La sortie de ce programme sur mon compilateur gcc sous Ubuntu 10.04:
234561345138032514932134513792
alors comment trouver combien de non. d'arguments réellement passés à la fonction?
Tu ne peux pas. Vous devez gérer pour que l'appelant indique en quelque sorte le nombre d'arguments. Vous pouvez:
Vous pouvez laisser le préprocesseur vous aider à tricher en utilisant cette stratégie, volée et modifiée depuis ne 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 intelligentes 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 à 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 cheval de bataille ici est PP_ARG_N
. Il renvoie son 128e argument, en ignorant les 127 premiers arguments (nommés arbitrairement _1
_2
_3
etc.), en nommant le 128e argument N
et en 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 128e valeur de 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 de PP_ARG_N
sera égal à 1. Ainsi, chaque argument de __VA_ARGS__
bosses PP_RSEQ_N
plus d'un, laissant la bonne réponse dans le 128ème emplacement.
(Apparemment 127 arguments est le maximum autorisé par C .)
Tu ne peux pas. Quelque chose d'autre doit vous dire (par exemple pour printf, cela est impliqué par le nombre de descripteurs de format% dans la chaîne de format)
Si vous disposez d'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 package de macros P99 pour y parvenir.
Tu ne peux pas. varargs n'est pas conçu pour rendre cela possible. Vous devez implémenter un autre mécanisme pour indiquer à la fonction le nombre d'arguments. Un choix courant consiste à passer un argument sentinelle à la fin de la liste des paramètres, par exemple:
varfun(1, 2, 3, 4, 5, 6, -1);
Une autre consiste à passer le dé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 ou d'oublier de le mettre à jour que de se souvenir et de maintenir la sentinelle à la fin.
C'est à vous de voir comment vous le faites (considérez le modèle de printf, dans lequel la chaîne de format détermine combien - et quel type - d'arguments il y a).
Le moyen le plus sûr est celui décrit ci-dessus. Mais si vous avez VRAIMENT besoin de connaître le nombre d'arguments sans ajouter l'argument supplémentaire mentionné, vous pouvez le faire de cette façon (mais notez qu'il dépend beaucoup de la machine, du système d'exploitation et même, dans de rares cas, du compilateur). J'ai exécuté ce code à l'aide de 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 des int. Si vous avez des arguments de taille différente, il faut que je fasse quelques ajustements.
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 (2) forme longue. Dans le 2ème test, j'ai réussi une structure parce que je voulais simuler de nombreux 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);
}
Lisez un pointeur vers des pointeurs depuis EBP.
#define getReturnAddresses() void ** puEBP = NULL; __asm { mov puEBP, ebp };
Usage
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 C86 x86 de la méthode __cdecl qui a pris un nombre variable d'arguments pour réussir.
Vous devrez peut-être ajuster la partie -1 en fonction de votre pile/arguments.
Je n'ai pas trouvé cette méthode. Je pense que je l'ai peut-être trouvé sur les forums UC à un moment donné.
Je ne peux pas recommander d'utiliser cela dans du code propper, mais si vous avez un détour hacky sur un exe x86 avec la convention d'appel __cdecl avec 1 argument et le reste sont ... des arguments variables, cela pourrait fonctionner. (Win32)
Exemple d'appel de la méthode de détour.
void __cdecl hook_ofSomeKind_va_list(void* self, unsigned char first, ...)
Ajouter ou NULL à la fin me permet d'avoir un certain nombre d'arguments et de ne pas avoir peur qu'il sorte 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);
}
Dans ce code, il est possible lorsque vous passez uniquement 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;
}
Vous pouvez également utiliser une valeur significative qui indique la fin des arguments. Comme un 0 ou -1. Ou une taille de type maximale comme 0xFFFF pour un ushort
.
Sinon, vous devez mentionner le nombre à l'avance ou le déduire d'un autre argument (format
pour printf()
comme des fonctions) .