- Overview
Function Overloading is not available in C, which makes it difficult to solve the problem of variable number function parameters. Even if C ++ is used, function Overloading is difficult if the number of parameters is uncertain. in this case, some users use pointer parameters to solve the problem. the following section discusses how to handle the number of indefinite parameters in the C language.
- Definition
Let's take a look at several macros.
In the include file of VC ++ 6.0, there is an stdarg. h header file, which has the following macro definitions:
# DEFINE _ intsizeof (N) (sizeof (n) + sizeof (INT)-1 )&~ (Sizeof (INT)-1 ))
# Define va_start (AP, V) (AP = (va_list) & V + _ intsizeof (V) // The first optional parameter address
# Define va_arg (AP, t) (* (T *) (AP + = _ intsizeof (t)-_ intsizeof (t) // The next parameter address
# Define va_end (AP) (AP = (va_list) 0) // set the pointer to invalid
If you do not understand the above macro definitions, you can skip this step and read the subsequent content.
- Parameter distribution in the stack, location
In the process, stack addresses are allocated from high to low. when a function is executed, the parameter list is pushed into the stack, the high address of the stack, the return address of the function, and the Execution Code of the function, during the stack import process, the stack address is constantly decreasing. Some hackers modify the function return address in the stack and execute their own code to execute their own inserted code segments.
In short, the distribution of functions in the stack is: address from high to low, in turn: function parameter list, function return address, function Execution Code segment.
In the stack, the distribution of each function is in reverse order. that is, the highest part of the last parameter in the list, and the first parameter in the lowest part of the list address. the parameter distribution in the stack is as follows:
Last Parameter
Second to last parameter
...
First Parameter
Function return address
Function Code segment
- Sample Code
Void arg_test (int I ,...);
Int main (INT argc, char * argv [])
{
Int int_size = _ intsizeof (INT );
Printf ("int_size = % d/N", int_size );
Arg_test (0, 4 );
Arg_cnt (4, 1, 2, 3, 4 );
Return 0;
}
Void arg_test (int I ,...)
{
Int J = 0;
Va_list arg_ptr;
Va_start (arg_ptr, I );
Printf ("& I = % P/N", & I); // print the address of parameter I in the stack
Printf ("arg_ptr = % P/N", arg_ptr );
// Print the arg_ptr address after va_start,
// It should be higher than the address of parameter I in sizeof (INT) bytes
// Arg_ptr points to the address of the next parameter.
J = * (int *) arg_ptr );
Printf ("% d/N", I, j );
J = va_arg (arg_ptr, INT );
Printf ("arg_ptr = % P/N", arg_ptr );
// Print the address of arg_ptr after va_arg
// It should be higher than the sizeof (INT) byte before calling va_arg
// Arg_ptr points to the address of the next parameter.
Va_end (arg_ptr );
Printf ("% d/N", I, j );
}
- Code Description:
Int int_size = _ intsizeof (INT); obtains the number of bytes occupied by the int type.
Va_start (arg_ptr, I); obtain the first variable parameter address,According to the definition (va_list) & V, add _ intsizeof (V), which is the address of the next parameter, that is, the address of the first variable parameter.
J = va_arg (arg_ptr, INT); get the value of the first parameter, and move the arg_ptr pointer up to a _ intsizeof (INT), that is, point to the address of the next variable parameter.
Va_end (arg_ptr); leave arg_ptr empty, that is, arg_ptr = 0;
Summary: The process of reading variable parameters is actually the process of reading the parameter content one by one from the low address to the high address by using pointers to traverse the parameter list in the stack segment.
- Issues and Solutions during compilation
Although you can read all the variable parameters by traversing the parameter list in the stack, since you do not know how many variable parameters are there, when should I end the traversal? If there are too many traversal parameters in the stack, it is very likely to read some invalid data.
Solution:. you can specify the number of parameters in the first starting parameter, then you can read all variable parameters in the loop; B. define an end mark. When calling a function, pass this mark in the last parameter. In this way, when traversing a variable parameter, you can end the traversal of the Variable Parameter Based on this mark;
The following is an example code:
// The first parameter defines the number of optional parameters, which is used to obtain the initial parameter content cyclically.
Void arg_cnt (int cnt ,...);
Int main (INT argc, char * argv [])
{
Int int_size = _ intsizeof (INT );
Printf ("int_size = % d/N", int_size );
Arg_cnt (4, 1, 2, 3, 4 );
Return 0;
}
Void arg_cnt (int cnt ,...)
{
Int value = 0;
Int I = 0;
Int arg_cnt = CNT;
Va_list arg_ptr;
Va_start (arg_ptr, CNT );
For (I = 0; I <CNT; I ++)
{
Value = va_arg (arg_ptr, INT );
Printf ("Value % d = % d/N", I + 1, value );
}
}Although the number of read parameters can be solved according to the above two methods, what should we do if the parameter types are not fixed? If we do not know the parameter type, there is no way to process the read parameters. solution: You can customize some possible parameter types. In this way, in the variable parameter list, you can change the type in the variable parameter list, and then read the variable parameter value according to the type, and perform accurate conversion. when passing parameters, you can pass the following parameters: Variable Parameter number, variable parameter Type 1, variable parameter value 1, variable parameter type 2, variable parameter value 2 ,....
Here is a complete example:
# Include <stdio. h>
# Include <stdarg. h>
Const int int_type = 100000;
Const int str_type = 100001;
Const int char_type = 100002;
Const int maid = 100003;
Const int float_type = 100004;
Const int double_type = 100005;
// The first parameter defines the number of optional parameters, which is used to obtain the initial parameter content cyclically.
// Variable parameters are transmitted in the form of arg_type, arg_value... to process different variable parameter types
Void arg_type (int cnt ,...);
// The first parameter defines the number of optional parameters, which is used to obtain the initial parameter content cyclically.
Void arg_cnt (int cnt ,...);
// Test the usage of va_start and va_arg and the address distribution of function parameters in the stack.
Void arg_test (int I ,...);
Int main (INT argc, char * argv [])
{
Int int_size = _ intsizeof (INT );
Printf ("int_size = % d/N", int_size );
Arg_test (0, 4 );
Arg_cnt (4, 1, 2, 3, 4 );
Arg_type (2, int_type, 222, str_type, "OK, hello World! ");
Return 0;
}
Void arg_test (int I ,...)
{
Int J = 0;
Va_list arg_ptr;
Va_start (arg_ptr, I );
Printf ("& I = % P/N", & I); // print the address of parameter I in the stack
Printf ("arg_ptr = % P/N", arg_ptr );
// Print the arg_ptr address after va_start,
// It should be higher than the address of parameter I in sizeof (INT) bytes
// Arg_ptr points to the address of the next parameter.
J = * (int *) arg_ptr );
Printf ("% d/N", I, j );
J = va_arg (arg_ptr, INT );
Printf ("arg_ptr = % P/N", arg_ptr );
// Print the address of arg_ptr after va_arg
// It should be higher than the sizeof (INT) byte before calling va_arg
// Arg_ptr points to the address of the next parameter.
Va_end (arg_ptr );
Printf ("% d/N", I, j );
}
Void arg_cnt (int cnt ,...)
{
Int value = 0;
Int I = 0;
Int arg_cnt = CNT;
Va_list arg_ptr;
Va_start (arg_ptr, CNT );
For (I = 0; I <CNT; I ++)
{
Value = va_arg (arg_ptr, INT );
Printf ("Value % d = % d/N", I + 1, value );
}
}
Void arg_type (int cnt ,...)
{
Int arg_type = 0;
Int int_value = 0;
Int I = 0;
Int arg_cnt = CNT;
Char * str_value = NULL;
Va_list arg_ptr;
Va_start (arg_ptr, CNT );
For (I = 0; I <CNT; I ++)
{
Arg_type = va_arg (arg_ptr, INT );
Switch (arg_type)
{
Case int_type:
Int_value = va_arg (arg_ptr, INT );
Printf ("Value % d = % d/N", I + 1, int_value );
Break;
Case str_type:
Str_value = va_arg (arg_ptr, char *);
Printf ("Value % d = % d/N", I + 1, str_value );
Break;
Default:
Break;
}
}
}
The above is my personal opinion. I hope you will correct and express your opinion on this. Thank you very much !!!