How to count the number of arguments passed to a function that accepts a variable number of arguments?

57,624

Solution 1

You can't. You have to manage for the caller to indicate the number of arguments somehow. You can:

  • Pass the number of arguments as the first variable
  • Require the last variable argument to be null, zero or whatever
  • Have the first argument describe what is expected (eg. the printf format string dictates what arguments should follow)

Solution 2

You can let the preprocessor help you cheat using this strategy, stolen and tweaked from another answer:

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

There's a few clever tricks here.

1) Instead of calling the variadic function directly, you're calling a macro that counts the arguments and passes the argument count as the first argument to the function. The end result of the preprocessor on main looks like:

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

2) PP_NARG is a clever macro to count arguments.

The workhorse here is PP_128TH_ARG. It returns its 128th argument, by ignoring the first 127 arguments (named arbitrarily _1 _2 _3 etc.), naming the 128th argument N, and defining the result of the macro to be N.

PP_NARG invokes PP_128TH_ARG with __VA_ARGS__ concatenated with PP_RSEQ_N, a reversed sequence of numbers counting from 127 down to 0.

If you provide no arguments, the 128th value of PP_RSEQ_N is 0. If you pass one argument to PP_NARG, then that argument will be passed to PP_128TH_ARG as _1; _2 will be 127, and the 128th argument to PP_128TH_ARG will be 1. Thus, each argument in __VA_ARGS__ bumps PP_RSEQ_N over by one, leaving the correct answer in the 128th slot.

(Apparently 127 arguments is the maximum C allows.)

Solution 3

If you have a C99 compliant compiler (including the preprocessor) you can circumvent this problem by declaring a macro that computes the number of arguments for you. Doing this yourself is a bit tricky, you may use P99_VA_ARGS from the P99 macro package to achieve this.

Solution 4

You can't. Something else has to tell you (for instance for printf, it's implied by the number of % format descriptors in the format string)

Solution 5

You can't. varargs aren't designed to make this possible. You need to implement some other mechanism to tell the function how many arguments there are. One common choice is to pass a sentinel argument at the end of the parameter list, e.g.:

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

Another is to pass the count at the beginning:

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

This is cleaner, but not as safe, since it's easier to get the count wrong, or forget to update it, than it is to remember and maintain the sentinel at the end.

It's up to you how you do it (consider printf's model, in which the format string determines how many — and what type — of arguments there are).

Share:
57,624
codeomnitrix
Author by

codeomnitrix

Young | Enthusiastic | Ecstatic | Raw Programmer | Counter-Strike

Updated on September 04, 2021

Comments

  • codeomnitrix
    codeomnitrix over 2 years

    How to count the no of arguments passed to the function in following program:

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

    This program's output over my gcc compiler under ubuntu 10.04:

    234561345138032514932134513792
    

    so how to find how many no. of arguments actually passed to the function?

  • Alexandre C.
    Alexandre C. over 13 years
    @codeomnitrix: it's sad but true. As a rule of thumb, stay away from variable length arguments. Unless you do C++0x.
  • codeomnitrix
    codeomnitrix over 13 years
    Ok thanks, but much more beyond my c knowledge. I will try to understand this one
  • Matt Joiner
    Matt Joiner over 13 years
    For now, also stay away from C++0x. However the variadic templates in C++0x are really nice.
  • Alexandre C.
    Alexandre C. over 13 years
    @Matt: With variadic templates you can also write type safe variadic functions.
  • Owl
    Owl almost 6 years
    How do you know that the next pointer is null? Or is it pointing to uninitialised memory?
  • Alexander
    Alexander over 5 years
    Beware that printf("%%") expects no arguments, though.
  • Haroun Hajem
    Haroun Hajem almost 4 years
    Every modern language has the ability to count a list except C++, what a shame.
  • Craig B
    Craig B over 3 years
    @HarounHajem, C++ is not a modern language as programming languages go. :)
  • user1742529
    user1742529 almost 3 years
    I checked your ways. Thank you! See comment to answer for details: stackoverflow.com/questions/1688942/…
  • SO Stinks
    SO Stinks almost 3 years
    I've been using NULL as a sentinel for a list of pointer arguments and it has worked for me but I've discovered that apparently in general this could result in undefined behavior because of how NULL is implemented in some architectures.
  • SO Stinks
    SO Stinks almost 3 years
    It is my understanding that in standard C you cannot use NULL to terminate the argument list of a variadic function without risking undefined behavior so your second bullet point, while it might often work, is a bad choice. You basically must tell the function how many arguments there are, with your first and second bullet points being the only two feasible possibilities. Some compilers however have features that allow a NULL terminator like gcc which has __attribute__((sentinel)) while other C-like language compilers have, for example, NS_REQUIRES_NIL_TERMINATION.
  • berhauz
    berhauz over 2 years
    whoever put me a negative quote on this answer has possibly not seen that after the traditional for loop that indeed doesn't provide any direct count, the initializer list allows using: elts.size() that perfectly answers the question.