"Warm reminder: The following content is from the Netizen's selfless dedication or book excerpt, in this express thanks!" 】
In C programming we encounter functions with variable numbers of parameters, such as the printf () function, which is defined as:
int printf (const char* format, ...);
In addition to having a parameter format fixed, the number and type of arguments followed are mutable, for example, we can have the following different invocation methods:
printf ("%d", I);
printf ("%s", s);
printf ("The number is%d, string is:%s", I, s);
How exactly does the C function of the variable parameter and the function compiler of these mutable parameters implement? This article carries on some discussion to this question, hoped can have some help to everybody. Limited to my level, if there is inappropriate in the text, please correct me.
(i) write a simple variable-parameter C function
Let's explore how to write a simple variable-parameter C function. The C function that writes the variable parameter is used in the program to the following macros:
void Va_start (Va_list arg_ptr, Prev_param);
Type Va_arg (va_list arg_ptr, type);
void Va_end (Va_list arg_ptr);
VA here is the meaning of the variable-argument (variable parameter). These macros are defined in Stdarg.h, so a program that uses mutable parameters should contain this header file. Here we write a simple variable parameter function, change the function to have at least one integer argument, the second argument is an integer (optional), the function just prints the values of these two parameters.
void Simple_va_fun (int i, ...)
{
va_list arg_ptr;
int & nbsp;j=0;
Va_start (arg_ PTR, i);
J = Va_arg (arg_ptr , int);
Va_end (arg_ptr);
printf ("%d%d/n", I, j);
return;
We can declare our function in our header file like this:
extern void Simple_va_fun (int i, ...);
We can call this in the program:
Simple_va_fun (100);
Simple_va_fun (100,200);
As you can see from the implementation of this function, we should have the following steps to use mutable parameters:
1) First define a variable of type va_list in the function, here is ARG_PTR, this variable is a pointer to the parameter;
2) Then use the Va_start macro to initialize the variable arg_ptr, the second parameter of the macro is the first variable parameter of the previous parameter, is a fixed parameter;
3) Then return the variable parameter with Va_arg, and assign the value to the integer j, the second parameter of Va_arg is the type of the parameter you want to return, here is the int type;
4) Finally, the Va_end macro is used to end the variable parameter, and then you can use the second parameter in the function. If the function has more than one mutable parameter, call Va_arg in turn to get each parameter.
If we call it in the following three ways, it is legal, but the result is different:
1)
Simple_va_fun (100); The result is: 100-123456789 (variable value)
2)
Simple_va_fun (100,200); The result:
3)
Simple_va_fun (100,200,300); The result :
We see that the first call has an error, the second is correct, and the third call, although the result is correct, conflicts with the original design of our function. In the following section we explore the causes of these results and how mutable parameters are handled in the compiler.
typedef char * VA_LIST;
#define _INTSIZEOF (N)/
(sizeof (n) +sizeof (int)-1) &~ (sizeof (int)-1))
#define VA_START (AP,V) (AP = (va_list) &v + _intsizeof (v))
#define VA_ARG (AP,T)/
(* (* (t *) (AP + = _intsizeof (t))-_intsizeof (t)))
#define VA_END (AP) (AP = (va_list) 0)
The definition of _intsizeof (n) is primarily for some systems that require memory alignment. The C-language function is pressed from right to left onto the stack, and figure (1) is the position of the function's parameters in the stack. We see that va_list is defined as char*, and some platforms or operating systems are defined as void*. Then look at the definition of va_start, defined as &v+ _intsizeof (v), while &v is the address of the fixed parameter on the stack, so after we run Va_start (AP, v), the AP points to the address of the first mutable parameter on the stack:
High Address |-----------------------------|
| function return address |
|-----------------------------|
|....... |
|-----------------------------|
| nth parameter (first variable parameter) |
|-----------------------------|<--va_start after AP point
| n-1 parameters (last fixed parameter) |
Low Address |-----------------------------|<--&v
figure (1)
then we use VA_ARG () to obtain the variable parameter value of type T, the example above is the int type, we take a look at the return value of the va_arg int type:
J= (* (int*) ((AP + = _intsizeof (int))-_intsizeof (int)));
First ap+=sizeof (int), which already points to the address of the next parameter. It then returns the int* pointer to ap-sizeof (int), which is the address of the first mutable parameter in the stack (Figure 2). The contents of this address (parameter values) are then assigned to J.
High Address |-----------------------------|
| function return address |
|-----------------------------|
|....... |
|-----------------------------|<--va_arg after AP point
| nth parameter (first variable parameter) |
|-----------------------------|<--va_start after AP point
| n-1 parameters (last fixed parameter) |
Low Address |-----------------------------|<--&v
figure (2)
the last thing to say is the meaning of the Va_end macro, the x86 platform is defined as ap= (char*) 0; The AP no longer points to the stack, but is the same as null. Some are directly defined as ((void*) 0) so that the compiler does not generate code for Va_end, such as the one defined by GCC on the Linux x86 platform. Here are a few things to note: Because the address of the parameter is used for the Va_start macro, the parameter cannot be declared as a register variable or as a function or an array type. The description of Va_start,va_arg,va_end is these, we should note that different operating systems and hardware platform definitions are somewhat different, but the principle is similar.
(c) Issues to be noted in programming of variable parameters
because Va_start, Va_arg, va_end and so on define macro, so it seems very stupid, the type and number of variable parameter is completely controlled by program code in this function, it is not able to intelligently recognize the number and type of different parameters. One would ask, is it true that the smart identification parameter is not implemented in printf? That's because the function printf parses the type of the parameter from the fixed parameter format string, and then calls Va_arg to get the variable argument. That is, if you want to implement intelligent identification of variable parameters, you have to make judgments in your own program. Another problem is that the compiler is not strict enough on the prototype of the Variadic function, which is bad for the programming error. If Simple_va_fun () is changed to:
void Simple_va_fun (int i, ...)
{
va_list arg_ptr;
char *s = NULL;
Va_start (arg_ptr, i);
s = va_arg (arg_ptr, char*);
va_end (arg_ptr);
printf ("%d%s/n", I, s);
return;
}
The variable parameter is type char*, when we forget to call the function with two parameters, we will get the error of Core dump (Unix) or the Illegal page (window platform). But it is possible not to make mistakes, but mistakes are hard to find and not conducive to the writing of high-quality programs.
The following is a reference to the compatibility of VA series macros. System V UNIX defines va_start as a macro with only one parameter:
Va_start (va_list arg_ptr);
and ANSI C is defined as:
Va_start (va_list arg_ptr, Prev_param);
If we want to use System V definition, should use the VARARG.H header file defined macro, ANSI C macro and System V macro is incompatible, we generally use ANSI C, so the definition of ANSI C is enough, but also easy to transplant the program.
Summary:
The function principle of variable parameters is very simple, and the VA series is defined by the macro definition, and the implementation is related to the stack. When we write the C function of a mutable function, there are pros and cons, so we don't need to use variable parameters on unnecessary occasions. If in C + +, we should take advantage of C + + polymorphism to realize the function of variable parameters, as far as possible to avoid the C language approach to achieve.
How to use variable parameters in C language