C - Role of the first argument in a variable number of arguments

In this code, the first argument value is 1, which means the total number of arguments passed.

In the called function, I can access all the arguments.

Then what's the point of using the first argument?

Am I not supposed to restrict access to multiple arguments.

int main(){
  fun1(1, "Apple", "Boys", "Cats", "Dogs");
  return 0;
}

void fun1(int num, ...){
   char *str;
   va_list ptr;
   va_start(ptr, num);
   str = va_arg(ptr, char *);
   str = va_arg(ptr, char *);
   printf("%s ", str);
}

      

Output: Boys

+3


source to share


7 replies


The role of the first argument is usually to determine the number and sometimes the type (for example:) of the printf()

variational arguments.

On x86 architecture, if the calling convention cdecl is used, this first argument is the topmost argument on the stack (the argument with the lowest address, i.e. the last argument pushed onto the stack). Therefore, you always know its address:



-------------------------------
|          1st Arg            |
-------------------------------
|        return address       | 4 bytes
-------------------------------
| saved old stack frame (EBP) | 4 bytes
------------------------------- <-- EBP

      

EBP

+ 8 contains the address of the first argument.

+6


source


There are two points of view to answer this question.

1. A run-time perspective

va_start()

and va_arg()

need to know where to find the arguments. Usually the compiler will compute fixed memory locations for all arguments. This is not possible at compile time with a variadic function. The functions are stdarg

provided by the runtime and know how the arguments are found on the stack, but they need some reference. Therefore, at compile time, you always need to know one location: the location of the last nonvariant argument. va_start()

and va_args()

can calculate the locations of the following arguments.

This is most likely a practical reason that C requires at least one nonvariant argument in the standard John Bolinger is referring to, thanks for the comment.



2.From a programmer's point of view

If you try to read more arguments than were passed, you are invoking undefined behavior because you are reading from memory locations where no arguments were actually found. So somehow you need to know when to stop. One easy way to achieve this is to simply pass the number of next arguments, as in your snippet. A well-known variational function printf()

, it knows the number of arguments when parsing a format string.

So, to address your board: there is no automatic limit, it's up to you how you let your function know how many arguments to read, and your responsibility is only to read the valid arguments.

+6


source


fun1

variadic . This is the meaning of the three points ...

. There must be some way to tell the function how long its argument list is. Ideally, it would also be possible to tell the function what the types of its arguments are. The format string printf

is an example of a schema for this.

Don't use variable functions, pass an array. With variadic functions, you have almost no compiler support for detecting when you passed the wrong number or type of arguments.

+3


source


If not for the first argument, you don't know when to stop parsing other arguments.

+2


source


To answer your closest question, the last explicit argument (in your case the first and only argument) is needed on some platforms to determine where the argument list begins in memory. Without it, he va_start

will not always be able to determine where to point. At least one of your explicit arguments should also be used to determine the number of variables.

+2


source


There is no way fun1

to know how many arguments were actually passed in the function call; there is no metadata on the stack (or in registers) that says "Yes, this chunk of memory matches the argument passed from the caller."

Try calling fun1

like fun1(1);

(or any other integer value) and see what happens.

Variadic functions must trust their caller to provide them with the information they need in the fixed argument list in order to read any remaining variables correctly. If you called fun1

like fun1(0)

, it would still try to find two objects char *

on the stack or registers and try to print the second, which would most likely result in a runtime error.

fun1

should look at the argument num

and not try to read more arguments than that. You must trust the caller fun1

to pass the correct number and types of arguments that match num

.

+2


source


num

c void fun1(int num, ...){

serves 2 purposes:

  • The argument before ...

    , int num

    in this case, is used to indicate where the variable number of arguments,,, ...

    begins and initializes va_list

    to begin access. This one is listed on C.

    #include <stdarg.h>
    void fun1(int num, ...){
      va_list ap;
      va_start(ap, num);
      char *s = va_arg(ap, char *);
    
          

  • Usually, the arguments before ...

    indicate in some way the number and types of the next arguments. It is not listed in C. Some typical implementations include:

    • The argument before ...

      is a count of the next arguments. This seems to be close to the OP's intended approach.

    • The format is a gap containing various information about the type and number of arguments. printf("%s %d\n", "Age", 25);

    • Sentinel: The arguments before ...

      do not indicate the addition count, but the last one passed the special:sum(1,2,3,INT_MIN);

    • Use a global variable to specify the number of arguments after x

      . This coding is rarely good practice.

I would expect the OP's code to have used the first argument to indicate the number of next arguments.

void fun1(int num, ...){
   va_list ptr;
   va_start(ptr, num);

   for (int i = 0; i<num; i++) {
     char *str = va_arg(ptr, char *);
     printf("%s ", str);
   }

   va_end(ptr);
}

int main(){
  fun1(4, "Apple", "Boys", "Cats", "Dogs");
  return 0;
}

      

+1


source







All Articles