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
source to share
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.
source to share
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.
source to share
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.
source to share
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.
source to share
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
.
source to share
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 initializesva_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;
}
source to share