How can I pass opetional arguments to C functions?


hi,

Is it possible to pass various amount of parameters to a C function (not to main(), but the other ones I define) to then read in that function and define what's optional and what's not?

Can read it with something like argc/argv like in main()?

I of cource tried, but without success and am not sure whether I do the things right. My guess is that this is not possible due the fact the pointers/types would get lost. Is that correct or am I missing it?

Thanks,
Maxim Maletsky
LVL 1
s-maximAsked:
Who is Participating?
 
gj62Connect With a Mentor Commented:
Some more info:

A partial parameter list can be terminated by the ellipsis notation, a comma followed by three periods (, ...), to indicate that there may be more arguments passed to the function, but no more information is given about them. Type checking is not performed on such arguments. At least one parameter must precede the ellipsis notation and the ellipsis notation must be the last token in the parameter list. Without the ellipsis notation, the behavior of a function is undefined if it receives parameters in addition to those declared in the parameter list.

To call a function with a variable number of arguments, simply specify any number of arguments in the function call. An example is the printf function from the C run-time library. The function call must include one argument for each type name declared in the parameter list or the list of argument types.

Here's some more info from Microsoft:

The following example shows how the va_start, va_arg, and va_end macros, along with the va_list type (declared in STDARG.H), work together:

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

//  Declaration, but not definition, of ShowVar.
int ShowVar( char *szTypes, ... );

void main()
{
    ShowVar( "fcsi", 32.4f, 'a', "Test string", 4 );
}
//  ShowVar takes a format string of the form
//   "ifcs", where each character specifies the
//   type of the argument in that position.
//
//  i = int
//  f = float
//  c = char
//  s = string (char *)
//
//  Following the format specification is a list
//   of n arguments, where n == strlen( szTypes ).
void ShowVar( char *szTypes, ... )
{
    va_list vl;
    int i;

    //  szTypes is the last argument specified; all
    //   others must be accessed using the variable-
    //   argument macros.
    va_start( vl, szTypes );

    // Step through the list.
    for( i = 0; szTypes[i] != '\0'; ++i )
    {
        union Printable_t
        {
            int     i;
            float   f;
            char    c;
            char   *s;
        } Printable;

        switch( szTypes[i] )    // Type to expect.
        {
        case 'i':
            Printable.i = va_arg( vl, int );
            printf( "%i\n", Printable.i );
            break;

        case 'f':
            Printable.f = va_arg( vl, float );
            printf( "%f\n", Printable.f );
            break;

        case 'c':
            Printable.c = va_arg( vl, char );
            printf( "%c\n", Printable.c );
            break;

        case 's':
            Printable.s = va_arg( vl, char * );
            printf( "%s\n", Printable.s );
            break;

        default:
            break;
        }
    }
    va_end( vl );
}

The preceding example illustrates these important concepts:

A list marker must be established as a variable of type va_list before any variable arguments are accessed. In the preceding example, the marker is called vl.


The individual arguments are accessed using the va_arg macro. The va_arg macro needs to be told the type of argument to retrieve so it can transfer the correct number of bytes from the stack. If an incorrect type of a size different than that supplied by the calling program is specified to va_arg, the results are unpredictable.


The result obtained using the va_arg macro should be explicitly cast to the desired type.


The va_end macro must be called to terminate variable-argument processing.


0
 
gj62Commented:
Sure, look at printf, for example.

You use the ellipses - sample code below:

#include <stdio.h>
#include <stdarg.h> /* va_list, va_arg, va_end     */

int set(char *item, int num, ...);     /* Declare the function. Notice the ellipses ...        */

/************************************************************************/

main()
{
    char *item="pear";

    int   Ret;
     
    Ret = set (item,4, "apple", "pear", "banana", "grape");
     
    if (Ret)
    {
        printf ("%s found\n", item);
    }
    else
    {
        printf("%s not found\n", item);
    }
}

/************************************************************************/

int set(char *item, int num, ...)
{
    va_list ap;      /* define 'ap' It acts as a                     * pointer to the undefined
           * variables.               */
    int Ret=0;
    int Inc=0;   /* Assume the worst.            */
    va_start(ap, num);      /* seed 'ap'               */

    do
    {
        if ( item == va_arg(ap, char *))
        {
            Ret = 1;
        }
    } while ( Ret==0 && ++Inc < num);

    va_end(ap);          /* tidy up.               */
    return (Ret);
}


0
 
ssnkumarCommented:
Do you want your function to work like main()!? Which can take 1 or 2 or 3 or no arguments? Or is it variable arguements as gj62 has explained?
I think the Q is not clear to me! Please clarify...

-Narendra
0
Live webcast with Pinal Dave

Pinal Dave will teach you tricks to help identify the real root cause of database problems rather than red herrings. Attendees will learn scripts that they can use in their environment to immediately figure out their performance Blame Shifters and fix them quickly.

 
akshayxxCommented:
>>>>Can read it with something like argc/argv like in main()?

argc and argv in the main is not example of variable number of arguments.
its only two arguments ..( in some obfuscated codes i have seen more)
 one is number of tokens and another is array of tokens .. token= character string tokens
every token from argv is just a string .. u cannot tell if its integer or double or what .. u have to pass another token for any such identification..

 if va_arg concept, as gj62 proposed above, sounds tough to you . ( though i would recommend va_arg concept).. u can also do something like what main does.. pass two variables one is lenght of tokens .. and another is array of tokens .. and then start parsing tokens..

there can be another solution also .. like this one
typedef enum VARTYPE{INT,CHAR,DOUBLE};//u can add more
typedef struct generic_type{
VARTYPE typ;
void * data;
}
 
then u can pass array of this struct ..to the function. and in function u can take ur variables out, one by one..

big headache in this would be filling up the arguments in the array , before passing to the function .. which inthe case of va_arg .. is done automatically.

.while in both cases u have to do some amount of effort while retrieving the values..

dont know why i m telling this alternative method.. .go ahead with stdarg   etc.
0
 
cupCommented:
Note there are two variable arg include files: vararg.h and stdarg.h.  Do not mix them.  You may see vararg.h in older books - that is before ANSI C came out.  The ANSI std uses stdarg.h
0
 
KrypCommented:
>        if ( item == va_arg(ap, char *))
This is a very poor way of doing a string comparison.
It only 'works' because your implementation has removed duplicate string constants, so when you get to "pear"=="pear", you're comparing identical addresses.
0
 
s-maximAuthor Commented:

I like what `gj62' writes about the strarg usage. I actually have it within my applciation from some third-party library, but again - wasn't sure how exactly to use it. Thanks for explaining it with a great example.

Maxim Maletsky
maxim@php.net
0
 
s-maximAuthor Commented:

great and clear example. Thanks!
0
All Courses

From novice to tech pro — start learning today.