Sie können sich vom Präprozessor helfen lassen, mit dieser Strategie zu schummeln, die von einer anderen Antwort gestohlen und optimiert wurde:
#include <stdio.h>
#include <stdarg.h>
#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_128TH_ARG(__VA_ARGS__)
#define PP_128TH_ARG( \
_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;
}
Hier gibt es ein paar clevere Tricks.
1) Anstatt die variadische Funktion direkt aufzurufen, rufen Sie ein Makro auf, das die Argumente zählt und die Anzahl der Argumente als erstes Argument an die Funktion übergibt. Das Endergebnis des Präprozessors auf main sieht so aus:
_variad(5, 2, 4, 6, 8, 10);
2) PP_NARG
ist ein cleveres Makro zum Zählen von Argumenten.
Das Arbeitstier hier ist PP_128TH_ARG
. Es gibt sein 128. Argument zurück, indem es die ersten 127 Argumente (willkürlich benannt) ignoriert _1
_2
_3
usw.), wobei das 128. Argument genannt wird N
und das Ergebnis des Makros zu definieren N
.
PP_NARG
ruft PP_128TH_ARG
mit __VA_ARGS__
verkettet mit PP_RSEQ_N
eine umgekehrte Zahlenfolge, die von 127 bis 0 herunterzählt.
Wenn Sie keine Argumente angeben, wird der 128. Wert von PP_RSEQ_N
ist 0. Wenn Sie ein Argument an übergeben PP_NARG
dann wird dieses Argument übergeben PP_128TH_ARG
wie _1
; _2
wird 127 sein, und das 128. Argument zu PP_128TH_ARG
wird 1 sein. Somit ist jedes Argument in __VA_ARGS__
Beulen PP_RSEQ_N
um eins vorbei, wobei die richtige Antwort im 128. Slot bleibt.
(Anscheinend sind 127 Argumente das Maximum, das C zulässt.)
Du kannst nicht. Varargs sind nicht darauf ausgelegt, dies zu ermöglichen. Sie müssen einen anderen Mechanismus implementieren, um der Funktion mitzuteilen, wie viele Argumente vorhanden sind. Eine gängige Wahl besteht darin, ein Sentinel-Argument am Ende der Parameterliste zu übergeben, z. B.:
varfun(1, 2, 3, 4, 5, 6, -1);
Eine andere besteht darin, die Zählung am Anfang zu übergeben:
varfun(6, 1, 2, 3, 4, 5, 6);
Das ist sauberer, aber nicht so sicher, da es einfacher ist, die Zählung falsch zu machen oder zu vergessen, sie zu aktualisieren, als sich am Ende an den Sentinel zu erinnern und ihn zu pflegen.
Es liegt an Ihnen, wie Sie es tun (betrachten Sie das Modell von printf, in dem der Format-String bestimmt, wie viele – und welche Art – von Argumenten es gibt).
Der sicherste Weg ist wie oben beschrieben. Aber wenn Sie WIRKLICH die Anzahl der Argumente wissen müssen, ohne das erwähnte zusätzliche Argument hinzuzufügen, können Sie dies auf diese Weise tun (beachten Sie jedoch, dass dies sehr maschinenabhängig, betriebssystemabhängig und in seltenen Fällen sogar vom Compiler abhängig ist). Ich habe diesen Code mit Visual Studio 2013 auf einem 64-Bit-DELL E6440 ausgeführt.
Ein weiterer Punkt, an dem ich durch sizeof(int) geteilt habe, lag daran, dass alle meine Argumente ints waren. Wenn Sie unterschiedliche Größenargumente haben, muss dort etwas angepasst werden.
Dies beruht darauf, dass das aufrufende Programm die standardmäßige C-Aufrufkonvention verwendet. (varfun() erhält die Anzahl der Argumente von “add esp,xxx” und es gibt zwei Formen des add, (1) Kurzform und (2) Langform. Im 2. Test habe ich eine Struktur bestanden, weil ich es wollte viele Argumente simulieren, um die Langform zu erzwingen).
Die ausgedruckten Antworten sind 6 und 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);
}
groups.google.com/group/comp.std.c/browse_frm/thread/…
– Nyan
12. Dezember 2010 um 13:31 Uhr
Ihr Programm funktioniert auf meiner Maschine einwandfrei. Es gibt alle an die Funktion übergebenen Argumente aus
– Schweta
6. Mai 2011 um 10:08 Uhr
verwandte Frage: Makro zum Zählen der Anzahl der Argumente
– Eitan T
23. Februar 2017 um 13:22 Uhr