C variable parameters

Source: Internet
Author: User

C variable parameters

Favorites

1. function declaration
First, implement a variable parameter function similar to printf (). The last parameter of the function must be represented by..., as shown in figure
Int log (char * arg1 ,...)
In this way, the compiler can know that this function is a variable parameter function. This parameter has nothing to do with the internal implementation of variable parameter functions. It just allows the compiler to compress all the parameters on the stack without generating any errors when compiling statements that call such functions, of course... there must be at least one common parameter, which is limited by implementation means.
2. function implementation
The C language uses several macro implementations to address variable parameters. The following is the definition of these macros in the kernel source code of linux2.18. I believe that C languages that comply with the c89 and c99 standards are basically defined in this way.

Typedef char * va_list;

/*
Storage alignment properties -- stacks are aligned by machine words
*/
# DEFINE _ aupbnd (sizeof (acpi_native_uint)-1)
# DEFINE _ adnbnd (sizeof (acpi_native_uint)-1)

/*
Variable Argument list macro definitions -- macro used for internal implementation of Variable Parameter Functions
*/
# DEFINE _ BND (x, BND) (sizeof (x) + (BND ))&(~ (BND )))
# Define va_arg (AP, t) (* (T *) (AP) + = (_ BND (T, _ aupbnd)-(_ BND (t, _ adnbnd ))))
# Define va_end (AP) (void) 0
# Define va_start (AP, A) (void) (AP) = (char *) & (A) + (_ BND (A, _ aupbnd ))))

The following uses an x86 32-bit host as an example to analyze the usage of these macros.
To understand these macros, you must have some knowledge about how to pass parameters in C language. Unlike Pascal
Similarly, when passing parameters in C language, the push command is used to press the parameters one by one from the right to the left. Therefore, the C language uses the stack pointer to access parameters. Although the push of X86 can be compressed into 2, 4 or 8 bytes at a time
STACK: when the pressure parameter is pushed to the stack, the size of the machine word is the smallest unit. That is to say, the address of the parameter is aligned with the word, which is _ BND (x, BND) cause. Additionally
Common sense: whether it is assembly or C, compiled x86 functions are generally executed immediately after entering the function body.
Push EBP
MoV EBP, ESP
These two commands. First, add EBP to the stack, and then assign the current stack pointer to EBP. In the future, all the parameters in the access stack use EBP as the base pointer.

Explain the functions of these macros one by one.
_ BND (x, BND), the number of bytes occupied by the parameter of the computing Type X in the stack, of course, the number of bytes after the word alignment. Acpi_native_unit is a machine word. The 32-bit machine is defined as typedef u32 acpi_native_uint;
Obviously, the value of _ aupbnd and _ adnbnd is 4-1 = 3 = 0x00000003. bitwise inversion (~ (BND) is 0 xfffffffc.
Therefore, the _ BND (x, BND) macro is
(Sizeof (x) + 3) & 0 xfffffffc)
Obviously, the function is -- if sizeof (X) is not an integer multiple of 4, add 4 to the remainder.
_ BND (sizeof (char), 3) = 4
_ BND (sizeof (struct size7struct), 3) = 8

Va_start (AP, A), initialize the parameter AP, and assign the address of the first parameter on the Right of function parameter A to the AP. A must be a parameter pointer, so this type of function must have at least one common parameter. The following example function assigns the pointer of the second parameter to the AP.

Va_arg (AP, T) to obtain the AP pointing to the parameter value and point the AP to the next parameter. t is used to specify the current parameter type.
Note that (AP) + = (_ BND (T, _ aupbnd) is enclosed by a pair of parentheses and then subtracted (_ BND (T, _ adnbnd ),
The _ aupbnd and _ adnbnd are equal. Therefore, the obtained value is the parameter value currently pointed to by the AP, but the number of bytes occupied by the current parameter after the word alignment is added to the AP to point to the next parameter.

Va_end (AP) is beautiful.

3. Conclusion
First use... the parameter declaration function is a variable parameter function. Next, initialize the parameter pointer using the va_start (AP, a) macro in the function, and then use va_arg (AP, type) get the parameter values one by one from left to right

The analysis here is clear. The following is an example.

Int log (char * FMT ,...)
{
Va_list AP;
Int D;
Char C, * P, * s;

Va_start (AP, FMT );
While (* FMT)
Switch (* FMT ++ ){
Case's ':/* string */
S = va_arg (AP, char *);
Printf ("string % s/n", S );
Break;
Case 'D':/* int */
D = va_arg (AP, INT );
Printf ("int % d/N", d );
Break;
Case 'C':/* char */
C = va_arg (AP, char );
Printf ("char % C/N", C );
Break;
}
Va_end (AP );
}

//-------------------------------------------------------------
C variable parameters

Overview
There is an uncertain length parameter in C language, such :"... ", Which is mainly used in functions with uncertain number of parameters. The most common example is the printf function.

Prototype:
Int printf (const char * Format [, argument]...);

Example:
Printf ("Enjoy yourself everyday! // N ");
Printf ("the value is % d! // N ", value );

This variable parameter can be said to be a difficult part to understand in the C language. Here there are several problems that will lead to some analysis on it.
Note: In C ++, function overload can be used to differentiate calls of different function parameters, but it still cannot represent any number of function parameters.

Problem: Implementation of printf

How can I implement the printf function and solve the Variable Parameter Problem?

Answer and analysis:
A header file <stdarg. h> is defined in the Standard C language to deal with the variable parameter list. It contains a set of macros and a va_list typedef declaration. A typical implementation is as follows:

Typedef char * va_list;

# Define va_start (list) List = (char *) & va_alist

# Define va_end (list)

# Define va_arg (list, mode )//

(Mode *) (List + = sizeof (mode) [-1]

Implement printf by yourself:

# Include <stdarg. h>

Int printf (char * format ,...)

{

Va_list AP;

Va_start (AP, format );

Int n = vprintf (format, AP );

Va_end (AP );

Return N;

}

Problem: parameters are determined only during running.

Is there a way to write a function. The specific form of this function parameter can be determined at runtime?

Answer and analysis:
Currently, there is no "regular" solution, but there is a single eccentric, because there is a function that has set an example for us in this regard, that is, main (), its prototype is:
Int main (INT argc, char * argv []);
The function parameters are argc and argv.

Think deeply, "You can only determine the parameter format at runtime", that is, you cannot see the accepted parameters from the Declaration, that is, the parameters do not have a fixed form at all. You can set
Define a void
* Type parameters, which are used to point to the actual parameter area, and can be interpreted as needed in the function. This is the meaning of argv in the main function, and argc is used to indicate the actual
Number of parameters, which provides further convenience for our use. Of course, this parameter is not required.

Although the parameters do not have a fixed form, we must parse the meaning of the parameters in the function. Therefore, we naturally have a requirement that the format of the content in the parameter zone should be between the caller and the called, size, effectiveness, and other aspects to reach an agreement, otherwise it will be miserable to talk about each other.

Problem: Transmission of variable length parameters

Sometimes, you need to write a function and pass its variable length parameter directly to another function. Can this requirement be implemented?

Answer and analysis:
At present, you have no way to do this directly, but we can make a detour. First, we define the parameter of the called function as the va_list type, at the same time, the variable length parameter list is converted to va_list in the call function, so that variable length parameters can be passed. See the following:
Void subfunc (char * FMT, va_list argp)
{
...
Arg = va_arg (FMT, argp);/* obtain the desired parameters one by one from argp */
...
}

Void mainfunc (char * FMT ,...)
{
Va_list argp;
Va_start (argp, FMT);/* convert the variable length parameter to va_list */
Subfunc (FMT, argp);/* pass va_list to the subfunction */
Va_end (argp );
...
}

Problem: The variable length parameter type is a function pointer.

I want to use va_arg to extract the variable length parameter with the type of function pointer, but the result is always incorrect. Why?


Answer and analysis:
This is related to the implementation of va_arg. A simple and demo version of va_arg is implemented as follows:
# Define va_arg (argp, type )//
(* (Type *) (argp) + = sizeof (type)-sizeof (type )))

The argp type is char *.

If you want to use va_arg to extract parameters of the function pointer type from the variable parameter list, for example
INT (*) (), then va_arg (argp, INT (*) () is extended:
(* (INT (*) () *) (argp) + = sizeof (INT (*) ()-sizeof (INT (*)())))
Obviously, (INT (*) () *) is meaningless.
To solve this problem, define the function pointer as an independent data type using typedef, for example:
Typedef int (* funcptr )();
At this time, calling va_arg (argp, funcptr) will be extended:
(* (Funcptr *) (argp) + = sizeof (funcptr)-sizeof (funcptr )))
In this way, you can pass the compilation check.

Problem: Get variable length parameters

There is a function with variable length parameters. The following code is used to obtain real parameters of the float type:

Va_arg (argp, float );

Can this be done?

Answer and analysis:
No. In variable length parameters, the "widening" principle is applied. That is, the float type is extended to double; char,
Short is extended to int. Therefore, if you want to go to the variable length parameter list for the original float type parameters, you need to use va_arg (argp,
Double ). Va_arg (argp, INT) is used for char and short types ).

Problem: A restriction for defining variable length parameters

Why does my compiler not allow me to define the following functions, that is, variable length parameters, but there is no fixed parameter?

Int F (...)

{

...

}

Answer and analysis:
No. This is required by ansi c. You must define at least one fixed parameter.
This parameter will be passed to va_start (), and then the va_arg () and va_end () will be used to determine the type and value of the variable length parameter for all actual calls.

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.