Was macht “…” als letzter Parameter in einer C-Funktionsdeklaration?

Lesezeit: 9 Minuten

Benutzeravatar von alaamh
alaah

Oft sehe ich eine Funktion, die so deklariert ist:

void Feeder(char *buff, ...)

Was bedeutet?

es erlaubt eine variable Anzahl von Argumenten unspezifizierten Typs (wie z printf tut).

Sie müssen mit darauf zugreifen va_start, va_arg und va_end

sehen http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html für mehr Informationen

Benutzeravatar von N 1.1
N 1.1

Variadische Funktionen

Variadische Funktionen sind Funktionen, die eine variable Anzahl von Argumenten annehmen können und mit Auslassungspunkten anstelle des letzten Parameters deklariert werden. Ein Beispiel für eine solche Funktion ist printf.

Eine typische Deklaration ist

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

Variadische Funktionen müssen mindestens einen benannten Parameter haben, also zum Beispiel

    char *wrong(...);  

ist in C nicht erlaubt.

Benutzeravatar von RvdK
RvdK

variadische Funktion (mehrere Parameter)

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

Benutzeravatar von dafmetal
dafmetal

Die drei Punkte „…“ werden Ellipsen genannt. Wenn Sie sie in einer Funktion verwenden, wird diese Funktion a variadisch Funktion. Sie in einer Funktionsdeklaration zu verwenden bedeutet, dass die Funktion eine beliebige Anzahl von Parametern nach den bereits definierten akzeptiert.

Zum Beispiel:

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

sind alles gültige Funktionsaufrufe, das Folgende jedoch nicht:

Feeder();

Es bedeutet, dass a variadische Funktion wird deklariert.

Benutzeravatar von arsho
arsch

Funktioniert mit ... als letzter Parameter werden variadische Funktionen genannt (vgl. 2016). Dies ... wird verwendet, um Parameter variabler Länge mit nicht spezifizierten Typen zuzulassen.

Wir können variadische Funktionen verwenden, wenn wir uns über die Anzahl der Parameter oder ihre Typen nicht sicher sind.

Beispiel für eine variadische Funktion:
Nehmen wir an, wir brauchen eine Summenfunktion, die die Summe der variablen Anzahl von Argumenten zurückgibt. Wir können hier eine variadische Funktion verwenden.

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

Ausgabe:

C-Ausgang

Übungsproblem: Ein einfaches Problem zum Üben der variadischen Funktion finden Sie in Hackerrank-Übungsproblem hier

Bezug:

crifans Benutzeravatar
Krifan

  • ... = drei Punkte = drei Punkte = aufgerufen: ellipsis
    • meint: Variable Anzahl Parameter
      • im Vergleich zur normalen Funktion: Fest (Anzahl der benannten) Parameter
  • Funktion mit ... para heißt: Variadische Funktion

Variadische Funktion

Definition

  • gültig:
    • int validFunctionWithNamedParameterThenEllipsis(int a, double b, ...);
  • ungültig
    • int invalidFunctionOnlyEllipsis(...);

Beispiel

beliebter Fall:

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

Anruf:

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

->

  • format == "year=%d, name=%s"
    • benannter Parameter
  • ... == 2021, "crifan"
    • variable Parameteranzahl
      • hier total 2 Parameter
        • zuerst: ganzzahliger Typ 2021
        • Zweitens: Zeichenfolgentyp "crifan"

So erhalten / berechnen Sie Parameter für die Variadic-Funktion

  • Kernlogik: verwenden va_listmit va_start, va_arg, va_end

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

Beispiel

Durchschnitt

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

exkl

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

Mein Fall: 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;
}

Einige Anmerkung

va_start

beim Anruf va_start, last_arg ist der zuletzt genannte Parameter, NICHT der erste

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

  • Korrekt
    • va_start(argList, oflag);
  • falsch
    • va_start(argList, path);
      • (XCode’s Clang) Compiler warnt
        • Second argument to 'va_start' is not the last named parameter

va_arg

type va_arg(va_list ap, type);

für Pass typegibt es einen Sonderfall:

beim Passieren:

  • verkohlen
  • unsigned char
  • unsigned kurz

aber zurück ist immer:

  • unsigned int

-> also beim Code:

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

gemäß:

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

-> mode_t==unsigned short

gleichwertig:

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

Also Compiler-Warnung:

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'

ändern:

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

Warnung vermeiden konnte.

Zugehöriges Dokument

1413530cookie-checkWas macht “…” als letzter Parameter in einer C-Funktionsdeklaration?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy