A variable parameter indicates that the number of parameters can be changed. It can be more or less, or the type of the parameter can also be changed. It can be int, double, char *, class, struct, and so on. Variable parameters are the key to implementing functions such as printf () and sprintf (). You can also use variable parameters to sum up any amount of data, it is convenient to calculate the average value (otherwise, an array or each write overload will be used ). There is a special keyword parame in C #, but there is no similar syntax in C and C ++. Fortunately, this article will focus on how to use these functions.
Step 1 Variable Parameter Representation
Use three points... View the declaration of the printf () and scanf () functions:
Int printf (const char *,...);
Int scanf (const char *,...);
These three vertices are used in Macros as Variadic Macros. The default name is _ VA_ARGS __. For example:
# Define WriteLine (...) {printf (_ VA_ARGS _); putchar ('\ n ');}
Then WriteLine ("MoreWindows ");
The returned value of printf () indicates the number of bytes of the output. Change the macro above:
# Define WriteLine (...) printf (_ VA_ARGS _) + (putchar ('\ n ')! = EOF? 1: 0 );
In this way, the return value of the WriteLine macro is obtained. It returns the number of bytes of the output, including the final '\ n '. As shown in the following example, both I and j will output 12.
Int I = WriteLine ("MoreWindows ");
WriteLine ("% d", I );
Int j = printf ("% s \ n", "MoreWindows ");
WriteLine ("% d", j );
Step 2: How to handle the va_list type
The function uses va_list and three macros related to variable parameters to process variable parameters. This is the key to variable parameters.
You can find the va_list definition in <stdarg. h>:
Typedef char * va_list;
Next we will introduce three macros closely related to it: va_start (), va_end (), and va_arg ().
You can also find the definitions of these three Macros in <stdarg. h>:
# Define va_start (ap, v) (ap = (va_list) & v + _ INTSIZEOF (v ))
# Define va_end (ap) (ap = (va_list) 0)
# Define va_arg (ap, t) (* (t *) (ap + = _ INTSIZEOF (t)-_ INTSIZEOF (t )))
The _ INTSIZEOF macro is defined as follows:
# Define _ INTSIZEOF (n) (sizeof (n) + sizeof (int)-1 )&~ (Sizeof (int)-1 ))
To analyze the four macros:
Va_end (ap) is the simplest, that is, to set the pointer to NULL.
In va_start (ap, v), ap = (va_list) & v + _ INTSIZEOF (v) first obtains the v address and adds _ INTSIZEOF (v ). _ INTSIZEOF (v) is a little complicated. (Sizeof (n) + sizeof (int)-1 )&~ (Sizeof (int)-1) All operations are bit operations, which seem a little troublesome. Otherwise, it is very simple, that is, to get an integer to sizeof (int ). For example, if sizeof (int) is 4, 1, 2, 3, 4, take 4, 5, 6, 7, and 8. The arithmetic expression of C language is (x + N-1)/n) * n, when n is a power of 2, you can replace the last two steps with bitwise operations-Clear the minimum n-1 binary digits to 0.
Va_arg (ap, t) is to extract t-type data from the ap and move the pointer back. For example, va_arg (ap, int) indicates that an int data is taken and the pointer is shifted to four bytes.
Therefore, in the function, use va_start () to obtain the starting address of the variable parameter, use va_arg () to take values one by one, and then use va_end () to resolve the variable parameter.
Step 3: vfprintf () function and vsprintf () function
The vfprintf () function is very important. From the perspective of its name, we can see that it is highly correlated with the commonly used printf () function. It has multiple overloaded versions. Here is the most common one:
Function prototype
Int vfprintf (
FILE * stream,
Const char * format,
Va_list argptr
);
The first parameter is a FILE pointer. The FILE structure is essential for reading and writing files in C language. Stdout must be input to the screen output.
The second parameter specifies the output format.
The third parameter is of the va_list type. This is rare, but it is actually a char * that indicates the starting address of the variable parameter.
Returned value: the number of bytes (excluding the last '\ 0') returned for success, and-1 returned for failure.
Vsprintf () is similar to the above function, and only lists the function prototype:
Int vsprintf (
Char * buffer,
Const char * format,
Va_list argptr
);
There is also an int _ vscprintf (const char * format, va_list argptr); it can be used to calculate the size of the buffer string in the vsprintf () function.
Sample Code
The following describes the printf () and WriteLine () functions implemented by myself.
Int Printf (char * pszFormat ,...)
{
Va_list pArgList;
Va_start (pArgList, pszFormat );
Int nByteWrite = vfprintf (stdout, pszFormat, pArgList );
Va_end (pArgList );
Return nByteWrite;
}
Int WriteLine (char * pszFormat ,...)
{
Va_list pArgList;
Va_start (pArgList, pszFormat );
Int nByteWrite = vfprintf (stdout, pszFormat, pArgList );
If (nByteWrite! =-1)
Putchar ('\ n'); // Note 2
Va_end (pArgList );
Return (nByteWrite =-1? -1: nByteWrite + 1 );
}
The call is the same as the printf () function.
The sum of variable parameters is provided. Unfortunately, the number of variable parameters passed in cannot be determined in C, C ++ (printf () is to scan '%' to check the number of parameters). Therefore, you must either specify the number or set the value at the end of the parameter:
Set the value of the Guard:
Const int GUARDNUMBER = 0; // the ID of the sentry.
// The number of variable parameters cannot be determined. In printf (), the number of '%' is scanned. Here, the Sentinel ID is set to determine the termination of variable parameters.
Int MySum (int I ,...)
{
Int sum = I;
Va_list argptr;
Va_start (argptr, I );
While (I = va_arg (argptr, int ))! = GUARDNUMBER)
Sum + = I;
Va_end (argptr );
Return sum;
}
Call printf ("% d \ n", MySum (1, 3, 5, 7, 9, 0 ));
However, you cannot directly input 0: printf ("% d \ n", MySum (0); // error
Number:
Int MySum (int nCount ,...)
{
If (nCount <= 0)
Return 0;
Int sum = 0;
Va_list argptr;
Va_start (argptr, nCount );
For (int I = 0; I <nCount; I ++)
Sum + = va_arg (argptr, int );
Va_end (argptr );
Return sum;
}
The first parameter indicates the number of subsequent parameters, for example:
Printf ("% d \ n", MySum (5, 1, 3, 5, 7, 9 ));
Printf ("% d \ n", MySum (0 ));
Header file used by the Code:
# Include <stdarg. h>
# Include <stdio. h>
Variable parameters are used more than the preceding methods. However, when using variable parameters in C and C ++, be careful when using printf () when a function is used, the number of parameters passed in must not be less than the number of '%' characters in the preceding formatted string. Otherwise, access may be out of bounds. If you are not lucky, the program will crash.
The prototype of variable parameters involves the parameter entry stack when a function is called. This article will be devoted to further discussions.
Note 1. you do not need to use vfprintf () to parse parameters on the Internet to implement printf (). However, the function is rarely similar to printf () (in fact, you are fully familiar with printf () there are not many people. If you don't believe it, you can first look at "C traps and defects" to understand many of printf () parameters that are not commonly used, go to Microsoft Visual Studio \ VC98 \ CRT \ SRC to view the OUTPUT. C Implementation of printf ).
Note 2. output of a single character putchar (ch) is much more efficient than printf ("% c", ch. When the string is not long, the efficiency of calling putchar () multiple times is higher than that of calling printf ("% s \ n", szStr. It is obvious when many functions are called.