6

I am trying to convert some Java code into C. The Java code goes like this:

public static int minimum( int... minimum ) {

    assert( minimum.length > 0 );

    if ( minimum.length > 0 )
     .... // some code that i am able to translate to C without any hassle
}

Now I understand how to have varargs in C by using the stdarg.h header and using the macros provided. However I am stuck doing the minimum.length part.

I have tried strlen but the terminal is giving me an incompatible integer to pointer conversion warning. Is there any way in C where I can replicate the same thing that Java does?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Saad
  • 399
  • 8
  • 25
  • 1
    Does this answer your question? [How to determine the end of va\_arg list?](https://stackoverflow.com/questions/37356030/how-to-determine-the-end-of-va-arg-list) – Joshua Oct 04 '21 at 03:50

2 Answers2

9

Not directly, as pointed out by @MichaelBurr, you need to pass the number of elements or use a sentinel.

An indirect way to do this is using compound literals:

#include <stdio.h>
#include <stdarg.h>
#include <limits.h>

#define minimum(...) fnminimum(sizeof((int []) {__VA_ARGS__}) / sizeof(int), __VA_ARGS__)

static int fnminimum(int n, ...)
{
    int num, min = INT_MAX;
    va_list ap;

    va_start(ap, n);
    while (n--) {
        num = va_arg(ap, int);
        if (num < min) {
            min = num;
        }
    }
    va_end(ap);
    return min;
}

int main(void)
{
    int a = 1;

    printf("%d\n", minimum(2, 30, 7, a++, 4));
    return 0;
}

Another (ugly) method using NARGS macros (limited to N args):

#include <stdio.h>
#include <stdarg.h>
#include <limits.h>

#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define minimum(...) fnminimum(NARGS(__VA_ARGS__), __VA_ARGS__)

static int fnminimum(int n, ...)
{
    int num, min = INT_MAX;
    va_list ap;

    va_start(ap, n);
    while (n--) {
        num = va_arg(ap, int);
        if (num < min) {
            min = num;
        }
    }
    va_end(ap);
    return min;
}

int main(void)
{
    printf("%d\n", minimum(2, 30, 7, 1, 4));
    return 0;
}

Output:

1
Community
  • 1
  • 1
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • 1
    Rev 2 `#define minimum...` is very interesting! It appear to evaluate args only once. Good if code was `printf("%d\n", minimum(a++, 30, 7, 1, 4));` – chux - Reinstate Monica Feb 11 '17 at 15:46
  • Thank you @chux, edited. And yes, `sizeof` [doesn't evaluate the args](http://stackoverflow.com/a/8225813/1606345) (except when it is a variable length array) – David Ranieri Feb 11 '17 at 18:24
  • 1
    ... and a _compound literal_ [cannot be a VLA](http://stackoverflow.com/a/14550583/2410359). Nice solution. (I think this solution should be listed before the ugly one.) – chux - Reinstate Monica Feb 11 '17 at 19:36
4

There is no built-in way to get the number of vararg arguments passed in C.

You need to do one of the following:

  • pass in a count explicitly,
  • pass in a count implicitly (as printf() does via the number of conversion specifiers)
  • or use a sentinel value (such as NULL or 0) to indicate the end of the vararg list

I have seen schemes that use macros and the __VA_ARGS__ identifier to automatically place a sentinel at the end of the varargs list when calling a function.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • If i were to employ the third option then the code would be like this: `assert ( NULL != va_arg( vaList, int) )` assuming `vaList` as our argument list? – Saad Feb 11 '17 at 08:10
  • 1
    @Saad: no. You wouldn't be using `assert`. As you pop each value off the list (`va_arg(args, type)`), you'd check to see whether the value popped is the sentinel value — with a regular `if` statement. – Jonathan Leffler Feb 11 '17 at 08:12
  • 1
    `NULL` is not a good choice. C does not specify its _type_ and `va_arg()` needs to know the type. A sentinel value needs to certainly have the same type as arguments. `foo_i(1,2,NULL); foo_sa("1","2",NULL); foo_sb("1","2",0);` are all problematic. `foo_sc("1","2",(char*)0);` and `foo_sd("1","2",(char*)NULL);` are OK – chux - Reinstate Monica Feb 11 '17 at 15:56