Wie kann man die Anzahl der Argumente zählen, die an eine Funktion übergeben werden, die eine variable Anzahl von Argumenten akzeptiert?

Lesezeit: 7 Minuten

Benutzeravatar von codeomnitrix
kodomnitrix

So zählen Sie die Anzahl der an die Funktion übergebenen Argumente im folgenden Programm:

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

Die Ausgabe dieses Programms über meinen gcc-Compiler unter Ubuntu 10.04:

234561345138032514932134513792

also wie findet man wie viele nein. der tatsächlich an die Funktion übergebenen Argumente?

  • 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

Benutzeravatar von Alexandre C
Alexandre C.

Du kannst nicht. Sie müssen es irgendwie schaffen, dass der Anrufer die Anzahl der Argumente angibt. Du kannst:

  • Übergeben Sie die Anzahl der Argumente als erste Variable
  • Das letzte Variablenargument muss null, null oder was auch immer sein
  • Lassen Sie das erste Argument beschreiben, was erwartet wird (z. B. diktiert die Zeichenfolge im Format printf, welche Argumente folgen sollen).

  • @codeomnitrix: Es ist traurig, aber wahr. Halten Sie sich als Faustregel von Argumenten mit variabler Länge fern. Es sei denn, Sie tun C++0x.

    – Alexander C.

    12. Dezember 2010 um 16:59 Uhr

  • Halten Sie sich vorerst auch von C++0x fern. Allerdings sind die vielfältigen Templates in C++0x wirklich nett.

    – Matt Tischler

    13. Dezember 2010 um 13:37 Uhr

  • @Matt: Mit variadischen Vorlagen können Sie auch typsichere variadische Funktionen schreiben.

    – Alexander C.

    13. Dezember 2010 um 13:51 Uhr

  • @HarounHajem, C ++ ist keine moderne Sprache, wenn es um Programmiersprachen geht. 🙂

    – Craig B

    4. Dezember 2020 um 0:56 Uhr

  • Nach meinem Verständnis können Sie in Standard-C NULL nicht verwenden, um die Argumentliste einer variadischen Funktion zu beenden, ohne ein undefiniertes Verhalten zu riskieren. Daher ist Ihr zweiter Aufzählungspunkt, obwohl er häufig funktioniert, eine schlechte Wahl. Sie müssen der Funktion grundsätzlich mitteilen, wie viele Argumente es gibt, wobei Ihr erster und zweiter Aufzählungspunkt die einzigen zwei möglichen Möglichkeiten sind. Einige Compiler haben jedoch Funktionen, die einen NULL-Terminator zulassen, wie gcc, der dies hat __attribute__((sentinel)) während andere Compiler für C-ähnliche Sprachen beispielsweise NS_REQUIRES_NIL_TERMINATION.

    – SO stinkt

    13. Juni 2021 um 2:04 Uhr

Benutzeravatar von Dan Fabulich
Dan Fabulich

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 Nund das Ergebnis des Makros zu definieren N.

PP_NARG ruft PP_128TH_ARG mit __VA_ARGS__ verkettet mit PP_RSEQ_Neine 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_NARGdann 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.)

  • Ich habe deine Wege überprüft. Vielen Dank! Siehe Kommentar zur Antwort für Details: stackoverflow.com/questions/1688942/…

    – Benutzer1742529

    3. Juni 2021 um 16:07 Uhr

Wenn Sie einen C99-kompatiblen Compiler (einschließlich des Präprozessors) haben, können Sie dieses Problem umgehen, indem Sie ein Makro deklarieren, das die Anzahl der Argumente für Sie berechnet. Dies selbst zu tun ist ein bisschen schwierig, Sie können verwenden P99_VA_ARGS von dem Makropaket P99 um das zu erreichen.

  • Ok danke, aber viel mehr übersteigt mein Wissen. Ich werde versuchen, diesen zu verstehen

    – Kodomnitrix

    13. Dezember 2010 um 6:43 Uhr

Du kannst nicht. Etwas anderes muss Ihnen sagen (z. B. für printf wird dies durch die Anzahl der %-Formatdeskriptoren im Format-String impliziert)

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).

  • Ich habe NULL als Sentinel für eine Liste von Zeigerargumenten verwendet und es hat für mich funktioniert, aber ich habe festgestellt, dass dies anscheinend im Allgemeinen zu einem undefinierten Verhalten führen kann, da NULL in einigen Architekturen implementiert ist.

    – SO stinkt

    12. Juni 2021 um 14:16 Uhr

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

  • Ich habe NULL als Sentinel für eine Liste von Zeigerargumenten verwendet und es hat für mich funktioniert, aber ich habe festgestellt, dass dies anscheinend im Allgemeinen zu einem undefinierten Verhalten führen kann, da NULL in einigen Architekturen implementiert ist.

    – SO stinkt

    12. Juni 2021 um 14:16 Uhr

Benutzeravatar von CodeAngry
CodeWütend

Sie könnten auch einen aussagekräftigen Wert verwenden, der das Ende der Argumente anzeigt. Wie eine 0 oder -1. Oder eine maximale Schriftgröße wie 0xFFFF für a ushort.

Andernfalls müssen Sie die Anzahl im Voraus erwähnen oder von einem anderen Argument abziehen (format zum printf() wie Funktionen).

1410120cookie-checkWie kann man die Anzahl der Argumente zählen, die an eine Funktion übergeben werden, die eine variable Anzahl von Argumenten akzeptiert?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy