In a C function declaration, what does "..." as the last parameter do?
Often I see a function declared like this:
void Feeder(开发者_运维问答char *buff, ...)
what does "..." mean?
it allows a variable number of arguments of unspecified type (like printf
does).
you have to access them with va_start
, va_arg
and va_end
see http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html for more information
Variadic functions
Variadic functions are functions which may take a variable number of arguments and are declared with an ellipsis in place of the last parameter. An example of such a function is
printf
.A typical declaration is
int check(int a, double b, ...);
Variadic functions must have at least one named parameter, so, for instance,
char *wrong(...);
is not allowed in C.
variadic function (multiple parameters)
wiki
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double tot = 0;
va_start(ap, count); //Requires the last fixed parameter (to get the address)
for(j=0; j<count; j++)
tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
va_end(ap);
return tot/count;
}
The three dots '...' are called an ellipsis. Using them in a function makes that function a variadic function. To use them in a function declaration means that the function will accept an arbitrary number of parameters after the ones already defined.
For example:
Feeder("abc");
Feeder("abc", "def");
are all valid function calls, however the following wouldn't be:
Feeder();
It means that a variadic function is being declared.
Functions with ...
as last parameter are called Variadic functions (Cppreference. 2016). This ...
is used to allow variable length parameters with unspecified types.
We can use variadic functions when we are not sure about the number of parameters or their types.
Example of variadic function: Let us assume we need a sum function that will return the summation of variable number of arguments. We can use a variadic function here.
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...)
{
int total, i, temp;
total = 0;
va_list args;
va_start(args, count);
for(i=0; i<count; i++)
{
temp = va_arg(args, int);
total += temp;
}
va_end(args);
return total;
}
int main()
{
int numbers[3] = {5, 10, 15};
// Get summation of all variables of the array
int sum_of_numbers = sum(3, numbers[0], numbers[1], numbers[2]);
printf("Sum of the array %d\n", sum_of_numbers);
// Get summation of last two numbers of the array
int partial_sum_of_numbers = sum(2, numbers[1], numbers[2]);
printf("Sum of the last two numbers of the array %d\n", partial_sum_of_numbers);
return 0;
}
Output:
Practice problem: A simple problem to practice variadic function can be found in hackerrank practice problem here
Reference:
- Cppreference. (2016, February 13). Variadic functions. Retrieved July 25, 2018, from https://en.cppreference.com/w/c/variadic
...
= three dot = three point = called:ellipsis
- means: variable number of parameters
- compared to normal function: fixed (number of named) parameter
- means: variable number of parameters
- function with
...
para is called: Variadic function
Variadic function
Definition
- valid:
int validFunctionWithNamedParameterThenEllipsis(int a, double b, ...);
- invalid
int invalidFunctionOnlyEllipsis(...);
Example
popular case:
int printf(const char *format, ...)
call:
printf("year=%d, name=%s", 2021, "crifan");
->
format
=="year=%d, name=%s"
- named parameter
...
==2021, "crifan"
- variable number of parameter
- here total 2 parameter
- first: integer type
2021
- second: string type
"crifan"
- first: integer type
- here total 2 parameter
- variable number of parameter
How to get/calculate paremeter for Variadic function
- core logic: use
va_list
, withva_start
,va_arg
,va_end
Related definition
#include <stdarg.h>
void va_start(va_list ap, last_arg);
type va_arg(va_list ap, type);
void va_end(va list ap);
Example
average
#include <stdarg.h>
#include <stdio.h>
double average(int count, ...) {
va_list ap;
int j;
double sum = 0;
va_start(ap, count); /* Requires the last fixed parameter (to get the address) */
for (j = 0; j < count; j++) {
sum += va_arg(ap, int); /* Increments ap to the next argument. */
}
va_end(ap);
return sum / count;
}
int main(int argc, char const *argv[]) {
printf("%f\n", average(3, 1, 2, 3));
return 0;
}
maxof
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
int maxof(int, ...) ;
void f(void);
main(){
f();
exit(EXIT_SUCCESS);
}
int maxof(int n_args, ...){
register int i;
int max, a;
va_list ap;
va_start(ap, n_args);
max = va_arg(ap, int);
for(i = 2; i <= n_args; i++) {
if((a = va_arg(ap, int)) > max)
max = a;
}
va_end(ap);
return max;
}
void f(void) {
int i = 5;
int j[256];
j[42] = 24;
printf("%d\n",maxof(3, i, j[42], 0));
}
execl
#include <stdarg.h>
#define MAXARGS 31
/*
* execl is called by
* execl(file, arg1, arg2, ..., (char *)(0));
*/
int execl(const char *file, const char *args, ...)
{
va_list ap;
char *array[MAXARGS +1];
int argno = 0;
va_start(ap, args);
while (args != 0 && argno < MAXARGS)
{
array[argno++] = args;
args = va_arg(ap, const char *);
}
array[argno] = (char *) 0;
va_end(ap);
return execv(file, array);
}
My case: hook syscall()
/*==============================================================================
Hook: syscall()
==============================================================================*/
int syscall(int, ...);
// normally max number of syscall parameter is not exceed 8
// refer: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/kern/syscalls.master
int MaxSupportArgNum_syscall = 16;
%hookf(int, syscall, int number, ...){
os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: number=%d", number);
// Setting up some variables to get all the parameters from syscall
void *paraPtr, *paraList[MaxSupportArgNum_syscall];
// char *paraPtr, *paraList[MaxSupportArgNum_syscall];
va_list argList;
int curParaNum = 0;
va_start(argList, number);
while ((paraPtr = (void *) va_arg(argList, void *))) {
// while ((paraPtr = (char *) va_arg(argList, char *))) {
paraList[curParaNum] = paraPtr;
curParaNum += 1;
os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: [%d] paraPtr=%p", curParaNum, paraPtr);
}
va_end(argList);
// os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: argList=%{public}s", argList);
os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: curParaNum=%d", curParaNum);
bool isStat = (SYS_stat == number);
bool isStat64 = (SYS_stat64 == number);
if (isStat || isStat64){
char* curPath = (char *)paraList[0];
os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: isStat=%{bool}d, isStat64=%{BOOL}d, curPath=%{public}s", isStat, isStat64, curPath);
bool isJbPath = isJailbreakPath(curPath);
if (isJbPath){
os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: IS jailbreak path: %{public}s", curPath);
return OPEN_FAILED;
} else {
os_log(OS_LOG_DEFAULT, "hook_syscall_stat_stat64: NOT jailbreak path: %{public}s", curPath);
}
}
// return %orig;
// return %orig(number, ...);
// int retValue = %orig();
// int retValue = callOriginSyscall(number, curParaNum, paraList);
//// int retValue = callOriginSyscall(number, curParaNum, (void *)paraList);
// os_log(OS_LOG_DEFAULT, "hook_syscall_stat_file: retValue=%d", retValue);
// return retValue;
int paraNum = curParaNum;
int syscallRetValue = -1;
if (0 == paraNum){
syscallRetValue = %orig(number);
} else if (1 == paraNum){
void* para1 = paraList[0];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p", para1);
syscallRetValue = %orig(number, para1);
} else if (2 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p", para1, para2);
syscallRetValue = %orig(number, para1, para2);
} else if (3 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p", para1, para2, para3);
syscallRetValue = %orig(number, para1, para2, para3);
} else if (4 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
void* para4 = paraList[3];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p,para4=%p", para1, para2, para3, para4);
syscallRetValue = %orig(number, para1, para2, para3, para4);
} else if (5 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
void* para4 = paraList[3];
void* para5 = paraList[4];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p,para4=%p,para5=%p", para1, para2, para3, para4, para5);
syscallRetValue = %orig(number, para1, para2, para3, para4, para5);
} else if (6 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
void* para4 = paraList[3];
void* para5 = paraList[4];
void* para6 = paraList[5];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p", para1, para2, para3, para4, para5, para6);
syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6);
} else if (7 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
void* para4 = paraList[3];
void* para5 = paraList[4];
void* para6 = paraList[5];
void* para7 = paraList[6];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p,para7=%p", para1, para2, para3, para4, para5, para6, para7);
syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6, para7);
} else if (8 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
void* para4 = paraList[3];
void* para5 = paraList[4];
void* para6 = paraList[5];
void* para7 = paraList[6];
void* para8 = paraList[7];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p,para7=%p,para8=%p", para1, para2, para3, para4, para5, para6, para7, para8);
syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6, para7, para8);
} else if (9 == paraNum){
void* para1 = paraList[0];
void* para2 = paraList[1];
void* para3 = paraList[2];
void* para4 = paraList[3];
void* para5 = paraList[4];
void* para6 = paraList[5];
void* para7 = paraList[6];
void* para8 = paraList[7];
void* para9 = paraList[8];
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p,para7=%p,para8=%p,para9=%p", para1, para2, para3, para4, para5, para6, para7, para8, para9);
syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6, para7, para8, para9);
}
os_log(OS_LOG_DEFAULT, "hook_syscall_orig: syscallRetValue=%d", syscallRetValue);
return syscallRetValue;
}
Some Note
va_start
when call va_start
, last_arg
is the last named parameter, NOT the first one
for: int open(const char *path, int oflag, ...);
- correct
va_start(argList, oflag);
- wrong
va_start(argList, path);
- (XCode's clang) compiler will warning
Second argument to 'va_start' is not the last named parameter
- (XCode's clang) compiler will warning
va_arg
type va_arg(va_list ap, type);
for pass type
, there is a special case:
when pass in :
- char
- unsigned char
- unsigned short
but return always is:
- unsigned int
-> so when code:
curPara = (mode_t) va_arg(argList, mode_t);
according:
#include <sys/stat.h>
#include <sys/types.h>
-> mode_t
==unsigned short
equivalent to:
curPara = (mode_t) va_arg(argList, unsigned short);
so compiler warning:
Second argument to 'va_arg' is of promotable type 'mode_t' (aka 'unsigned short'); this va_arg has undefined behavior because arguments will be promoted to 'int'
change to:
curPara = (mode_t) va_arg(argList, unsigned int);
could avoid warning.
Related Doc
- The C Book — Variable numbers of arguments (gbdirect.co.uk)
- Variadic function - Wikipedia
- <stdarg.h> (opengroup.org)
- stdarg.h - Wikipedia
- C Library - <stdarg.h> (tutorialspoint.com)
精彩评论