In a C function declaration, what does "..." as the last parameter do?

CSyntaxFunctionParameters

C Problem Overview


Often I see a function declared like this:

void Feeder(char *buff, ...)

what does "..." mean?

C Solutions


Solution 1 - C

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

Solution 2 - C

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.

Solution 3 - C

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();

Solution 4 - 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;
}

Solution 5 - C

Solution 6 - C

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:

C output

Practice problem: A simple problem to practice variadic function can be found in hackerrank practice problem here

Reference:

Solution 7 - C

  • ... = three dot = three point = called: ellipsis
    • means: variable number of parameters
      • compared to normal function: fixed (number of named) parameter
  • 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"

How to get/calculate paremeter for Variadic function

  • core logic: use va_list, with va_start, va_arg, va_end
#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
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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionalaamhView Question on Stackoverflow
Solution 1 - CknittlView Answer on Stackoverflow
Solution 2 - CN 1.1View Answer on Stackoverflow
Solution 3 - CdafmetalView Answer on Stackoverflow
Solution 4 - CRvdKView Answer on Stackoverflow
Solution 5 - CIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 6 - CarshoView Answer on Stackoverflow
Solution 7 - CcrifanView Answer on Stackoverflow