【问题标题】:In a C function declaration, what does "..." as the last parameter do?在 C 函数声明中,“...”作为最后一个参数有什么作用?
【发布时间】:2011-02-13 16:51:42
【问题描述】:

我经常看到这样声明的函数:

void Feeder(char *buff, ...)

“……”是什么意思?

【问题讨论】:

    标签: c syntax function parameters


    【解决方案1】:

    它允许可变数量的未指定类型的参数(就像printf 一样)。

    您必须使用va_startva_argva_end 访问它们

    更多信息请参见http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html

    【讨论】:

      【解决方案2】:

      Variadic functions

      可变参数函数是可以接受可变数量参数的函数 并用省略号代替最后一个参数声明。 这种函数的一个例子是printf

      一个典型的声明是

          int check(int a, double b, ...);
      

      可变参数函数必须至少有一个命名参数,例如,

          char *wrong(...);  
      

      在 C 中不允许。

      【讨论】:

        【解决方案3】:

        三个点“...”称为省略号。在函数中使用它们会使该函数成为 variadic 函数。 在函数声明中使用它们意味着函数将接受已定义的参数之后的任意数量的参数。

        例如:

        Feeder("abc");
        Feeder("abc", "def");
        

        都是有效的函数调用,但以下不是:

        Feeder();
        

        【讨论】:

          【解决方案4】:

          这意味着正在声明variadic function

          【讨论】:

            【解决方案5】:

            可变函数(多参数)

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

            【讨论】:

              【解决方案6】:

              ... 作为最后一个参数的函数称为可变参数函数 (Cppreference. 2016)。这个... 用于允许具有未指定类型的可变长度参数。

              当我们不确定参数的数量或其类型时,我们可以使用可变参数函数。

              可变参数函数示例: 假设我们需要一个 sum 函数来返回可变数量参数的总和。我们可以在这里使用可变参数函数。

              #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;
              }
              

              输出:

              练习题:练习变参函数的简单题可以在hackerrank practice problem here

              参考:

              【讨论】:

                【解决方案7】:
                • ... = 三点 = 三点 = 调用:ellipsis
                  • 表示:可变个参数
                    • 与普通功能相比:固定(命名的数量)参数
                • 带有...参数的函数被调用:Variadic function

                变量函数

                定义

                • 有效:
                  • int validFunctionWithNamedParameterThenEllipsis(int a, double b, ...);
                • 无效
                  • int invalidFunctionOnlyEllipsis(...);

                示例

                热门案例:

                int printf(const char *format, ...)
                

                呼叫:

                printf("year=%d, name=%s", 2021, "crifan");
                

                ->

                • format == "year=%d, name=%s"
                  • 命名参数
                • ... == 2021, "crifan"
                  • 可变数量的参数
                    • 这里总共2参数
                      • 第一:整数类型2021
                      • 秒:字符串类型"crifan"

                如何获取/计算可变参数函数的参数

                • 核心逻辑:使用va_list,与va_startva_argva_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);
                

                示例

                平均

                #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;
                }
                

                最大值

                #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));
                }
                

                执行

                #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);
                }
                

                我的案例: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;
                }
                

                一些注意事项

                va_start

                当调用va_start时,last_arg是最后一个命名的参数,而不是第一个

                发给:int open(const char *path, int oflag, ...);

                • 正确
                  • va_start(argList, oflag);
                • 错了
                  • va_start(argList, path);
                    • (XCode 的 clang) 编译器会警告
                      • Second argument to 'va_start' is not the last named parameter

                va_arg

                type va_arg(va_list ap, type);

                对于通过type,有一种特殊情况:

                传入时:

                • 字符
                • 无符号字符
                • 无符号短

                但返回总是:

                • 无符号整数

                -> 所以当代码:

                curPara = (mode_t) va_arg(argList, mode_t);

                根据:

                #include <sys/stat.h>
                #include <sys/types.h>
                

                ->mode_t==unsigned short

                相当于:

                curPara = (mode_t) va_arg(argList, unsigned short);

                所以编译器警告:

                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'

                改为:

                curPara = (mode_t) va_arg(argList, unsigned int);

                可以避免警告。

                相关文档

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2015-06-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-11-30
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多