Be sure to note that C does not support default parameters
C + + supports a function definition of the number of variable parameters, which is related to the order of the stack in the case of the function parameter Call of C + +, first referencing a paragraph of other netizens to describe the function call, and the parameter into the stack:
Start------------Reference------------
C supports variable-parameter functions, which means that the C support function has a variable number of arguments, and the most common example is the printf () series function that we are very familiar with. We also know that when a function is called, the argument is from right to left stack. If the general form of a variable parameter function is:
F (P1, p2, p3, ...)
Then the order of the parameters into the stack (and the stack) is:
...
Push P3
Push P2
Push P1
Call F
Pop P1
Pop P2
Pop p3
...
I can conclude that if you support a function with variable parameters, the order of the parameters in the stack will almost certainly be right-to-left. Also, the parameter stack cannot be done by the function itself, but by the caller.
The latter part of this conclusion is not difficult to understand, because the function itself does not know how many arguments the caller passed in, but the caller knows, so the caller should be responsible for putting all the arguments out of the stack.
In the general form of a variable parameter function, the left side is an already determined parameter, and the right ellipsis represents the unknown parameter portion. For an already determined parameter, its position on the stack must also be determined. Otherwise, it means that the parameters that have been determined cannot be positioned and found, so that the function is not guaranteed to execute correctly. The position of the measure parameter on the stack is how far away from the exact function call point (called f). The parameters that have been determined, their position on the stack, should not depend on the exact number of parameters because the number of parameters is unknown!
So, the choice can only be, has been determined by the parameters, leaving the function call point has a definite distance (nearer). To satisfy this condition, only the parameter into the stack obeys the Right-to-left rule. That is, the left to determine the parameters of the stack, from the function call point has a certain distance (the leftmost parameter last into the stack, from the function call point closest).
This way, when the function starts executing, it can find all the parameters that have been determined. According to the function's own logic, it is responsible for finding and interpreting the later variable parameters (where it is farther away from the call point), usually depending on the values of the parameters that have been determined (typical format explanations such as the Prinf () function, which unfortunately are vulnerable).
It is said that the parameters in Pascal are from left to right to press the stack, as opposed to C. For Pascal, which supports only fixed-parameter functions, it has no problem with variable parameters. Therefore, it is possible to choose which parameters to feed into the stack.
Even the argument stack is done by the function itself, not by the caller, because the type and number of parameters of the function are fully known. This is more efficient than using C, because it takes less code (in C, where the function generates a parameter out of the stack code each time it is called).
C + + for compatibility with C, so still support functions with variable parameters. But a better choice in C + + is often a function overload.
End of------------reference------------
As described above, we look at the definitions of functions such as printf () and sprintf () to verify this:
_crtimp int __cdecl printf (const char *, ...);
_crtimp int __cdecl sprintf (char *, const char *, ...);
When both functions are defined, the __CDECL keyword is used, and the rules of the __CDECL keyword contract function call are:
The caller is responsible for clearing the call stack, which passes through the stack, and the stack order is from right to left.
Next, let's take a look at how printf () uses the variable number parameter, and here's an excerpt from the MSDN example,
Code that refers only to the ANSI System Compatibility section, code for UNIX systems refer directly to MSDN.
------------Example Code------------
Copy Code code as follows:
#include <stdio.h>
#include <stdarg.h>
int average (int-I, ...);
void Main (void)
{
printf ("Average is:%d/n", Average (2, 3, 4,-1));
}
int average (int-I, ...)
{
int count = 0, sum = 0, i = A;
Va_list marker;
Va_start (marker, a); /* Initialize variable arguments. */
while (I!=-1)
{
sum + = i;
count++;
i = va_arg (marker, int);
}
Va_end (marker); /* Reset variable arguments. */
Return (sum?) (Sum/count): 0);
}
The example code function is to calculate the average, which allows the user to enter multiple integer parameters, requiring that the latter parameter must be-1, indicating that the input of the parameter is complete, and then returning the average calculation result.
The logic is simple, first defining
Va_list marker;
Represents a list of parameters, and then calls Va_start () to initialize the argument list. Note that the Va_start () call not only uses the marker
This argument list variable, which also uses the first parameter, shows that the initialization of the argument list is related to the initial determination parameter given by the function, which is critical, and subsequent analysis will see why.
After calling Va_start () initialization, you can call the Va_arg () function to access the arguments in each argument list. Note Va_arg ()
The second parameter specifies the type of the return value (int).
When the program determines that all parameter accesses are complete, call the Va_end () function to end the argument list access.
In this way, it is easy to access variable number parameters, that is, to use Va_list,va_start (), Va_arg (), Va_end ()
Such a type with three functions. But for the function variable number parameter mechanism, the feeling is still confused. It seems that there needs to be a deeper inquiry into the exact answer.
Find the definition of Va_list,va_start (), Va_arg (), Va_end () in the .../vc98/include/stdarg.h file.
. h The code below (only extracts the ANSI compatible part of the Code, UNIX and other systems implemented slightly different, interested friends can study for themselves):
Copy Code code as follows:
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)
As you can see from the code, Va_list is just a type escape, which is actually defined as a pointer to a char* type, so that the memory is accessed in bytes.
The other three functions are actually just three macro definitions, wait a minute, let's look at the macro definition _intsizeof in the middle:
#define _INTSIZEOF (N) (sizeof (n) + sizeof (int)-1) & ~ (sizeof (int)-1))
The function of this macro is to calculate the length (size) of the byte aligned to the integer byte length for a given variable or type N. int occupies 4 bytes in 32-bit systems and 16-bit systems account for 2 bytes.
An expression
(sizeof (n) + sizeof (int)-1)
The effect is that if sizeof (n) is less than sizeof (int), then the calculated
The result value of the sizeof (n) is a bit higher than the value on the binary.
such as: sizeof (short) + sizeof (n)-1 = 5
The binary of 5 is 0x00000101,sizeof (short) is 0x00000010, so the binary value of 5 is greater than the binary value of 2
One higher to the left.
An expression
~ (sizeof (int)-1)
Produces a mask (mask) in order to shed the "fraction" portion of the previous computed value.
As in the example above, ~ (sizeof (int)-1) = 0x00000011 (thank Glietboys for reminding, this should be 0xffffff00)
With the 5 binary 0x00000101 do "and" operations to get the 0x00000100, that is, 4, and direct computing sizeof (short) should be 2.
By using an expression such as _intsizeof (short), you can get other types of byte lengths aligned according to the integer byte length.
The byte length of type int is aligned because the pointer variable in C + + is actually an integer value, the length is the same as the int, and the pointer's offset is required for the subsequent three macros to perform the operation.
About the content of byte alignment in programming please be interested friends to refer to other articles online, no longer repeat here.
Continue, the following three macro definitions:
First:
#define VA_START (AP,V) (AP = (va_list) &v + _intsizeof (v))
This is used in programming
Va_list marker;
Va_start (marker, a);
You can see that the function of the Va_start macro is to get the given argument list pointer (marker) to offset the corresponding position backwards according to the pointer length of the type of the first one that determines the parameter, and the previous _intsizeof (n) macro is used to compute the offset.
Second:
#define VA_ARG (Ap,t) (* (t *) (AP + _intsizeof (t))-_intsizeof (t))
At first glance this is a bit confusing, (AP + + _intsizeof (t))-_intsizeof (t) expression of a plus minus, for the return value is not effective Ah, that is, the return value is the value of AP, what reason?
The return value of this calculation is on the one hand, on the other hand, keep in mind that Va_start (), Va_arg (), va_end the invocation of these three macros is relevant, and the AP variable is the argument list pointer given when calling Va_start (), so
(AP + _intsizeof (t))-_intsizeof (t)
The expression is not just to return the address of the parameter currently pointing to, or to have the AP refer to the next argument (note that the AP jumps down one parameter, calculated according to the _intsizeof length of the type T).
Third:
#define VA_END (AP) (AP = (va_list) 0)
This is well understood, but the AP pointer is set to NULL, which counts as the end of the parameter read.
So far, the mechanism of the variable number function parameters is very clear. Finally, there are a few things to note:
In the process of reading a parameter with the va_arg () sequential jump pointer, there is no way to determine whether the next pointer is a valid address, and there is no place to know exactly how many parameters to read, which is the danger of this variable number parameter. In the previous example of averaging, it is required that the input must provide a special value (-1) at the end of the argument list to indicate the end of the argument list, so it can be assumed that if the caller does not follow this rule, it will cause the pointer to access the bounds.
Well, a friend might ask, the printf () function does not provide such a special value for identification.
Don't worry, printf () uses a different number of parameters to identify the way, may be more covert. Notice that his first definite parameter, which is the format string we use as formatting control, has a parameter descriptor such as "%d", "%s", and the printf () function, when parsing the format string, can determine the number of parameters that need to be read after the parameter descriptor. We might as well do the following experiments:
printf ("%d,%d,%d,%d/n", 1,2,3,4,5);
The actual supplied argument is more than the previous given parameter descriptor, so the result of this execution is
1,2,3,4
That is, printf () thinks that there are only 4 parameters behind the format string, and the rest is out of line. Then do one more experiment:
printf ("%d,%d,%d,%d/n", 1,2,3);
The actual supplied parameter is less than the given parameter descriptor, so the result of the execution is (if there is no exception)
1,2,3,2367460
In this place, the results of each person's execution may be different because the pointer to the last parameter is already pointing to an illegal address. This is also where special attention is needed for functions such as printf ().
Summarize:
There are more places to pay attention to when using variable number of function parameters. I personally suggest avoiding the use of this model. For example, the previous calculation average, rather than using an array or other list as a parameter to pass a series of values to the function, do not have to write such a perverted function. On the one hand, it is easy to see the pointer access across the border, on the other hand, in the actual function call, all the calculated values in sequence as parameters in the code, very nasty.
That said, there are some places where this function is useful, such as the formatted synthesis of strings, like the printf () function; In practice, I often use a writelog () function written by myself to record file logs, defined as printf (), It is very flexible and convenient to use, such as:
Writelog ("User%s, login count%d", "Guanzhong", 10);
What's written in the file is
User Guanzhong, login count 10
The use of programming language, in accordance with the basic rules of the premise, is the beholder, benevolent. In short, after a thorough understanding, choose a good habit of their own can be