The well-known C-library function printf is a variable parameter function. How does it implement it? However, its implementation is conditional, and the function parameter's inbound stack order must be the order from right to left, that is, the function parameter. Before the function is called, it must be the rightmost parameter that is first imported into the stack, and the parameters must be passed through the stack. The following example shows the function func (arg1, arg2, arg3 ), the function stack should be:
EBP is a frame pointer register, which is generally used to access the stack. With the stack structure, let's take a look at the specific implementation principles of variable C parameters:
# Include <stdio. h> Enum {ptchar, ptint, ptfloat, ptdouble,}; void printsum (unsigned long paramformat ,...) {/* The 16-bit high is the variable parameter type, and the 16-bit low is the variable parameter count */INT paramtype = (paramformat> 16); int paramnum = paramformat & 0 xFFFF; /* required mformat = EBP + 8, address of the first parameter */unsigned long * parg = required mformat;/* EBP + 0x0c, address of the second parameter */parg ++; switch (paramtype) {Case ptchar: {int sum = 0; For (INT I = 0; I <paramnum; I ++) {char * pvalue = (char *) parg; su M + = * pvalue; parg ++;} printf ("% d \ n", sum);} break; Case ptint: {int sum = 0; for (INT I = 0; I <paramnum; I ++) {int * pvalue = (int *) parg; sum + = * pvalue; parg ++ ;} printf ("% d \ n", sum) ;}break; Case ptfloat: {float sum = 0;/**/parg ++;/* floating point parameter, the stack occupies 8 bytes, so the pointer offset is 8 */For (INT I = 0; I <paramnum; I ++) {float * pvalue = (float *) parg; sum + = * pvalue; parg ++;} printf ("% F \ n", sum) ;} break; Case ptdouble: {double sum = 0 ;/ * Double-precision floating-point parameters, the stack occupies 8 bytes, so the pointer offset is 8 */For (INT I = 0; I <paramnum; I ++) {double * pvalue = (double *) parg; sum + = * pvalue; parg ++;} printf ("% F \ n", sum );} break; default: printf ("unknowned type! \ N "); break ;}} void main () {unsigned long paramformat = 3; char a = 1, B = 2, c = 3; printsum (paramformat,, b, c); paramformat = ptint <16; paramformat + = 3; int IA = 1, IB = 2, Ic = 3; printsum (paramformat, IA, IB, ic); paramformat = ptfloat <16; paramformat + = 3; float fa = 1, Fb = 2, Fc = 3; printsum (paramformat, fa, FB, FC ); paramformat = ptdouble <16; paramformat + = 3; double da = 1, DB = 2, Dc = 3; printsum (paramformat, da, DB, DC );}
the method above imposes restrictions on the order of function parameters in the stack. The method must be right-to-left, which is why Pascal's call method cannot implement printf, in addition, function parameters must be passed through the stack. For Some compilers to optimize the processing, function parameters are passed through registers, which does not meet the requirements. For this reason, this article uses the C ++ default parameter to implement variable parameters. Without these restrictions, the following is the implementation of Code :
# Include <stdio. h> Enum {ptchar, ptint, ptfloat, ptdouble,}; void printsum (unsigned long paramtype, void * arg1 = null, void * arg2 = null, void * arg3 = NULL, void * arg4 = null, void * arg5 = null, void * arg6 = null, void * arg7 = null, void * Arg8 = null, void * arg9 = NULL, void * arg10 = NULL) {void * Arg [10] = {arg1, arg2, arg3, arg4, arg5, arg6, arg7, Arg8, arg9, arg10 ,}; switch (paramtype) {Case ptchar: {int sum = 0; fo R (INT I = 0; I <10; I ++) {If (ARG [I]! = NULL) {char * pvalue = (char *) Arg [I]; sum + = * pvalue;} elsebreak;} printf ("% d \ n", sum );} break; Case ptint: {int sum = 0; For (INT I = 0; I <10; I ++) {If (ARG [I]! = NULL) {int * pvalue = (int *) Arg [I]; sum + = * pvalue;} elsebreak;} printf ("% d \ n", sum );} break; Case ptfloat: {float sum = 0; For (INT I = 0; I <10; I ++) {If (ARG [I]! = NULL) {float * pvalue = (float *) Arg [I]; sum + = * pvalue;} elsebreak;} printf ("% F \ n", sum );} break; Case ptdouble: {double sum = 0; For (INT I = 0; I <10; I ++) {If (ARG [I]! = NULL) {double * pvalue = (double *) Arg [I]; sum + = * pvalue;} elsebreak;} printf ("% F \ n", sum );} break; default: printf ("unknowned type! \ N "); break ;}} void main () {unsigned long paramtype = ptchar; char a = 1, B = 2, c = 3; printsum (paramtype, &, & B, & C); paramtype = ptint; int IA = 1, IB = 2, Ic = 3; printsum (paramtype, & IA, & IB, & ic ); paramtype = ptfloat; float fa = 1, Fb = 2, Fc = 3; printsum (paramtype, & FA, & FB, & FC); paramtype = ptdouble; double da = 1, DB = 2, Dc = 3; printsum (paramtype, & Da, & dB, & DC );}