How does the Linux c function open realize the variable parameter?

in learning Linux c programming, I encountered the open function int open (const char * pathname, int flag,.)
open function to perform different operations depending on whether the third parameter is passed.
now I want to write a function int va_fun (const char * str, int n,.), if only two parameters are passed in (va_fun (str,n)), returns 1, If a third parameter is passed in (va_fun (str,n,int_the_third)),) returns 2, but I don"t know how to implement it, I hope the kind-hearted person will give me a hint.

I have tried the following code:

-sharpinclude<stdio.h>
-sharpinclude<stdarg.h>

char *va_fun(const char *s,int n,...)
{
    va_list ap;
    int a;
    va_start(ap,n);
    if(a=va_arg(ap,int))
    {
        return "do it";
    }
    else
    {
        return "==";
    }
    va_end(ap);
}
void IntegerVarArgFunc(int i, ...){
    va_list pArgs;
    va_start(pArgs, i);
    int j = va_arg(pArgs, int);
    va_end(pArgs);
    printf("i=%d, j=%d\n", i, j);
}
int main()
{
    puts(va_fun("a",1));
    puts(va_fun("a",1,2));
    IntegerVarArgFunc(1);
    IntegerVarArgFunc(1,2);
    IntegerVarArgFunc(1,2,3);
    return 0;
}

but this doesn"t work. What"s the right thing to do?

(the output of the above code is:
do it
do it
iFirm1, jobmuri 157228448 / / j is a random value
iTun1, juni2
iTun1, jarg2)

Feb.07,2022

this is the implementation of variable parameters of C language functions. Detailed help information is available for
man va_arg.

it seems that you haven't figured out the variable parameter mechanism of c language, so I'll say a few more words.
to understand this, you need to know some preliminary knowledge points:

< H1 > C language function calling mechanism < / H1 >

there are five types of memory when the program process is running:

  • Code snippet: the machine instruction that stores the code
  • data segment: stores manually initialized global variables, static variables (length and content are determined at compile time)
  • BSS section: stores global and static variables that are not manually initialized in the program
  • Heap: memory for malloc/free and new/delete operations
  • stack: store function return address, function parameters, local variables, etc.
When the

function is called, the return address of the caller (the next instruction) is stacked first, then the arguments of the called function are stacked, and finally the local variables of the called function are stacked.
then gives control to the called function (jump to the entrance of the called function to start execution).
after the function is executed, first put the return value of the function into the register, and then bounce the local variable stack, the function argument stack, the function back to the address stack and jump back to that address to continue execution. The calling function is returned.

there are several common calling mechanisms:

    When
  • _ _ cdecl: is called, the function argument is stacked from right to left, and the caller is responsible for loading the stack when it returns. this is the default method adopted by linux, because the caller is responsible for pushing and loading the stack, so the variable parameter list can be passed.
  • When
  • _ _ stdcall: is called, the function argument is stacked from right to left; when returned, the callee is responsible for loading the stack. As you can see, it is impossible to pass variable parameters in this way of calling. because if you want to clean the stack when the function returns, you must know the type and number of parameters. In this way, the callee cannot know how many parameters are actually passed by the caller, so it cannot guarantee that the stack will be cleaned .
  • When
  • _ _ pascal: is called, the arguments of the function are stacked from left to right; when returned, the callee is responsible for loading the stack.
  • When
  • _ _ fastcall: is called, the first two arguments are passed not through the stack, but through the register (so fast)
  • _ _ thiscall: is designed to solve the problem of object-oriented passing this pointers, and others are similar to _ _ stdcall.
< H1 > va_arg variable parameter mechanism < / H1 >

the definition of a variable parameter function generally has the following form:

void foo(int fixed1, int fixed2, int last_fixed, ...) {
   va_list ap;
   <> *s;

   va_start(ap, last_fixed);
   while (<>)  {
       s = va_arg(ap, <>);
   }
   va_end(ap);
}

here, va_list, va_start, va_end, va_arg (and a va_copy) are actually macro definitions. Their functions are as follows:

  • va_list: this is actually defining an untyped pointer variable (void*). During processing, this pointer points to each variable parameter in turn.
  • va_start: initializes the pointer ap (va_list), which points ap to the address of the first variable parameter. According to the preliminary knowledge above, you can see that all the parameters are put into the stack in turn. So in the stack, the last fixed parameter is followed by a variable parameter. So the second parameter of this macro must be the last fixed parameter (last_fixed). Because it is a fixed parameter, its type is also clear, and the amount of memory consumed is also clear. As long as you skip sizeof (last_fixed), you will naturally locate the first variable parameter. Note: the last fixed parameter cannot be declared as a register variable (register), because the register variable is not on the stack and has nothing to do with the address of the variable parameter, so it cannot be located.
  • va_end: has no special meaning, just to pair with va_start. Because there is a "{" in the macro of va_start, there must be a matching "}" after it. Otherwise, the syntax is wrong.
  • va_arg: has two functions: one is to fetch the current variable parameter, and the other is to jump the ap pointer to the next variable parameter. To achieve this, you must know the type of the current variable parameter. So the second parameter of this macro must specify its type.

there are two issues that must be coordinated between the caller and the callee:

< H2 > question 1: how many variable parameters are there in total? < / H2 >

there are generally two ways to solve this problem:

one way is, like printf, to use fixed parameters to indicate that there are several subsequent variable parameters, for example:

char name[] = "ahua";
int id = 777;
printf("hello %s, your id is %d\n", name, id);

you can see that in the string, the placeholder (%?) corresponds to the variable parameter one-to-one, so the number is clear.

another way is to indicate the end of a variable parameter through a pre-agreed special value, for example:

sum('total', 1, 43, 22, 55, 31, -1);

A special value (- 1) is passed here to indicate that there are no variable parameters behind.

< H2 > question 2: what is the type of each variable parameter? < / H2 >

there are also two ways to solve this problem:
one way is still like printf, which specifies the type of each variable parameter through fixed variables (for example:% s is a string,% d is int,% ld is long int,% lld is long long int, and so on). Note: the variable parameter for% c is still int, instead of char, because the compiler optimizes the code by default and aligns the memory address.

another way is to agree that all variable parameters are of the same type. Such as the sum in the above example.

< H1 > routine < / H1 >

simply write the above two examples:

-sharpinclude <stdio.h>
-sharpinclude <stdarg.h>

int myprint(const char *fmt, ...) {
    va_list ap;
    char *place_holder = (char*)fmt;
    int a;
    char *b;
    double c;
    va_start(ap, fmt);
    while (*place_holder != 0x00) {
        int i = place_holder - fmt;
        switch (*place_holder) {
            case 'i':
                a = va_arg(ap, int);
                printf("arg%d = %d\n", i, a);
                break;
            case 's':
                b = va_arg(ap, char*);
                printf("arg%d = %s\n", i, b);
                break;
            case 'f':
                c = va_arg(ap, double);
                printf("arg%d = %f\n", i, c);
                break;
        }
        place_holder PP;
    }
    va_end(ap);
    return 0;
}

int mysum(const char* label, ...){
    va_list ap;
    int sum = 0;
    int val = 0;
    va_start(ap, label);
    while (1) {
        val = va_arg(ap, int);
        if (val < 0) {
            break;
        }
        sum += val;
    }
    printf("sum of '%s' is %d.\n", label, sum);
    return 0;
}

int main() {
    int x = 666;
    char y[] = "hello";
    double z = 3.14159;
    myprint("i", x);
    myprint("isf", x, y, z);

    int a = 1, b = 2, c = 3, d = 4;
    mysum("a,b,c,d", a, b, c, d, -1);
    return 0;
}

the running result is as follows:

arg0 = 666
arg0 = 666
arg1 = hello
arg2 = 3.141590
sum of 'a,b,c,d' is 10.
Menu