A Variable Parameter Function is also called a va function, such as printf, scanf, and exec.
1. Example:
// Fun: print the value of the parameter after N
Void fun (int n ,...);
Int main ()
{
Int Part1 = 128;
Int Part 2 = 256;
Int Part3 = 512;
Fun (Part1, Part2, Part3 );
Return 0;
}
Void fun (INT Part1 ,...)
{
Int * P = & Part1; // get the address of Part1
Printf ("% d \ n", * ++ P); // print the value of Part2
Printf ("% d \ n", * ++ P); // print the value of Part3
}
C's default function call specification is _ cdecl, that is, all parameters are pushed to the stack from right to left. The strict fun statement should be:
Void _ cdecl fun (int n ,...);
In Main, before calling the fun function, add the parameters to the stack. The order of the parameters in the stack is as follows:
Push Part3
Push Part2
Push Part1
Then call fun to execute the function body code.
The parameters of the stack are placed at the high address. Because the stack is grown from the high address to the low address, the order of Part1, Part2, and Part3 in the memory will be:
0xfe6c Part3
0xfe70 Part2
0xfe74 Part1
In this way, the following parameters can be accessed by obtaining the address & Part1 and ++ of the first parameter, but this must be the _ cdecl function call specification, for example, the library functions of the printf series (sprintf, fprintf) are all functions that can accept variable parameters. Assume that the following statement is provided:
Printf ("% d \ n", m, n, k );
We can see that we can get the number (number of format operators) and type (for example, % d) of parameters through the first parameter ), this is why printf ("% d \ n", m, n, k) can be successfully executed, while printf ("% d \ n", m, n) the cause of the failure. If the number of passed parameters is greater than the number of format characters, you can ignore them (the parameters can be executed normally in Linux). If the number is smaller than the number of format characters, access is out of bounds (a warning is reported in Linux, but the parameters are still executed normally, out-of-bounds parameters are random values ). (When executing your own program, remember to add./, for example,./A. Out)
2. Use the varargs macro to compile functions that support variable parameter lists. In the asci c standard, these macros are included in stdarg. h.
For example, the following code:
# Include <stdio. h>
# Include <stdarg. h>
Void _ cdecl fun (int n,...); // do not add _ cdecl
Int main (INT argc, char * argv [])
{
Int Part1 = 128;
Int Part 2 = 256;
Int Part3 = 512;
Fun (Part1, Part2, Part3 );
Return 0;
}
Void fun (int n ,...)
{
Va_list AP;
Va_start (AP, N );
Printf ("% d \ n", va_arg (AP, INT ));
Printf ("% d \ n", va_arg (AP, INT ));
Va_end (AP );
}
In the implementation provided by Microsoft for VC, we can see the following definition:
# DEFINE _ addressof (V) (& (v ))
# DEFINE _ intsizeof (N) (sizeof (n) + sizeof (INT)-1 )&~ (Sizeof (INT)-1 ))
Typedef char * va_list;
# Define va_start (AP, V) (AP = (va_list) _ addressof (v) + _ intsizeof (V) // AP points to the address of the next parameter in V
# Define va_arg (AP, t) (* (T *) (AP + = _ intsizeof (t)-_ intsizeof (t ))) // AP points to the address of the next parameter, but returns the value of this parameter. //
# Define va_end (AP) (AP = (va_list) 0)
Va_list a char pointer, each with a single byte addressing.
Va_start calculates the parameter type size through _ intsizeof and allows the AP to obtain the address of the parameter object after v.
Va_arg AP points to the next parameter object of the AP in the parameter list, and returns the T-type parameter object pointed to before the AP.
_ Intsizeof (n) converts the length of N to an integer multiple of the int length. If n is of the char type, sizeof (n) = 1 is converted to an integer multiple of the int length. Assume that sizeof (N) = 4 m + K (M> = 0, K =,), sizeof (n) + 4-1 = 4 m + (K + 3 ),~ (Sizeof (INT)-1) = ~ (4-1) = ~ (100000011b) = 11111100b, so that any number &~ (Sizeof (INT)-1), the last two digits must be 0, which is a multiple of 4, (4 m + (K + 3 ))&~ (Sizeof (INT)-1) can store 4 m + K.
Rewrite these macros to the fun function:
Void fun (INT Part1 ,...)
{
Int Part2, Part3;
Char * AP; // va_list AP;
AP = (char *) & Part1 + 4; // va_start (AP, Part1), AP points to Part2
Part2 = * (int *) (AP + = 4-4); // return Part2, AP points to Part3, Part2 = va_arg (AP, INT)
Part3 = * (int *) (AP + = 4-4); // return Part2, AP points to Part3, Part3 = va_arg (AP, INT)
AP = (char *) 0; // AP pointing to null, var_end (AP)
}
If Part2 is Char, Shor is automatically converted to int type, and float is automatically converted to double type.
# Include <stdio. h>
# Include <stdarg. h>
Void fun (int n ,...);
Int main (INT argc, char * argv [])
{
Int Part1 = 128;
Float part2= 256.0; // float
Float Part3 = 512.0;
Fun (Part1, Part2, Part3 );
Return 0;
}
Void fun (int n ,...)
{
Va_list AP;
Va_start (AP, N );
Printf ("% F \ n", va_arg (AP, double); // double is correct, float is incorrect, and is automatically stored as double
Printf ("% F \ n", va_arg (AP, double ));
Va_end (AP );
}
# Include <stdio. h>
# Include <stdarg. h>
Void fun (int n ,...);
Int main (INT argc, char * argv [])
{
Int Part1 = 128;
Char Part2 = 'C'; // char
Short Part3 = 8; // short
Fun (Part1, Part2, Part3 );
Return 0;
}
Void fun (int n ,...)
{
Va_list AP;
Va_start (AP, N );
Printf ("% C \ n", va_arg (AP, INT); // char is automatically stored as int and cannot use Char. Even so, % C output characters are still available.
Printf ("% d \ n", va_arg (AP, INT); // The short is automatically stored as int and cannot be used.
Va_end (AP );
}
These problems are caused by memory alignment.
3. vprintf, vfprintf, vsprintf, vsnprintf, and vasprintf format output. There is a va_list parameter.
# Include <stdio. h>
# Include <stdarg. h>
Int vprintf (const char * format, va_list AP); // format the output to the standard output, corresponding to printf
Int vfprintf (File * stream, const char * format, va_list AP); // format the output to the file stream, corresponding to fprintf
Int vsprintf (char * s, const char * format, va_list AP); // format the output string, corresponding to sprintf
Int vsnprintf (char * s, size_t N, const char * format, va_list AP); // format the output to a string with a fixed length, corresponding to snprintf
Int vasprintf (char ** ret, const char * format, va_list AP); // corresponds to asprintf
For example, use vprintf to Implement error:
# Include <stdio. h>
# Include <stdarg. h>
Void error (char * function_name, char * format ,...)
{
Va_list AP;
Va_start (AP, format );
/* Print out name of function causing Error */
(Void) fprintf (stderr, "err in % s:", function_name );
/* Print out remainder of Message */
(Void) vfprintf (stderr, format, AP );
Va_end (AP );
(Void) Abort ();
}