Implementation of variable parameters in C Language

Source: Internet
Author: User

Implementation of variable parameters in C Language
1 principle of function calling in C Language

A function is a programming element implemented by most programming languages. The implementation principle of calling a function is: Execution jump + parameter transfer. For Jump execution, all CPUs directly provide jump commands. For parameter transmission, the CPU provides multiple methods. The most common method is to transmit Parameters Using stacks. C language standards Implement function calls, but there is no limit to implementation details. Different C compiler vendors can determine their implementation methods based on the underlying hardware environment.

For the general implementation principle of function calling, refer to the first section in my blog C language that uses setjmp and longjmp for exception handling.

2 variable parameters Implementation ideas 2.1 How to get the real parameters for later

Take the VC ++ compiler on the X86 architecture as an example. The sample code is as follows.

void f(int x, int y, int z){    printf("%p, %p, %p\n", &x, &y, &z);}int main(){    f(100, 200, 300);    return 0;}

Possible execution results:

00FFF674, 00FFF678, 00FFF67C

In VC ++, the function parameters are passed through the stack, and the parameters are pushed to the stack in the order from right to left. Shows the status of the parameter in the stack when calling f:

It can be seen that as long as we know the address of x, we can calculate the address of y and z, so that we can get the value of y and z through its address, without the value of its parameter name. The following code is used.

void f(int x, int y, int z){    char* px = (char*)&x;    char *py = px + sizeof(x);    char *pz = py + sizeof(int);    printf("x=%d, y=%d, z=%d\n", x, *(int*)py, *(int*)pz);}int main(){    f(100, 200, 300);    return 0;}

It can be seen that based on the first parameter of the function and the type of subsequent parameters, the address of subsequent parameters can be calculated based on the offset to obtain the value of subsequent parameters.
So we can rewrite the above code into a variable parameter form.

void f(int x, ...){    char* px = (char*)&x;    char *py = px + sizeof(x);    char *pz = py + sizeof(int);    printf("x=%d, y=%d, z=%d\n", x, *(int*)py, *(int*)pz);}int main(){    f(100, 200, 300);    return 0;}
2.2 how to identify the number and type of subsequent Parameters

Although it is written as a variable parameter, how can the function determine the number and type of the subsequent real parameters? This requires such information to be carried in fixed parameters, such as printf (char *,...) The format string method used. The first parameter carries the number and type of subsequent parameters. We implement a simple vertex that can only recognize three types of signs: % s, % d, and % f.

Void f (char * fmt ,...) {char * p0 = (char *) & fmt; char * ap = p0 + sizeof (fmt); char * p = fmt; while (* p) {if (* p = '%' & * (p + 1) = 'D') {printf ("the parameter type is int, value: % d \ n ", * (int *) ap); ap + = sizeof (int );} else if (* p = '%' & * (p + 1) = 'F') {printf ("the parameter type is double, value: % f \ n ", * (double *) ap); ap + = sizeof (double );} else if (* p = '%' & * (p + 1) = 's') {printf ("the parameter type is char *, value: % s \ n ", * (char **) ap); ap + = sizeof (char *);} p ++ ;}} int main () {f ("% d, % f, % s", 100, 1.23, "hello world"); return 0 ;}

Output:

The parameter type is int, the value is 100, the parameter type is double, the value is 1.230000, the parameter type is char *, and the value is hello world

To simplify the analysis parameter code, define some macros to simplify them, as shown below.

# Define va_list char */* Variable Parameter address */# define va_start (ap, x) ap = (char *) & x + sizeof (x) /* the initialization Pointer Points to the first variable parameter */# define va_arg (ap, t) (ap + = sizeof (t), * (t *) (ap-sizeof (t)/* Get the parameter value and move the pointer to the subsequent parameter */# define va_end (ap) ap = 0/* end parameter Processing */void f (char * fmt ,...) {va_list ap; va_start (ap, fmt); char * p = fmt; while (* p) {if (* p = '%' & * (p + 1) = 'D') {printf ("the parameter type is int and the value is % d \ n", va_arg (ap, int ));} else if (* p = '%' & * (p + 1) = 'F') {printf ("the parameter type is double, value: % f \ n ", va_arg (ap, double);} else if (* p = '%' & * (p + 1) ='s ') {printf ("the parameter type is char * and the value is % s \ n", va_arg (ap, char *);} p ++ ;} va_end (ap);} int main () {f ("% d, % f, % s, % d", 100, 1.23, "hello world", 200 ); return 0 ;}
3. Correct implementation of Variable Parameter Functions

In the above example, variable parameter functions are easily implemented without using any library functions. Don't be too happy. The above code can be compiled and correctly executed in the VC ++ compiler on the X86 platform. However, after gcc compilation, the operation is incorrect. It can be seen that GCC's real parameter passing implementation for variable parameters is not the same as VC ++.

Compile and run under gcc: [[email protected] ~] $./A. out parameter type is int, value is 0 parameter type is double, value is 0.000000 Segmentation fault

As you can see, the above Code cannot be transplanted. To make the variable parameter functions run correctly across platforms and compilers, you must use the macro defined in stdarg. h In the C standard header file instead of what we define. (The names and functions of these macros are exactly the same as those defined by us. This is no coincidence !) The stdarg. H files attached to different C compilers have different definitions for these macros. I would like to reiterate the usage paradigm of these macros again:

Va_list ap; va_start (ap, fixed parameter name);/* initialize */Variable Parameter 1 type x1 = va_arg (ap, variable parameter type 1) based on the last fixed parameter ); /* obtain the first variable parameter value */Variable Parameter 2 type x2 = va_arg (ap, variable parameter type 2) based on the parameter type;/* According to the parameter type, obtain the second variable parameter value */... va_end (ap);/* end */

This time, we will remove our macro definition and replace it with # include

# Include
  
   
# Include
   
    
Void f (char * fmt ,...) {va_list ap; va_start (ap, fmt); char * p = fmt; while (* p) {if (* p = '%' & * (p + 1) = 'D') {printf ("the parameter type is int and the value is % d \ n", va_arg (ap, int ));} else if (* p = '%' & * (p + 1) = 'F') {printf ("the parameter type is double, value: % f \ n ", va_arg (ap, double);} else if (* p = '%' & * (p + 1) ='s ') {printf ("the parameter type is char * and the value is % s \ n", va_arg (ap, char *);} p ++ ;} va_end (ap);} int main () {f ("% d, % f, % s, % d", 100, 1.23, "hello world", 200 ); return 0 ;}
   
  

The code can be correctly executed under VC ++ and GCC.

4. Several issues to be aware of: 4.1 va_end (ap); must not be omitted

In some compiler environments, va_end (ap); does not work, but it may involve memory collection in other compilers and cannot be omitted.

4.2 increase in the default type of variable parameters

C language programming mentioned in:

In the absence of a function prototype, both the char and short types will be converted to the int type, and the float type will be converted to the double type. In fact, variable parameters marked with... always perform this type upgrade.

Reference in "C traps and defects:

** The 2nd parameters of the va_arg macro cannot be specified as char, short, or float **. Because char and short parameters are converted to the int type, float parameters are converted to the double type ...... For example, this write is definitely not correct: c = va_arg (ap, char); because we cannot pass a char type parameter, if it is passed, it is automatically converted to the int type. The formula above should be written as: c = va_arg (ap, int );
4.3 The compiler cannot check the parameter type

For variable parameters, the compiler cannot perform any checks and must ensure correctness by the caller.

4.4 A Variable Parameter Function must provide one or more fixed parameters.

Variable parameters must be determined by fixed parameters. Therefore, at least fixed parameters (f) must be provided in the function ,...).
Of course, you can also provide more fixed parameters, such as f (fixed parameter 1, fixed parameter 2 ,...). Note that when two or more fixed parameters are provided, va_start (ap, x) in a macro, x must be the name of the last fixed parameter (that is, the fixed parameter next to the variable parameter ).

5. Variable Parameter functions of C and heavy-duty functions of C ++

The function overload feature of C ++ allows the same name to be repeatedly used to define a function, as long as the parameters (types or quantities) of functions with the same name are different. For example,

void f(int x);void f(int x, double d);void f(char* s);

Although the function names in the source code are the same, the compiler generates three functions with different function names (name mangling) after processing ). Although there are some similarities in use, this is obviously not a concept with the Variable Parameter Function of C.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.