4

I've just read: "C void arguments" about the differences between these function definitions in C:

int f(void)

and

int f()

Understanding that the second form means a function returning integer that takes any number of parameters, I wondered how can we actually access and use these unknown parameters?

I'd love to get sample code and explanation.

Also, I know the mechanism of the Varargs in C (with va_arg, va_end, va_start functions) and would be glad to hear about the differences between this mechanism and the f() form mentioned above.

Thank you a lot!

Community
  • 1
  • 1
Reflection
  • 1,718
  • 2
  • 17
  • 33

4 Answers4

7

The second version does NOT accept a variable number of parameters, it accepts a fixed (but unspecified) sequence of parameters. In effect, it declares the function, but does not prototype the function. So calls to the function will not be checked for valid types. It's common for the compiler to look at the first call to the function and check other calls against the types found in the first call.

This is why, if you omit #include <stdio.h>, the first call to printf will be acceptable, but any calls to printf with different types will generate an error. Eg.

int main() {
    printf("hello");        //<--- accepted, compiler may assume `int printf(char *);`
    printf("%s", "world");  //<--- error, type mismatch
}

To have a function accept a variable number, it must have at least one fixed parameter, and then the token ....

int f (int first, ...);

You will need to include the stdarg.h header file. And the function can access the parameters using macros.

void f (int c,...){
    va_list ap;
    va_start(ap, c);
    for(;c--;)
        printf("%d", va_arg(ap,int));
    va_end (ap);
}

It's convient to have the fixed argument be a count of the remaining arguments. You'll also need to determine the type of each argument somehow. In this example, they are assumed to all be int.

luser droog
  • 17,793
  • 3
  • 49
  • 96
4
 int f(void);

declares f as a function taking no arguments and returning an int result, whereas

 int f();

declares f as a function taking a fixed but unspecified number and type of arguments (and, of course, returning an int result).

Given such a declaration, there has to be a definition somewhere that actually defines the parameters. If it takes no parameters, it might be defined as:

int f() {
    return 42;
};

If it takes 2 int parameters, it might be defined as:

int f(int x, int y) {
    return x + y;
}

Either definition is compatible with int f();, but only the first is compatible with int f(void).

A function declaration that specifies the type(s) of the parameters, or, as a special case, uses the void keyword to specify that there are no parameters, is a prototype. A function declaration that doesn't do this, such as int f(); is an old-style declaration.

Unless you're stuck using a decades-old pre-ANSI compiler that doesn't support prototypes, or you're maintaining very old code and don't have the time or other resources to update it, there is no reason not to use prototypes. If you call a function with no visible prototype, you still have to pass the correct number and type(s) of arguments; the difference is that the compiler will be unable to tell you if the call is incorrect.

(You can define a function that takes a variable number and type of arguments; printf is an example of this. This is done using the , ... notation. The function itself uses features defined in <stdarg.h> to process the arguments. But that has nothing to do with the declarations shown in your question.)

Here's a small program using obsolescent old-style function declarations and definitions:

#include <stdio.h>
#include <stdlib.h>

int add();

int main(argc, argv)
int argc;
char **argv;
{
    if (argc == 3) {
        /* NOTE: atoi() does no error checking */
        printf("sum = %d\n", add(atoi(argv[1]), atoi(argv[2])));
    }
    else {
        fprintf(stderr, "Usage: %s x y\n", argv[0]);
        exit(EXIT_FAILURE);
    }
}

int add(x, y)
int x, y;
{
    return x + y;
}

Note that if I had written add(1, 2, 3, 4) or add("foo", "bar"), the compiler wouldn't have caught the error; the program just would have misbehaved.

Here's an equivalent program using modern function declarations and definitions:

#include <stdio.h>
#include <stdlib.h>

int add(int x, int y);

int main(int argc, char **argv) {
    if (argc == 3) {
        /* NOTE: atoi() does no error checking */
        printf("sum = %d\n", add(atoi(argv[1]), atoi(argv[2])));
    }
    else {
        fprintf(stderr, "Usage: %s x y\n", argv[0]);
        exit(EXIT_FAILURE);
    }
}

int add(int x, int y) {
    return x + y;
}

The visible prototype means that the compiler is able to diagnose incorrect calls.

Keith Thompson
  • 230,326
  • 38
  • 368
  • 578
  • I'm curious though, how would the compiler use the right calling convention if it doesn't know the parameter types of the function. – newacct Sep 13 '13 at 20:48
  • 1
    @newacct: **It doesn't**. Given a call with no visible prototype, the compiler *assumes* that the promoted arguments match the defined parameter types. If that assumption is invalid, then the call has undefined behavior. That's why you should always use prototypes. – Keith Thompson Sep 13 '13 at 20:51
1

As to the f() form: click.

An example, a printf-like function that can print decimal numbers or strings into a file descriptor:

#include <stdarg.h>
/* Formatted printing into a file descriptor */                          
int printfd(int fd, char *fmt, ...)                                      
{                                                                        
    int bytes = 0;                                                   
    int i_val = 0;                                                   
    va_list va;                                                      
    va_start(va, fmt);                                               
    while (*fmt) {                                                   
            char *perc = strchr(fmt, '%');                           
            int len = perc == NULL ? strlen(fmt) : perc - fmt;       
            if (len) {                                               
                    bytes += write(fd, fmt, len);                    
                    fmt += len;                                      
            } else {                                                 
                    fmt = perc + 1;                                  
                    if (*fmt == 0)                                   
                            continue;                                
                    else if (*fmt == '%')                            
                            bytes += write(fd, fmt, 1);              
                    else if (*fmt == 'd')                            
                            bytes += writedec(fd, va_arg(va, int));  
                    else if (*fmt == 's')                            
                            bytes += writestr(fd, va_arg(va, char*));
                    fmt++;                                           
            }                                                        
    }                                                                
    va_end(va);                                                      
    return bytes;                                                    
}                                                                        
Community
  • 1
  • 1
SzG
  • 11,409
  • 4
  • 23
  • 37
  • 3
    No, that does not answers the question! – Basile Starynkevitch Sep 13 '13 at 18:11
  • @BasileStarynkevitch - Au Contraire. The title of this post suggest asker wants to know how to ***Use the parameters of a function that takes any number of parameters, in C***. Perhaps the follow on questions confuse that original stated intent, but nonetheless this post answers it well. +1 to SzG. – ryyker Sep 13 '13 at 18:18
1

int f(void) means a function returning int that takes no parameters.

You can see this example to see the consequence of using int f()

#include <stdio.h>

void f();

int main() {
  f(); /* prints some garbage */
  f(16);  /* prints 16 */ 
  return 0;
}

void f(a1)
  int a1;
{
   printf("%d\n", a1);
}

And also this:-

#include <stdio.h>
    #include <stdlib.h>
    int f();
    int f(int x) {
        return x;
    }
    int main (int argc, char *argv[]) {
         printf ("%d\n", f(atoi(argv[1])));
         return 0;
    }

pax> gcc -Wall --std=c99 -o qq qq.c
pax> ./qq 42
42
Rahul Tripathi
  • 152,732
  • 28
  • 233
  • 299