When I was okay, I thought about printf and I didn't even know what the principle was like. Find an article, but add some understanding to write this article.
Address: http://www.cnblogs.com/liyou_blog/archive/2010/09/01/1814663.html
The correct format of the library function printf in ansi c is as follows:
Int printf (char * FMT ,...);
The ellipsis indicates that the number and type of the parameter table are variable. The ellipsis can only appear at the end of the parameter table. How to implement such a function with a variable parameter table? In the C programme language, an example function is implemented:
Void minprintf (char * FMT ,...);
First, let's take a look at its implementation method, and then study its implementation principle. The minprintf function only processes the format strings and parameters, and the format conversion is implemented by calling the printf function.
Code # include "stdarg. H "# include" stdio. H "Void minprintf (char * FMT ,...) {va_list AP; // each unnamed parameter is directed in turn. // do not be afraid. va_list is a macro. As will be mentioned later, there are also va_start, va_arg, va_end char * P, * sval; int ival; double dval; va_start (AP, FMT); // point the AP to the first unnamed parameter for (P = FMT; * P; P ++) {If (* P! = '%') {Putchar (* P); continue;} switch (* ++ p) {Case 'D': ival = va_arg (AP, INT ); printf ("% d", ival); break; Case 'F': dval = va_arg (AP, double); printf ("% F", dval); break; case's ': For (sval = va_arg (AP, char *); * sval; sval ++) putchar (* sval); break; default: putchar (* P ); break ;}} va_end (AP );}
In the above Code, the standard header file "stdarg. H" contains a set of macro definitions as follows, which define how to traverse the parameter table. The implementation of this header file varies with machines, but the interfaces provided are the same.
Typedef char * va_list;
Void va_start (va_list AP, prev_param);/* ANSI version */
Type va_arg (va_list AP, type );
Void va_end (va_list AP );
Va_list is actually a character pointer type, and a variable is declared in the code. Parameters are referenced in sequence. The Code declares that this type of variable is AP, that is, "parameter Pointer ". Macro va_start initializes the AP as a pointer to the first unnamed parameter. The first parameter is the AP itself, and the second parameter is a variable next to the variable in front of the variable table, that is, "... "The previous parameter; the macro must be called once before the AP is used. The parameter table must contain at least one famous parameter (that is, "char * FMT"). va_start uses the last famous parameter as the start point.
Call va_arg to obtain the parameter. The first parameter is AP, the second parameter is the specified type of the parameter to be obtained, and then the value of the specified type is returned, point the AP location to the next variable location in the variable parameter table;
After obtaining all the parameters, we need to turn off the AP pointer to avoid problems. The method is to call va_end, which sets the input parameter AP to null.
How function parameters are transmitted
Function parameters are accessed in the form of stacks in the data structure, from right to left. The parameters are stored in the stack space of the memory. When the function is executed, it is written into the stack from the last one. Stack bottom high address and stack Top Low address, for example:
Void Foo (int x, float y, char Z );
Then, when calling the function, the real parameter char Z is first stack, then float y, and finally int X, therefore, the storage order of variables in the memory is X-> Y-> Z (from low address to high address). Therefore, if you know the address of any variable, and know the type of other variables (in this way, the pointer shift operation can be correctly implemented), you can find other input variables. The following is a validation sample code. The output is 1, 2, 3, 4,
Code void fun (int ,...) {int I; int * temp = & A; temp ++; for (I = 0; I <A; ++ I) {printf ("% d ,", * temp); temp ++ ;}} int main () {int A = 1; int B = 2; int c = 3; int d = 4; fun (4, a, B, C, D); Return 0 ;}
Finally, a function used in practice is implemented using a variable parameter table:
Int max (INT nnum ,...) {va_list AP; va_start (AP, nnum); // initialize apint maxnum = va_arg (AP, INT); // obtain the largest first data int num; for (INT I = 1; I <nnum; I ++) // {num = va_arg (AP, INT); // obtain the next element if (Num> maxnum) {maxnum = num ;}} va_end (AP); Return maxnum ;}int main () {printf ("% d", max (5, 20, 11, 23, 5, 10 ));}
Program running output:
23
Note:
Due to the macro definition to implement the variable length parameter table, the following problems may occur:
1. Because it is a macro implementation, the parameter type is not checked. It is easy to obtain a value through implicit type conversion (such as inputting a float,
But it is converted to the int type), so there will be some unexpected results.
2. When the parameter pointer is moved, cross-border access is prone, which is beyond the actual range of the function stack frame and may cause the running result of the program to crash.